diff options
410 files changed, 14363 insertions, 5663 deletions
@@ -1,3 +1,1912 @@ +commit 6e9bb9178356620bd47d9f2e31abf42b7f1a8f11 +Merge: e2cefd8 87c8bb3 +Author: Bdale Garbee <bdale@gag.com> +Date: Thu Jul 16 13:31:42 2015 -0600 + + Merge branch 'master' into branch-1.6 + +commit 87c8bb3956897830da1f7aaca2990a9571767b73 +Merge: 643c2fb d6445b3 +Author: Bdale Garbee <bdale@gag.com> +Date: Thu Jul 16 07:54:35 2015 -0600 + + Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos + +commit d6445b3739ac2c5dd040efdb97317a6b2a48044a +Author: Keith Packard <keithp@keithp.com> +Date: Wed Jul 15 18:31:05 2015 -0700 + + Bump Java library versions + + Avoid problems if you have an old version of the library installed + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 643c2fb03833d658320f476ef731bbb06fe3cc31 +Merge: e41786f 271f56a +Author: Bdale Garbee <bdale@gag.com> +Date: Wed Jul 15 16:43:50 2015 -0600 + + Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos + +commit 271f56a41c7e785b0fab7e572325df842d104277 +Author: Keith Packard <keithp@keithp.com> +Date: Wed Jul 15 11:41:03 2015 -0700 + + Bump configure.ac versions to 1.6.1 + + And set android version to 9 + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3cb5b31a534ab0c987667f37c976a5cd589d42a5 +Author: Keith Packard <keithp@keithp.com> +Date: Wed Jul 15 11:40:24 2015 -0700 + + doc: Update for 1.6.1 + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 7338719414ec2c34235c368a55934be0765661c1 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 22:33:07 2015 -0700 + + Bump version to 1.6.0.4 (android version 8) + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 52dc7dc5a791f3e7e307ae11f5c6a20b5bf71ba6 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 22:31:47 2015 -0700 + + altosdroid: Cleanup Makefile.am + + Avoid re-creating library symlinks. + Make builds depend on resource files too. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c4af5cb233013b35d6763f5adf8d11b47f847111 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 22:17:16 2015 -0700 + + altosdroid: Clean up tab layout + + Fuss with weights and gravitys, then add some wrapping layouts to get + things looking reasonable on small and larger screens. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e41786fb384892961a6547e17812a24314ce9623 +Author: Bdale Garbee <bdale@gag.com> +Date: Sat Jul 11 22:59:34 2015 -0600 + + add debian branch spec to vcs-git: line in control so Debian tools work right + +commit 251263f72a1c189aac709d3d0410eb916a9f66d6 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 20:37:16 2015 -0700 + + altosdroid: Add 'Auto' to map tracker list + + Add an 'auto' menu entry when selecting trackers from the map. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2997c9720f58b2955925e4e99c11a6ec302114a9 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 20:36:18 2015 -0700 + + altosdroid: Note time at startup to avoid flipping trackers + + Need to set the initial 'switch' time in onStart to avoid flipping + between trackers before we've done any other 'switching' action. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2a85f273e33a316bd044c4c8dce17c19633cffe6 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 20:21:34 2015 -0700 + + Generate Android version info from configure.ac + + This avoids having version data in two places. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ed682ca39496849b6c0d6bdf81bee6263864895f +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:55:43 2015 -0700 + + altosdroid: Add other igniter status, various other layout changes + + Show the first four igniters (A-D) in the pad tab. Make pad and flight + layouts look a bit better + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 84021b8e0ab9262262345ce47671c3c0c6c43566 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:54:36 2015 -0700 + + altosdroid: Don't disconnect from bluetooth onStartCommand + + If we've already got a bluetooth connection running, don't slam it + shut when the UI starts up again. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit bdc953e26ac2dd67021f905807324c6a02e49690 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:54:07 2015 -0700 + + altosdroid: Remove a debug line in AltosVoice + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 51bf46248ca7ee3c817e62274b7366258c9f87cf +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:53:06 2015 -0700 + + altosdroid: Pop up menu of nearby trackers on map click + + This lets the user select one of potentially many overlapping + trackers, and also makes it clear when the current tracker is being + changed. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b64ab2a8a25b0c22443bc77829c7f35b4f1c2455 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:17:40 2015 -0700 + + altosdroid: Keep speaking even when screen is off + + Move the voice and telemetry disabling calls from onStop to onDestroy + so that a stopped application only leaves off updating the screen, and + not the voice bits as well. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit d015cfc1499a263549f52d46e9e5b934fcb94f53 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:15:08 2015 -0700 + + altoslib: Preload maps based on distance rather than number of tiles + + This lets you get the specific area requested at all zoom levels, + rather than having further detail only at lower resolution zooms. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit afa37e4667ace42c1f43b01b613e639772cfeb75 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:13:37 2015 -0700 + + altoslib: Convert longitude from distance in AltosMapTransform + + This computes the longitude cooresponding to a specific distance at a + specific latitude. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 1b6f3de0a547fa452d5c40775bcf59c49b229e5e +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:11:48 2015 -0700 + + altoslib: Limit simultanous map tile downloads to 128 + + Before this change, every tile requested would get downloaded at the + same time. With moving to distance-based offline map loading radius + values, the number of tiles at closer zooms was in the thousands, + overwhelming the network. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b313a5a3d5aba89330c0e20eeac00cc571828953 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:10:44 2015 -0700 + + altoslib: Make earth size constants public + + This lets other code share the values. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a9bc364ecc69d9085146a39198f0671de164eb2e +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jul 11 19:09:28 2015 -0700 + + altoslib: Make AltosMap floor/ceil static. Check transform in paint + + This avoids crashing in paint when no transform has been set yet. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 002c523fae9369f0261c28f33152289d965d406b +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 18:40:21 2015 -0700 + + telegps: Add receiver battery to Status display + + Makes it a bit cramped, but it's useful to have if you're using TeleBT. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3d508b66c2a15286bb9af88e4d92209463e0725d +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 18:35:07 2015 -0700 + + altosui: Correctly show/hide receiver battery value + + Override the hide() test function which has the listener_state + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f76d5e4fc2ed1e0d79c096cc89793d671ecb78c3 +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 18:33:46 2015 -0700 + + altosuilib: Receiver battery voltage lives in listener_state + + The code to detect whether to show or hide this entry was using + functions that weren't given the listener_state and hence returned + MISSING all of the time. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 7c0f66bf64f410415afaff1b5c8e1443512b7a57 +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 18:31:29 2015 -0700 + + altoslib: Support TeleBT v3.0 battery voltage conversion + + TeleBT v3.0 uses an STM32 instead of a cc1111, so it needs a different + voltage computation from the raw ADC value. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ee1b0bd05bedb8a5a631cc79c77fde8fd920ac38 +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 18:42:29 2015 -0700 + + altos/telebt-v3.0: Report battery voltage correctly + + There was an extra %d, and an extra ':' in the output, making the UI + not parse the voltage correctly + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 6ecd75a7abb5fcee440f7672082013088634680b +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 12:23:04 2015 -0700 + + altoslib: Don't crash if dragging a map view without any tiles + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 06908e377b7b932bfe3f6dfc840a0a13340f32ce +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 25 12:22:04 2015 -0700 + + altosdroid: Class of offline map view widget changed + + Switch around AltosViewPager to match. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 71d924288d45b09ae655d06df9780ba286e3f3be +Author: Keith Packard <keithp@keithp.com> +Date: Wed Jun 24 12:02:22 2015 -0700 + + altosdroid: Display direction in map view + + Use direction in map view when available, otherwise use bearing + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c7067f14359d25a8275f2b09e7b30c06c0424dbb +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:40:11 2015 -0700 + + altoslib: Fix replay to run in realtime again + + At some point, this got sped up to 10x normal speedx + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3e7588e382c70e467b1f328fcfb6bc38a6b79ac7 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:22:06 2015 -0700 + + Bump version to 1.6.0.3 + + Mark the release of 1.6.0.3 altosdroid + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3ce5e24fefaddaa74eadba4722e904354c871387 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:21:45 2015 -0700 + + altosdroid: Update version numbers in manifest + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit d9f96c45d0a3099e9e5fd3c75cc27f9415fcaf55 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:21:30 2015 -0700 + + altosdroid: Mark tab-dependent voice output as done + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit aed8d3ee2561bbec59b9684fb2042186191302ca +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:04:47 2015 -0700 + + altosdroid: Make sure flight voice output always starts with 'speed' + + This resets the flight-mode voice output state back to start again + each time the flight tab is selected + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b1b41e0823a60769e7d2d806f4d97ae043d7dae3 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 22:00:33 2015 -0700 + + altosdroid: Make sure whole flight state is spoken even when no-one is moving + + This eliminates the case where much of the flight state wasn't + reported if the tracker or receiver weren't moving. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f275e73f42e0aaf1760da99fb93c394320aafb84 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 21:47:06 2015 -0700 + + altoslib: Typo slipped into AltosState.java + + Oops. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit bfa6cd8934b993bd4a67cfc7a4eeecf9b11915ef +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 21:39:09 2015 -0700 + + altosdroid: Change voice output around + + This makes the voice output depend on the current displayed tab; where + the 'recovery' and 'map' tabs get the same value. + + Pad + + Reports igniter and GPS status changes + + Flight + + Report flight state changes and max height after apogee + Report current speed, height and bearing/elevation/range once + every 10 seconds while the rocket is in motion. + + Recovery + + Report distance and bearing when the location of either the + tracker or the receiver changes by more than 10m, but not more + than once every 10 seconds. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a761b34ed8fc64435f5a49623f4a5c55e2dda33a +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 21:37:25 2015 -0700 + + altosdroid: Define strings for the tab names + + Use these everywhere instead of replicating the same name; might + reduce errors. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 56146cd379e1319b7adcf8e22cdda55f771e11be +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 21:35:43 2015 -0700 + + altosdroid: Show direction to target in recover tab + + This takes the bearing to target and current direction of motion (from + the Android API) and computes a turn amount and displays that so you + don't have to know which way is north when walking towards the rocket. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 60b8bea12edb954e6140a92c8412364c9581e3c2 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 23 21:38:37 2015 -0700 + + altoslib: Use a longer filter for descent values + + This makes descent speeds almost useful, a huge improvement + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 1513693602c2a4cab0783833d1844c066edabb71 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 23:21:05 2015 -0700 + + altosdroid: Fix line drawing to old tracker location + + Selecting an old tracker would often fail to switch the bearing line + as it was using the map data instead of just using the local data for + the relavant tracker. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 87d2ab135b493486162d33ff172eba1f44dc0ce5 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 21:04:01 2015 -0700 + + altosdroid: Mark four-tab change done + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit fb0bd0ec18088ef01549cdb96243d591f618e32b +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 20:59:17 2015 -0700 + + altosdroid: Allow tracker selection from online map widget + + Need separate tracker selection code for online maps + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2b6768ed32d7be444c49caa40d30b520177bb22a +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 20:43:23 2015 -0700 + + altosdroid: Switch to four tabs (pad/flight/recover/map) + + Ascent and descent were almost the same; no reason to have both. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0f56903774d9e8bb033dfc0af6945e8ddc1d3065 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 20:08:05 2015 -0700 + + altosdroid: Select tracker by clicking on map + + This lets you pick a tracker from the map, rather than having to use + the menu. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a959c1926048d1b96a06aa291131afd7c8e771c7 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 18:41:01 2015 -0700 + + altosdroid: Get rid of on-line only maps tab + + The offline tab did both, so delete the online one and replace it with + the offline one. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 18fe64cf2648568dd0bde5acd7b627f1ddb6917e +Author: Keith Packard <keithp@keithp.com> +Date: Mon Jun 22 18:26:34 2015 -0700 + + altosdroid: Display online/offline maps in same tab + + Make the map portion switchable between online and offline maps, + leaving the rest of the tab alone. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ee656c9d41238ab2c56859a03fe6b8ce8ff2df4e +Author: Keith Packard <keithp@keithp.com> +Date: Sun Jun 21 10:34:00 2015 -0700 + + altosdroid: Add map source preference + + Not hooked up yet. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 9af72a2e629779833ff1787bbfc2ddc8b9d88bba +Author: Keith Packard <keithp@keithp.com> +Date: Sun Jun 21 09:37:05 2015 -0700 + + altosdroid: Show receiver battery voltage in the 'pad' view + + Helpful to determine when the receiver battery is getting low + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4fbe9d5a1f56178a737ede6b31e8d01a02a7543f +Author: Keith Packard <keithp@keithp.com> +Date: Sun Jun 21 09:36:20 2015 -0700 + + altosdroid: Use AltosMap set_zoom_centre + + This keeps the center of the zoom gesture pinned to the screen. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 08e4e291d32bdb3ac3271a85190d277b1874d277 +Author: Keith Packard <keithp@keithp.com> +Date: Sun Jun 21 09:35:28 2015 -0700 + + altosuilib: Use AltosMap set_zoom_centre instead of in-line version + + Shares the same function with altosdroid this way. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e0c318cdd32b3c3fed5099c754aea3ebc6186a0f +Author: Keith Packard <keithp@keithp.com> +Date: Sun Jun 21 09:34:29 2015 -0700 + + altoslib: Add set_zoom_centre to AltosMap + + This zooms around a specific point, keeping it at the same place on + the screen. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b8bdb432aacc1a273ee484a29a24b3768c274db6 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:58:08 2015 -0700 + + altosdroid: Multiple tracker support for google maps + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c72e18fa1713b6e1aa7906210e79dd6354d2390f +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:57:29 2015 -0700 + + altosdroid: Stack map markers with newest rocket on top + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0f40284c554aaadc71a598de8f1c5fe64ea387e1 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:58:45 2015 -0700 + + altosdroid: Support for sorting rockets by age + + Now we can just sort rockets so that the top-most shown is the newest + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit de785b409e404a5296a7ff2037f52f3029536f28 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:57:10 2015 -0700 + + altosdroid: Remove debug + + Just noise at this point. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b2ad3b1ef59fe6e51c8c544f215c33f3b48c3aeb +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:55:44 2015 -0700 + + altosdroid: Switch trackers automatically when changing freq or baud + + This works by switching trackers when we receive telemetry newer than + the last time we changed the frequency configuration. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4a33336b8f468c5b0f2e14c0ee0242c9a24a8b90 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:54:20 2015 -0700 + + altosuilib: Allow for no transform in map mouse wheel function + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3882e358b6f2970cb1afebcf2a71da34a57002df +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 11:53:24 2015 -0700 + + altoslib: Clean up map tile removal + + Remove them while walking the hash table, rather than creating a list + to remove. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0cc03210d5d53d12604688f294b6ca39e3a025de +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 20 09:35:26 2015 -0700 + + altoslib/altosuilib: Fix equals methods, add hashCode + + Whenever we use a class as a HashMap key, that class needs to override + the equals(Object) and hashCode() methods. Otherwise, the hash table + won't work right. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 5568c30f0a4fe346b8ed58934c23653064427d65 +Author: Keith Packard <keithp@aimi.keithp.com> +Date: Thu Jun 18 17:37:35 2015 -0700 + + ao-bringup: Make turnon_easymega run without 'make install' + + Use relative paths for all altos tools + + Signed-off-by: Keith Packard <keithp@aimi.keithp.com> + +commit 6cf27ddd5e84824610d6a0bcbb81ba4626b71409 +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 18 17:12:41 2015 -0700 + + ao-bringup: Use local versions of tools instead of /usr/bin for turnon_easymega + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3e5521070564e9a184f3b781dad9d39cdd963510 +Author: Keith Packard <keithp@keithp.com> +Date: Thu Jun 18 16:56:19 2015 -0700 + + ao-bringup: Fail turnon_easymega if accel cal fails + + This prevents a failing board from being reported as good + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 6683146168216aacdc0842934cec1fb48ea03518 +Author: Keith Packard <keithp@keithp.com> +Date: Wed Jun 17 00:12:06 2015 -0700 + + telegps: use new AltosUIFrame constraint helper + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c71abc5c29025eb57fc78968a4ccf8c34cb3a6f2 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:49:45 2015 -0700 + + micropeak: Update mac/windows FTDI drivers + + Use latest FTDI drivers. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 1098f7502a603a9cf80ad950f53a2c2abdf7ec93 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:43:53 2015 -0700 + + altosuilib: Create grid-bag helper functions in AltosUIFrame + + This eases the burden of creating suitable GridBagConstraints values + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit cdeefaba7d5ef69f28e5dfb152c5f185f8b85f2e +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:42:59 2015 -0700 + + altosuilib: Show state.product if state.device_type isn't set + + MonitorIdle doesn't get the device type, only the product. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 469be0a57dc9932c26f9c38986d22f6e8b2fd6ed +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:42:22 2015 -0700 + + altoslib: Remove debug output from AltosMap + + It's just annoying now. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 76532162d63239b00a51dd0ff6b1356b07b62d2d +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:41:51 2015 -0700 + + altoslib: Expose public function to set state.product + + Just adds a setter function for this value. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 5cacce8099bfc4fa4019538ac88be00bd2023865 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:40:34 2015 -0700 + + altoslib: Let the application disable the link cancel dialog + + This lets the application control whether to pop up the cancel dialog + when the link isn't working. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 75682a5a18f28acf8f4a61a0d45dad461218186e +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 16 23:38:07 2015 -0700 + + altoslib: Set product for idle monitor + + This lets the UI show the product name + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c46c2c5767c6e909fa58587e6c864a4fbaa9fa20 +Merge: 39f4361 6cb7d76 +Author: Robert Garbee <robert@gag.com> +Date: Sat Jun 13 17:40:59 2015 -0600 + + Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos + +commit 39f4361675aa13899864f427a33d4aa48be56cd2 +Author: Robert Garbee <robert@gag.com> +Date: Sat Jun 13 17:40:16 2015 -0600 + + add beep when test completes + +commit 6cb7d76c3fbe32b442713e358654d07b2506897e +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 6 20:37:53 2015 -0700 + + altosdroid: Resource changes needed for multi-tracker mode + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 78df1d5213c402780fa2ce7e062c64cf5a01c45f +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 6 20:37:27 2015 -0700 + + altosdroid: Note recent changes + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c813c2c8f71017a686128e06b5178fc99ece251c +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 6 20:36:18 2015 -0700 + + altosdroid: Add multi-tracker support + + This lets you view multiple trackers in the offline maps tab (online + maps not done yet), saves state of each tracker to preferences. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f79d569dfe333621d63a1d4001c85a88f736ad58 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 6 20:35:32 2015 -0700 + + altoslib: Add preferences for saving/restoring multiple flight states + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 64ca3d2e7d2b23aedfdf98ef8ebd760bd3291534 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Jun 6 06:00:43 2015 -0700 + + ao-tools: Add missing ao-cal-freq man page + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 55753ac8b4b73ec58cb6ef874acc8d606193c5e7 +Author: Keith Packard <keithp@keithp.com> +Date: Fri Jun 5 22:52:51 2015 -0700 + + ao-bringup: Do telegps freq cal before testing GPS + + This lets the GPS get some time to see sats while we're messing with + the radio frequency calibration. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 527700623cc369cc58e15c29dc1ee374fa4efeb7 +Author: Keith Packard <keithp@keithp.com> +Date: Fri Jun 5 22:52:21 2015 -0700 + + ao-bringup: Use new ao-cal-freq program for cal-freq script + + Remove shell script bits that were unreliable. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b9797aa9b6ca38db79c22e4dcefc6efc8a148599 +Author: Keith Packard <keithp@keithp.com> +Date: Fri Jun 5 22:17:02 2015 -0700 + + ao-tools: Create ao-cal-freq + + Create C-based frequency calibration program to replace shell script + which isn't reliable. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 7c75ec6e11a9287b2360bb62ef4ddb4f0e2083c7 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Jun 2 12:48:42 2015 -0700 + + altosdroid: Highlight age in red when older than 10 seconds + + This lets you quickly identify stale data + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a533ea525620f194fd89fedad043659bb433d71b +Author: Keith Packard <keithp@keithp.com> +Date: Sun May 31 23:09:18 2015 -0700 + + altosdroid: Switch from custom title to standard Holo theme + + This gives us the menu button, which is awfully useful on devices + without a hardware version... + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0beb02f1848e34892cca6e34ba83d6ca836d6df2 +Author: Keith Packard <keithp@keithp.com> +Date: Fri May 29 09:49:30 2015 -0700 + + altoslib: Require 'debug' hook in AltosMapInterface + + This lets the map users redirect debug messages as appropriate + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4f1c4dddbce7b4e8673173f1690f79ba60e72ba2 +Author: Keith Packard <keithp@keithp.com> +Date: Fri May 29 09:43:58 2015 -0700 + + altosdroid: use 'show' to set new tab contents in onResume + + Hook onResume so that newly created/recreated tabs get current + contents. The set_visible hook isn't sufficient for that. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 625c496987c2d320a51f3d27f8f00bde17b46a78 +Author: Keith Packard <keithp@keithp.com> +Date: Fri May 29 09:42:58 2015 -0700 + + altosdroid: Missing call to super.onDetach from TabMapOffline + + Causes an exception when you shut down the application. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 50e709a4088f3d6846fd66cbe9b8c437b3f9c88b +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 22:13:39 2015 -0700 + + altosdroid: Split out AltosMapView into separate file + + This lets us use the regular layout configuration bits in the .xml + file instead of needing to patch the map object into the display. That + was causing problems when re-entering the map tab as the map view + would somehow end up with a zero width. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2e424f8dc2886aa475e6ddb21457eba08f768b16 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 22:12:26 2015 -0700 + + altosdroid: Add 'Current Location' as an option when preloading maps + + This lets you load maps around your current location, in case your + favorite launch site isn't in the list. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4315b91d7afc2391e3f7444906ac226500bf1345 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 22:08:51 2015 -0700 + + altosdroid: Save selected map type in AltosDroid object + + The map tabs may not have been created when the map type was selected, + so save the desired type in the main application object. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 41028472fd2e7e0209125e76b94e551f9d10f89c +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 22:06:55 2015 -0700 + + altosdroid: Disable debug output on release builds + + We generate an awful lot of debug spew to the log; presumably that's + not helping performance, so lose that for release builds. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e2d54de24df37baa5ff3837334d97f726934ec25 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 22:05:36 2015 -0700 + + altosdroid: Look for zipalign in the new place too + + This build tool moved for some reason... + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 85013045ca505096064aaf45c312b158d0263d2a +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 01:05:18 2015 -0700 + + altosdroid: Add map types and map preloading UIs + + This adds an ugly dialog to select which maps to preload, and also + adds the ability to display other map types. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ba698c2cc48677735046d0881df9c180674e4082 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 01:01:23 2015 -0700 + + altoslib: Pass all map loader params to set_load_params + + Add zoom and map type to the param list so we don't call set_zoom and + set_maptype separately, which only causes lots of extra image loads to + get started unnecessarily. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c63617415553d97f9be2f19b94365b53d4480c68 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 01:00:47 2015 -0700 + + altosuilib: Eliminate extra MapCache in AltosUIMapPreloadNew + + Use the cache from the map. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ff01fb7275973cdbfd976d3b4e638c6235108121 +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 28 01:00:22 2015 -0700 + + altosuilib: Get rid of AltosUIMapNew.set_load_params + + This isn't needed anywhere. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b49b74847ad55e14d1dbf2872ebbe313147e9fd3 +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 23:14:09 2015 -0700 + + altosuilib: Switch to altoslib map loading code + + Remove the map loading code from the UI bits and use the new altoslib + version instead. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3b4e6da65158a434905dc652e46c69d2c38cea7f +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 23:12:34 2015 -0700 + + altoslib: Add map loading helper class + + This adds the AltosMapLoader class, which iterates over a sequence of + zoom levels and formats to get local copies of a desired launch site. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ccd557c846eed37328d6799f36e61308bcbb678a +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 23:11:58 2015 -0700 + + altoslib: Provide toString method for AltosLatLon + + Makes printing them easier. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 756f501593365b80cfa6f7ca871da3291bbde67a +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 22:43:53 2015 -0700 + + altoslib: Start with map empty + + Don't load images from 0,0 for no good reason; wait until someone sets + a desired lat/lon + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ccba2bb2f193ffd6c3a3d934a46bc06466b4b258 +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 15:31:25 2015 -0700 + + altosuilib: Use altoslib site list loader + + Removes the custom version and uses the shared code + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 519d477cb752d9cdba78a4daa32b1f547bf889b4 +Author: Keith Packard <keithp@keithp.com> +Date: Wed May 27 15:30:29 2015 -0700 + + altoslib: Add AltosLaunchSite bits + + This asynchronously fetches the list of available launch sites from + the standard location and notifies the caller when finished. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 103a50db50be55c2293468d273dd94472dd89d94 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 23:05:49 2015 -0700 + + altosdroid: Place icons on screen instead of drawing path + + This makes drawing a bunch faster, and locating stuff on the screen easier. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2509b664df6a13e6ae9e6753dc9fa0d696a4f6c7 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 23:05:11 2015 -0700 + + altosdroid: Centralize debug printf code + + Create AltosDebug to hold the debug code, use it everywhere. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit bca342577740a9d04b8419ecadcff582e77f1e61 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 23:02:43 2015 -0700 + + altosdroid: Move pause before reopening bluetooth into connec thread + + This avoids stalling the UI while waiting for TBT to boot. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 7975d088a4ac44c0943134fa41d0e3b88f50b98f +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 19:47:04 2015 -0700 + + altosdroid: Add offline map tab + + It's not very fancy yet, but it does zoom and pan, and show the path + of the rocket with a line. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f822b84d8c25159ff113fef6a419b6e18e87a87a +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 01:04:00 2015 -0700 + + altosuilib: Get rid of AltosUIVersion.java + + It's been moved to altoslib/AltosVersion.java + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4895f443e4a748de2677e51869f20c05d265c944 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 00:56:17 2015 -0700 + + altosuilib: Remove old map bits + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f41fe2291891b28327c332098bdc601bc75fc4c0 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 00:46:21 2015 -0700 + + altosuilib: Use new map code for map preload UI + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit cd0f4de98ea709e5f070d5f1337658590d2004a1 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 00:33:02 2015 -0700 + + altosuilib: Add AltosUIImage.java + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 501fa41111b93cc213a1114a33612858e1e93ab5 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 26 00:29:53 2015 -0700 + + altoslib/altosuilib: Get new Map display code running in altosui and telegps + + Looks like the display is all hooked up. Still need to replace the + preload UIs. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit cb23b992be8ba40c97d8988c134a814a13ccd58c +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 20:34:05 2015 -0700 + + altoslib/altosuilib: Update library version to 7 + + So many ABI/API changes + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 68effc6e39f731a2d7bbe2963999c1e785118897 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 20:33:28 2015 -0700 + + altoslib: More frobbing with new map bits + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 00aca369c4070901e0400f291d5f269b5fb8015c +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 20:10:37 2015 -0700 + + altoslib: Get new abstract mapping code compiling + + Not useful yet, but at least it compiles now? + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 6ca2c42061b3c0160bf0137c9cd65989c522b826 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 16:35:23 2015 -0700 + + altoslib: Build AltosVersion.java in configure.ac + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit dcbd87bc685924a6587a5f4dae47d34f417601b0 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 16:26:01 2015 -0700 + + altos/telelco: Show box voltage with pad knob instead of firing button + + Turn left past '1' and see the firing box battery voltage instead of + pushing the firing button with the box disarmed. This seems like a + safer UI. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 91f617d450c187500593d1ae785958187f68ca14 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 15:49:21 2015 -0700 + + altos/telelco: Display telefire battery voltage + + When the firing button is pressed while unarmed, show the telefire + battery voltage in the display. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a54aac3361b7bd18f111e5ba06fb89015504b8a4 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 15:47:46 2015 -0700 + + altos: Add telelco v0.3 (v0.2 with cc1200 instead of cc1120) + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 9b268351aee44de959dcc4c792189c10a00428fe +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 25 13:23:54 2015 -0700 + + telefire: Report telefire battery voltage over telemetry link + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0e76cb2a7d5db24b6cecdccb6fb8d5bf5527fadf +Author: Keith Packard <keithp@keithp.com> +Date: Sun May 24 17:28:07 2015 -0700 + + altos: Only set CMAC RSSI value on valid packet received + + This ignores spurious packets for the purpose of showing the RSSI + value in telelco/telefire, avoiding warning about 'low RSSI' when the + radio receives noise. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e7c25e3ba04b1e9f8e6fa31e2d464fe96a074dad +Author: Keith Packard <keithp@keithp.com> +Date: Sun May 24 17:25:25 2015 -0700 + + telefire: Make 'good' RSSI value configurable + + Different radios will have different 'good' RSSI values, so let each + product configure it, leaving the default set to -90dBm, which is + a solid signal value for the CC1111. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 29edc37a8de56cb6eb028e3bf3f56aa70f109eba +Author: Keith Packard <keithp@keithp.com> +Date: Thu May 21 13:49:28 2015 -0700 + + altoslib: Create display-independent map support code + + This takes the swing-specific map code and creates a generic version. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 59a28811cb19d315b483df296145a2769c445f80 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:25:05 2015 -0700 + + Flip version to 1.6.0.2 + + Tag a version for development builds + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f3ddaae82215e365726f2a62a3dc46bfb29eb1b5 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:23:09 2015 -0700 + + micropeak: Use fast load mode by default in -load script + + Speed up flashing in default config + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit eac71f2b871357ff69581c713059a3741a82a932 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:22:10 2015 -0700 + + microsplash: Add 'publish' target to Makefile + + This dumps the resulting binary and -load script into the Binaries + directory of the altusmetrumllc repo + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit dba00b3d9102db99592f5822a703e64d98ace8bb +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:17:01 2015 -0700 + + altos: Support 32MHz xtal on cc1200 + + I ended up building some cc1200-based boards with 32MHz xtals, so just + make this an option when building the driver. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit fcb523cd083503591fa1277648e5deb258bbbaf4 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:16:10 2015 -0700 + + doc: Clarify what 'after motor' means + + Note that this means after motor burn-out, not after motor start. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b67e6ae8ce34ef119da96b442776bb3d78b4f874 +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:14:43 2015 -0700 + + ao-dump-up: Add --wait option to make testing µPusb easier + + The --wait option hangs around until a suitable device appears, so + that you can test a pile of µPusb devices without needing to + constantly interact with the command line. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2b57158737f85c7009658b3e923c66794f01bbdf +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:12:52 2015 -0700 + + altosui: Remove un-implemented --fetchmaps argument + + This has been stubbed out for a while, so just remove it. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3e5e9333420ede74d998556c1bbd5888e8ff75ae +Author: Keith Packard <keithp@keithp.com> +Date: Tue May 19 10:09:22 2015 -0700 + + altoslib: Expose locale and non-locale floating point parsing functions + + UI bits use locale-specific floating point formats, so parsing those + needs to use the locale. Network-based data, like .kml bits need to + use non-locale-specific parsing code, so now we've got both APIs + available, and each used as appropriate. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3fbf0a29a1b8a67b90ef965ee3e2e972c0ec33a1 +Author: Keith Packard <keithp@keithp.com> +Date: Mon May 18 10:52:24 2015 -0700 + + altoslib: Use Locale.ROOT for KML export + + This avoids locale-specific number formatting, which breaks + googleearth when importing the resulting file. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 1cc1900e13d79e0451587439c23bbb062d86cee3 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Apr 27 22:29:45 2015 -0700 + + altosdroid: Fix tab label height problems + + With a newer android API versions, we can set the indicator to a View + instead of just a string. This lets us wrap the desired string in a + TextView and show just that for the indicator, making it exactly the + right size. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 7bfa8841b65707d629b425b306ec4cc3acfc156c +Author: Keith Packard <keithp@keithp.com> +Date: Mon Apr 27 21:20:22 2015 -0700 + + altosdroid: Add USB support for TeleDongle/TeleBT + + This lets AltosDroid use a USB-connected receiver as well as Bluetooth devices. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 356617a3476e237311b8bbcefd6beda8271b120d +Author: Keith Packard <keithp@keithp.com> +Date: Sun Mar 29 12:10:47 2015 -0700 + + windows: Use new windows stub to launch applications + + This avoids needing to locate javaw on the system while also making + the registry entries less fragile. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit fe76229618643f0af7eae965e7a8fc6c70410d27 +Author: Keith Packard <keithp@keithp.com> +Date: Sun Mar 29 12:08:42 2015 -0700 + + icon: Convert windows stub into launcher program + + Instead of an empty windows stub that exists only to hold icons, add + useful code that allows it to find and run the related java + application. This also adds more resources to that application to + provide more information to Windows too. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b1b69c8b73cbffb56c688f6a968d144b642cdff2 +Author: Keith Packard <keithp@keithp.com> +Date: Fri Mar 20 15:09:20 2015 -0700 + + altos/stmf0: Have fast ADC ring buffer code use wrap-around + + Instead of requiring that the whole set of returned values fit + precisely in the ring, allow for wrap-around so that we can fetch an + odd number of ADC values. The previous version required that the fetch + amount always be a factor of the ADC buffer size. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 43b4044dc71d44cb25be6397b4d66fd792580eed +Author: Keith Packard <keithp@keithp.com> +Date: Thu Mar 19 01:12:24 2015 -0700 + + altos/chaoskey: Set USB VID/PID to 0x1d50/0x60c6 + + These are allocated from the OpenMoko USB vendor ID page and offer a + more 'official' number than from using the 0xfffe space. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c3321bd9f73c89686fe983a8d99f4e54fa91550e +Author: Keith Packard <keithp@keithp.com> +Date: Thu Mar 19 01:11:33 2015 -0700 + + altos: Add the ability to configure a different USB vendor ID + + ChaosKey will use an OpenMoko vid/pid, so we need the ability to + configure a different USB vendor ID for each product. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit fce4e6926de7cb5ef6ea64a8db134c442b86153b +Author: Keith Packard <keithp@keithp.com> +Date: Tue Mar 10 09:35:02 2015 -0600 + + ao-tools/ao-list: Show devices that have no TTY + + chaoskey doesn't advertise itself as a modem, so the kernel doesn't + allocate a tty device. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ce99807ef942de54a3f934d321baf3c3d26442bb +Author: Keith Packard <keithp@keithp.com> +Date: Tue Mar 10 09:34:01 2015 -0600 + + altos/test: Support old telemega eeprom file formats in ao_flight_test + + The old eeprom format used different stoarge for the accel calibration + data, which doesn't matter to this code, but the change in the format + value does. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f92be7e22150b2de4c899e687d3bbfc1eb842f9e +Author: Keith Packard <keithp@keithp.com> +Date: Tue Mar 10 09:32:20 2015 -0600 + + altos/test: Make aprs test code compile again + + Adding the compressed/uncompressed config option broke the APRS test harness. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 46f2a759dc21ebf3a7bf7e0566903fc1e7364719 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Mar 10 09:30:53 2015 -0600 + + ao-tools/ao-mega: Clean up formatting of pyro status messages + + There was an extra newline and missing space. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2c4ebe9b4b392531cd1a5bbafc4ddc38a9391af5 +Author: Keith Packard <keithp@keithp.com> +Date: Tue Mar 10 09:29:52 2015 -0600 + + altosui: Add map to MonitorIdle display + + Nice to be able to verify that maps are working by using this mode, + instead of needing to use flight monitoring. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit dec8de9c642fea1df924a667a4779a6c6c8c3453 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 16:53:58 2015 -0800 + + altos/stmf0: Need ao_exti.h for pin configuration + + Flash loader uses pin configuration to set up GPIOs for boot selection + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4e53a5da5a8921829a3bb290e7c051950a66ab75 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 16:40:30 2015 -0800 + + altos: Add makefile for chaoskey flash loader + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 601f33f5e2f833fed9ab10b24a9df91905d7f766 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 12:38:08 2015 -0800 + + altos: Build chaoskey by default + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f3406f38d71d0c9b55c9a3ae2356a778328509a9 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 12:36:57 2015 -0800 + + altos: Add .gitignore for chaoskey + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4862bec43b29264c361950700f935604f41b840b +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 12:33:36 2015 -0800 + + doc: Update telemetry docs to include new packet formats + + Add TeleMega TeleMetrum v2 and companion data packet formats. + + List which radio parts each product uses. + + Document modulation parameters for new data rates. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit cdd7ad469728fde178c69b9c99d70d6e0ab3f12d +Author: Keith Packard <keithp@keithp.com> +Date: Sat Mar 7 10:18:57 2015 -0800 + + altosdroid: Deal with bluetooth connection failures better + + Remember when we've closed the bluetooth connection so that we stop + operations, including reporting connection status messages or even + starting a connection attempt. + + Pass the AltosBluetooth object back in connection status messages so + that TelemetryService can tell when messages from closed objects get + delivered. There's a queue between the two, so the above fix catches + most of these instances, but not all of them. + + Stick a delay during reconnect -- if the TeleBT device is getting + power-cycled, it will need a few seconds to reconfigure the device at + startup, if AltosDroid manages to connect during that time, the + configuration commands will be ignored. + + Unlock the AltosBluetooth device while we connect so that cancel + calls will actually work. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit d446c90dab0aca7e501a0228f24c210758d84a1d +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 22:23:22 2015 -0800 + + altosui/telegps: Change from variable-units snuck into master + + show_units_name(double) only exists on the variable-units branch... + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit bef7c89dac68956a94ae386fa6b87165ab6cb484 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:16:06 2015 -0800 + + altos: Missing pad field in TMv2 data packet + + The normal ARM padding would have filled this in correctly, but it's + best to be explicit about the structure. + + This also adds a test to make sure the resulting telemetry declaration + is exactly 32 bytes, + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 3b133656df4698ceb7af5902711edf9253a29227 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:11:33 2015 -0800 + + altos: Wait for BT disconnect before sending command + + If AltosDroid manages to connect to the BT module before we've + configured it, we won't be able to talk to it as we can't force the + module to ignore connection attempts. Wait for AltosDroid to give up + and let us configure the device. Eventually, we'll manage, if + AltosDroid's delay interval is longer than the time it takes to + configure the unit. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 112f528755b6c8a2f6eef3bfec21fac981ffb44f +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:08:44 2015 -0800 + + ao-tools: Add ao-flash-stm32f0x + + This new script uses openocd to flash stm32f0x parts + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ff3c27e3b842107680dc48084f71eb8c63f1bcab +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:06:51 2015 -0800 + + altoslib: Round frequency when configuring radio + + This makes sure we set the right frequency, instead of being off by + 1kHz on a regular basis... + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e637367e8b940e1642a07b3b7c99147561de9cf1 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:06:02 2015 -0800 + + altosui/telegps: Add config option for APRS format + + Allow configuration of APRS compressed/uncompressed + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2614d20b324ab215ef22f178e3635d48e757fa9b +Author: Keith Packard <keithp@keithp.com> +Date: Mon Mar 2 21:02:31 2015 -0800 + + altos: Make APRS format (compressed/uncompressed) configurable + + This provides a choice of compressed vs uncompressed when sending APRS + packets to deal with receivers that still do not have support for the + more useful compressed format. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0724cc334a3bd8d81bbd4641d90a7e4040330efe +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 16:06:23 2015 -0800 + + altos/usbtrng: Split out random number generating code to separate driver + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit bd18bc5a42fcecfb710477371b9f62610a1ea640 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 16:04:00 2015 -0800 + + altos: Add chaoskey flash loader + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit a4c436a1c39da971b72d4302623f27af9d56cc38 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 16:02:17 2015 -0800 + + altos: Create chaoskey product + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 476c5b87ea0901f70fe98b581ecca9afaf957607 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 16:00:45 2015 -0800 + + altos/stmf0: Allocate USB buffers at startup + + This lets the extra allocations used from ao_usb_alloc_buffers be + allocated before the first USB connection happens. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e4b415cc8f839ceae48916b5e9d78f78589186cf +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 16:00:17 2015 -0800 + + altos/stmf0: Typo in ao_crc_stm.c + + Extra semicolon + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 27aef593fb4c037fdb65c9fb397829b42d72d0f2 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 15:59:30 2015 -0800 + + altos/stmf0: Fix fast ADC interface + + This was configuring the hardware wrong, and wasn't keeping the output + ring full. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit ec2d758844202108b446e6b12ec1da8812ceb265 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 28 15:07:16 2015 -0800 + + altos: Allow software to offer other USB interface classes than CDC + + This lets some boards offer non-CDC class USB interfaces so that the + modem driver doesn't pick them up. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 4af4e36cda96d053458eeb040e35886890917385 +Merge: 91b1a80 106b16b +Author: Bdale Garbee <bdale@gag.com> +Date: Sun Feb 22 14:55:40 2015 -0700 + + Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos + +commit 91b1a80650a7dcd7c5bf819618a8cea0fceb37d9 +Author: Bdale Garbee <bdale@gag.com> +Date: Sun Feb 8 09:29:01 2015 -0700 + + swap names so v3.0 is the default TeleDongle version to turn on + +commit 106b16b4d5d024543d7ad8c4b4762151e253f3c4 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 22:22:37 2015 -0800 + + altosdroid: Ignore automatic tab changing while activity is saved + + When the activity state is saved (after onSaveInstanceState()), we + can't update the UI until the activity is restarted or restored; that + means any UI changes we make, like switching tabs, must deal with this + by allowing those changes to be ignored, using commitAllowingStateLoss + instead of commit. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit e6630ac41ca0d8563cf9a0df5d4acba8192e9624 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 21:35:34 2015 -0800 + + altosdroid: Missing file: DeviceAddress.java + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 8f2d82461f3cf5da157b23ea45a2fa60d56b196b +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 21:32:54 2015 -0800 + + altosdroid: Only speak when GUI is running + + Create voice in onStart, stop it in onStop. This way, if some other + application is in use, the voice won't be annoying you. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 877609a60a9f2c61c1efad8285b2a3c22f59be28 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 21:19:09 2015 -0800 + + altosdroid: Explicitly disconnect BT on termination or 'disconnect' + + This adds an explicit message to the telemetry service telling it when + to stop trying to talk to the bluetooth device. Until this message is + received, the service will reconnect to the specified BT device. + + That message is sent when you 'quit' the application, or when you 'disconnect'. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c51d39c7ea1153cd2d0dc02c47824a9f35b22fb9 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 20:57:11 2015 -0800 + + altosdroid: Lots of bluetooth connection changes + + Appears to more reliably abort in-progress connection attempts so you + can switch TBT devices without having the previous device in + operation. + + Shows which device the connection is being attempted for. + + Eliminate the 10-second timer and just disable the service when the GUI + shuts down while no BT connection is running. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit b13a78e4f457f67605fc6dafc7f9733746a4f70c +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 16 20:49:39 2015 -0800 + + ao-bringup: Changes for telebt for keith's hacking + + Make the programming device auto-detect by username. + Load binary from ~/altusmetrumllc + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 2ebb4dff758058ae9512cf36518416eb69b928f0 +Author: Keith Packard <keithp@keithp.com> +Date: Sun Feb 15 08:57:55 2015 -0800 + + altos: Remove some accidental debug printfs from ao_packet.c + + While fixing the cc1200 configuration, I added some debug printfs to + this code. They were accidentally committed with the fix... + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 135abf0e7c5ceb5738a0b5f68fe2be4b7abdae5e +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 14 23:18:38 2015 -0800 + + altos/cc1200: Adjust bit-sync configuration + + The default bit timing adjustment mechanism allows for only a 0.2% + deviation from the programmed bit timing. I found one TeleMini device + which is beyond that tolerance as it was built with an older crystal + with more error. + + Switch to the more expensive synchronization mechanism which allows up + to 2% timing error, but requires a multi-byte preamble (which we + have). This fixes packet mode nicely. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 9c75faf1ec51eb2f9a8dc9402653490143a784d9 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 14 08:35:47 2015 -0800 + + altos: embed ao_alarm and ao_clear_alarm in ao_sleep_for + + sdcc won't embed these itself, and thus consumes too much flash for + telemetrum-v1.0 + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit cc64e0e9d35e01b349680159a5bbd68d059134cd +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 14 01:16:42 2015 -0800 + + ao-bringup/turnon_telemini: Detect which programmer to use by $USER + + Bdale uses TD 100, keithp uses TD 186. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit c4f9d96bdea192486f0e3f2d80b846c39a05c0ab +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 14 01:13:21 2015 -0800 + + altosuilib: Detect pair programming by product name, not USB id + + With TeleDongle, TeleBT and TeleMetrum coming in both pair- and self- + programmable versions, we can't use the USB id to tell them + apart. Instead, fetch the device name and use that instead. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 0e929ee2d0a3d1b1bacd36c2c3723ab860eb40b6 +Author: Keith Packard <keithp@keithp.com> +Date: Sat Feb 14 01:11:30 2015 -0800 + + altosui: Run all igniter status requests from non-GUI thread + + Anything run from the UI thread blocks the UI entirely; the Fire + Igniters startup code to collect the number of pyro channels when + building the UI was doing that from the UI thread. Switch that around + so that the UI doesn't get built until that reply comes back, allowing + the user to see the 'connecting' dialog, and cancel it if required. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit f4c812bef76a2cd95f675cb27ea89059561ceec7 +Author: Keith Packard <keithp@keithp.com> +Date: Fri Feb 13 23:51:10 2015 -0800 + + altos: Replace ao_alarm/ao_clear_alarm with ao_sleep_for + + Having arbitrary alarms firing in the middle of complicated device + logic makes no sense at all. Therefore only correct use of ao_alarm + and ao_clear_alarm was around a specific ao_sleep call, with correct + recovery in case the alarm fires. + + This patch replaces all uses of ao_alarm/ao_sleep/ao_clear_alarm with + ao_sleep_for, a new function which takes the alarm timeout directly. + + A few cases which weren't simply calling ao_sleep have been reworked + to pass the timeout value down to the place where sleep *is* being + called, and having that code deal with the return correctly. + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit 1445725b983134d5a967dee88ef997bf15d4a422 +Author: Tom Marble <tmarble@info9.net> +Date: Wed Feb 11 08:21:27 2015 -0600 + + Added continuous output option to ao-usbtrng + +commit 65837616a6d073da8e3e2bf9da524a48cffb77c2 +Author: Keith Packard <keithp@keithp.com> +Date: Mon Feb 9 07:28:18 2015 -0800 + + altos/stmf0: Add ao_crc_stm.c + + Tom discovered that this was missing + + Signed-off-by: Keith Packard <keithp@keithp.com> + +commit fa813bcb6afc851cf4029b56c19ba46a3ae578f5 +Author: Tom Marble <tmarble@info9.net> +Date: Mon Feb 9 08:35:24 2015 -0600 + + Minor typo in man page + +commit e2cefd8593d269ce603aaf33f4a53a5c2dcb3350 +Author: Bdale Garbee <bdale@gag.com> +Date: Sat Feb 7 22:36:22 2015 -0700 + + update ChangeLog for release + commit 26f61380ce6b4df80fa0b5a8a242cef79d5ae339 Author: Bdale Garbee <bdale@gag.com> Date: Sat Feb 7 22:23:38 2015 -0700 diff --git a/altosdroid/AndroidManifest.xml b/altosdroid/AndroidManifest.xml.in index 19e5a6dc..24035796 100644 --- a/altosdroid/AndroidManifest.xml +++ b/altosdroid/AndroidManifest.xml.in @@ -17,9 +17,9 @@ --> <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"/> + 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"/> @@ -38,23 +38,50 @@ 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:allowBackup="true" + android:theme="@style/CustomTheme"> <activity android:name="org.altusmetrum.AltosDroid.AltosDroid" android:label="@string/app_name" - android:configChanges="orientation|keyboardHidden" > + 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" diff --git a/altosdroid/Makefile.am b/altosdroid/Makefile.am index 361de13c..26e14ee7 100644 --- a/altosdroid/Makefile.am +++ b/altosdroid/Makefile.am @@ -14,11 +14,16 @@ DX=$(SDK)/platform-tools/dx ADB=$(SDK)/platform-tools/adb AAPT=$(SDK)/platform-tools/aapt APKBUILDER=$(SDK)/tools/apkbuilder -ZIPALIGN=$(SDK)/tools/zipalign +ZIPALIGN_A=$(SDK)/tools/zipalign +ZIPALIGN_B=$(SDK)/build-tools/*/zipalign JAVA_SRC_DIR=src/org/altusmetrum/AltosDroid EXT_LIBDIR=libs DRAWABLE_DIR=res/drawable +LAYOUT_DIR=res/layout +MENU_DIR=res/menu +VALUES_DIR=res/values +XML_DIR=res/xml ALTOSLIB_SRCDIR=../altoslib ALTOSLIB_JAR=altoslib_$(ALTOSLIB_VERSION).jar @@ -39,19 +44,25 @@ DRAWABLES=\ $(DRAWABLE_DIR)/greenled.png \ $(DRAWABLE_DIR)/grayled.png -SRC=$(JAVA_SRC) $(DRAWABLES) +LAYOUTS=$(LAYOUT_DIR)/*.xml +MENUS=$(MENU_DIR)/*.xml +VALUES=$(VALUES_DIR)/*.xml +XMLS=$(XML_DIR)/*.xml + +RES=$(LAYOUTS) $(MENUS) $(VALUES) $(XMLS) + +SRC=$(JAVA_SRC) $(DRAWABLES) $(RES) all: $(all_target) .NOTPARALLEL: -$(EXT_LIBDIR): +$(ALTOSLIB): $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) mkdir -p $(EXT_LIBDIR) - -$(ALTOSLIB): $(EXT_LIBDIR) $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) cd $(EXT_LIBDIR) && ln -sf $(shell echo $(EXT_LIBDIR) | sed 's|[^/]\+|..|g')/$(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) . -$(SUPPORT_V4): $(EXT_LIBDIR) $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR) +$(SUPPORT_V4): $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR) + mkdir -p $(EXT_LIBDIR) cd $(EXT_LIBDIR) && ln -sf $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR) . $(GOOGLE_PLAY_SERVICES_LIB): $(GOOGLE_PLAY_SERVICES_LIB_SRCDIR)/$(GOOGLE_PLAY_SERVICES_LIB) @@ -83,9 +94,15 @@ bin/AltosDroid-release.apk: bin/AltosDroid-release-unsigned.apk -storepass:file ~/altusmetrumllc/google-play-passphrase \ -signedjar bin/AltosDroid-release-signed.apk \ bin/AltosDroid-release-unsigned.apk AltosDroid - $(ZIPALIGN) -f 4 \ - bin/AltosDroid-release-signed.apk \ - bin/AltosDroid-release.apk + if [ -f $(ZIPALIGN_A) ]; then \ + $(ZIPALIGN_A) -f 4 \ + bin/AltosDroid-release-signed.apk \ + bin/AltosDroid-release.apk; \ + else \ + $(ZIPALIGN_B) -f 4 \ + bin/AltosDroid-release-signed.apk \ + bin/AltosDroid-release.apk; \ + fi release: bin/AltosDroid-release.apk diff --git a/altosdroid/Notebook b/altosdroid/Notebook index 6a246df7..73b5ed27 100644 --- a/altosdroid/Notebook +++ b/altosdroid/Notebook @@ -15,17 +15,9 @@ Desired AltosDroid feature list *) Monitor-idle mode - *) Frequency scanning + *) Online maps comes up tracking object at 0,0 - *) Select satellite imaging mode - - *) TeleBT battery voltage - - *) Deal with long bluetooth list. Currently, a list longer than - the screen makes it impossible to use entries off the bottom. - - *) Pickle/unpickle state instead of reloading entire history from - file. Current restart time is lengthy. + *) Have names for each serial number, default to callsign Completed features @@ -47,3 +39,70 @@ Completed features Done + *) Select satellite imaging mode + + Done + + *) Deal with long bluetooth list. Currently, a list longer than + the screen makes it impossible to use entries off the bottom. + + Done + + *) Pickle/unpickle state instead of reloading entire history from + file. Current restart time is lengthy. + + Done + + *) Offline maps + + Done + + *) Multi-tracker management + + Done + + *) Provide units for age field, turn red if old + + Done + + *) TeleBT battery voltage + + Done + + *) Evaluate performance issues + + Done. Offline maps were duplicating tabs at every redisplay. + + *) Merge offline/online maps into single tab with mode + + Done. + + *) Auto select tracker after long delay + + Done. + + *) Select tracker by clicking map + + Done. + + *) Convert to four tab design: + + Done. + + *) Make voice responses depend on selected tab + + Done. + + *) Monitor TeleMega igniters + + Done. Visible only in Pad tab + + *) Make it harder to switch trackers in map view. Too easy to touch + the screen and switch on accident. + + Done. A menu pops up with trackers within a small radius of + the touch point, letting you cancel if that wasn't your intent. + + *) Make sure it keeps talking with the screen blanked + + Done. Don't shut down voice when stopping UI. diff --git a/altosdroid/default.properties b/altosdroid/default.properties index 66db0d15..3ac25234 100644 --- a/altosdroid/default.properties +++ b/altosdroid/default.properties @@ -8,4 +8,4 @@ # project structure. # Project target. -target=android-10 +target=android-12 diff --git a/altosdroid/project.properties b/altosdroid/project.properties index 96b9551c..d178f98a 100644 --- a/altosdroid/project.properties +++ b/altosdroid/project.properties @@ -8,5 +8,5 @@ # project structure. # Project target. -target=android-10 +target=android-12 android.library.reference.1=google-play-services_lib/ diff --git a/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png Binary files differnew file mode 100644 index 00000000..bc9160df --- /dev/null +++ b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png diff --git a/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png Binary files differnew file mode 100644 index 00000000..4e427d89 --- /dev/null +++ b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml index 93d65517..bf295e4c 100644 --- a/altosdroid/res/layout/device_list.xml +++ b/altosdroid/res/layout/device_list.xml @@ -32,6 +32,13 @@ 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" @@ -44,13 +51,8 @@ <ListView android:id="@+id/paired_devices" android:layout_width="match_parent" android:layout_height="wrap_content" - android:stackFromBottom="true" android:layout_weight="1" - /> - <ListView android:id="@+id/new_devices" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:stackFromBottom="true" - android:layout_weight="2" + android:fadeScrollbars="false" + android:scrollbars="vertical" /> </LinearLayout> diff --git a/altosdroid/res/layout/map_preload.xml b/altosdroid/res/layout/map_preload.xml new file mode 100644 index 00000000..dc613bf2 --- /dev/null +++ b/altosdroid/res/layout/map_preload.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright © 2015 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <ScrollView android:layout_width="fill_parent" + android:layout_height="wrap_content"> + <LinearLayout android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + <TextView android:id="@+id/preload_site_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_site_label" + /> + <Spinner android:id="@+id/preload_site_list" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:prompt="@string/preload_site_label" + android:spinnerMode="dropdown" + /> + <TextView android:id="@+id/preload_latitude_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_latitude_label" + /> + <EditText android:id="@+id/preload_latitude" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:hint="@string/preload_latitude_label" + android:inputType="number"/> + <TextView android:id="@+id/preload_longitude_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_longitude_label" + /> + <EditText android:id="@+id/preload_longitude" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:hint="@string/preload_longitude_label" + android:inputType="number"/> + <TextView android:id="@+id/preload_types" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_types" + /> + <CheckBox android:id="@+id/preload_hybrid" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_hybrid" + /> + <CheckBox android:id="@+id/preload_satellite" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_satellite" + /> + <CheckBox android:id="@+id/preload_roadmap" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_roadmap" + /> + <CheckBox android:id="@+id/preload_terrain" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_terrain" + /> + <TextView android:id="@+id/preload_min_zoom_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_min_zoom" + /> + <Spinner android:id="@+id/preload_min_zoom" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:prompt="@string/preload_min_zoom" + android:spinnerMode="dropdown" + /> + <TextView android:id="@+id/preload_max_zoom_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_max_zoom" + /> + <Spinner android:id="@+id/preload_max_zoom" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:prompt="@string/preload_max_zoom" + android:spinnerMode="dropdown" + /> + <TextView android:id="@+id/preload_radius_label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_radius" + /> + <Spinner android:id="@+id/preload_radius" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:prompt="@string/preload_radius" + android:spinnerMode="dropdown" + /> + <Button android:id="@+id/preload_load" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/preload_load" + /> + <ProgressBar android:id="@+id/preload_progress" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@android:style/Widget.ProgressBar.Horizontal" + /> + </LinearLayout> + </ScrollView> +</LinearLayout> diff --git a/altosdroid/res/layout/map_type.xml b/altosdroid/res/layout/map_type.xml new file mode 100644 index 00000000..610e6bbf --- /dev/null +++ b/altosdroid/res/layout/map_type.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright © 2015 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <Button android:id="@+id/map_type_hybrid" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_hybrid" + android:onClick="selectType" + /> + <Button android:id="@+id/map_type_satellite" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_satellite" + android:onClick="selectType" + /> + <Button android:id="@+id/map_type_roadmap" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_roadmap" + android:onClick="selectType" + /> + <Button android:id="@+id/map_type_terrain" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/preload_terrain" + android:onClick="selectType" + /> +</LinearLayout> diff --git a/altosdroid/res/layout/tab_ascent.xml b/altosdroid/res/layout/tab_ascent.xml deleted file mode 100644 index b21ec426..00000000 --- a/altosdroid/res/layout/tab_ascent.xml +++ /dev/null @@ -1,299 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright © 2013 Mike Beattie <mike@ethernal.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" > - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/height_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/height_label" /> - - <TextView - android:id="@+id/height_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/height_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/max_height_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_height_label" /> - - <TextView - android:id="@+id/max_height_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_height_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - </LinearLayout> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/speed_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/speed_label" /> - - <TextView - android:id="@+id/speed_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/speed_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/max_speed_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_speed_label" /> - - <TextView - android:id="@+id/max_speed_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_speed_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - </LinearLayout> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/accel_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/accel_label" /> - - <TextView - android:id="@+id/accel_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/accel_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/max_accel_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_accel_label" /> - - <TextView - android:id="@+id/max_accel_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_accel_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - </LinearLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/lat_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/latitude_label" /> - - <TextView - android:id="@+id/lat_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/lat_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/lon_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/longitude_label" /> - - <TextView - android:id="@+id/lon_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/lon_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/apogee_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/apogee_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/apogee_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_redled" - android:contentDescription="@string/apogee_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/apogee_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_greenled" - android:text="@string/apogee_voltage_label" /> - - <TextView - android:id="@+id/apogee_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/apogee_voltage_label" - android:layout_toRightOf="@id/apogee_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/main_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/main_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/main_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_redled" - android:contentDescription="@string/main_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/main_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_greenled" - android:text="@string/main_voltage_label" /> - - <TextView - android:id="@+id/main_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/main_voltage_label" - android:layout_toRightOf="@id/main_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - -</LinearLayout>
\ No newline at end of file diff --git a/altosdroid/res/layout/tab_descent.xml b/altosdroid/res/layout/tab_descent.xml deleted file mode 100644 index 9e1fc820..00000000 --- a/altosdroid/res/layout/tab_descent.xml +++ /dev/null @@ -1,339 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright © 2013 Mike Beattie <mike@ethernal.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" > - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/speed_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/speed_label" /> - - <TextView - android:id="@+id/speed_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/speed_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/height_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/height_label" /> - - <TextView - android:id="@+id/height_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/height_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - </LinearLayout> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/elevation_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/elevation_label" /> - - <TextView - android:id="@+id/elevation_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/elevation_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/range_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/range_label" /> - - <TextView - android:id="@+id/range_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/range_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - </LinearLayout> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/bearing_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/bearing_label" /> - - <TextView - android:id="@+id/bearing_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/bearing_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/compass_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="" /> - - <TextView - android:id="@+id/compass_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/compass_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - </LinearLayout> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:baselineAligned="true" - android:orientation="horizontal" - android:paddingTop="5dp" > - - <RelativeLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - - <TextView - android:id="@+id/distance_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/gnd_distance_label" /> - - <TextView - android:id="@+id/distance_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/distance_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <TextView - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" > - </TextView> - - </LinearLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/lat_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/latitude_label" /> - - <TextView - android:id="@+id/lat_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/lat_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/lon_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/longitude_label" /> - - <TextView - android:id="@+id/lon_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/lon_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/apogee_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/apogee_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/apogee_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_redled" - android:contentDescription="@string/apogee_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/apogee_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_greenled" - android:text="@string/apogee_voltage_label" /> - - <TextView - android:id="@+id/apogee_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/apogee_voltage_label" - android:layout_toRightOf="@id/apogee_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/main_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/main_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/main_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_redled" - android:contentDescription="@string/main_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/main_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_greenled" - android:text="@string/main_voltage_label" /> - - <TextView - android:id="@+id/main_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/main_voltage_label" - android:layout_toRightOf="@id/main_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - -</LinearLayout> diff --git a/altosdroid/res/layout/tab_flight.xml b/altosdroid/res/layout/tab_flight.xml new file mode 100644 index 00000000..85c171b2 --- /dev/null +++ b/altosdroid/res/layout/tab_flight.xml @@ -0,0 +1,402 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright © 2013 Mike Beattie <mike@ethernal.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <LinearLayout + android:layout_weight="0" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:baselineAligned="true" + android:orientation="horizontal" > + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/speed_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/speed_label" /> + + <TextView + android:id="@+id/speed_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/speed_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/height_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/height_label" /> + + <TextView + android:id="@+id/height_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/height_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + </LinearLayout> + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:baselineAligned="true" + android:orientation="horizontal" > + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/max_speed_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/max_speed_label" /> + + <TextView + android:id="@+id/max_speed_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/max_speed_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/max_height_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/max_height_label" /> + + <TextView + android:id="@+id/max_height_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/max_height_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + </LinearLayout> + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:baselineAligned="true" + android:orientation="horizontal" + android:paddingTop="5dp" > + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/elevation_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/elevation_label" /> + + <TextView + android:id="@+id/elevation_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/elevation_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/range_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/range_label" /> + + <TextView + android:id="@+id/range_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/range_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + </LinearLayout> + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:baselineAligned="true" + android:orientation="horizontal" + android:paddingTop="5dp" > + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/bearing_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/bearing_label" /> + + <TextView + android:id="@+id/bearing_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/bearing_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/compass_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" /> + + <TextView + android:id="@+id/compass_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/compass_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + </LinearLayout> + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:baselineAligned="true" + android:orientation="horizontal" + android:paddingTop="5dp" > + + <RelativeLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView + android:id="@+id/distance_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/gnd_distance_label" /> + + <TextView + android:id="@+id/distance_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/distance_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <TextView + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" > + </TextView> + + </LinearLayout> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" > + + <TextView + android:id="@+id/lat_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/latitude_label" /> + + <TextView + android:id="@+id/lat_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/lat_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" > + + <TextView + android:id="@+id/lon_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/longitude_label" /> + + <TextView + android:id="@+id/lon_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/lon_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/apogee_view" + android:visibility="gone" + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" > + + <ImageView + android:id="@+id/apogee_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/apogee_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/apogee_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/apogee_redled" + android:contentDescription="@string/apogee_voltage_label" + android:paddingRight="5dp" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/apogee_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/apogee_greenled" + android:text="@string/apogee_voltage_label" /> + + <TextView + android:id="@+id/apogee_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/main_view" + android:visibility="gone" + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="5dp" > + + <ImageView + android:id="@+id/main_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/main_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/main_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/main_redled" + android:contentDescription="@string/main_voltage_label" + android:paddingRight="5dp" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/main_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/main_greenled" + android:text="@string/main_voltage_label" /> + + <TextView + android:id="@+id/main_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + </LinearLayout> +</LinearLayout> diff --git a/altosdroid/res/layout/tab_landed.xml b/altosdroid/res/layout/tab_landed.xml deleted file mode 100644 index f27baa9e..00000000 --- a/altosdroid/res/layout/tab_landed.xml +++ /dev/null @@ -1,211 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright © 2013 Mike Beattie <mike@ethernal.org> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:orientation="vertical" > - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" > - - <TextView - android:id="@+id/bearing_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/bearing_label" /> - - <TextView - android:id="@+id/bearing_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@+id/bearing_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/distance_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/distance_label" /> - - <TextView - android:id="@+id/distance_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@+id/distance_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/target_lat_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/target_latitude_label" /> - - <TextView - android:id="@+id/target_lat_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/target_lat_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/target_lon_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/target_longitude_label" /> - - <TextView - android:id="@+id/target_lon_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/target_lon_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/receiver_lat_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/receiver_latitude_label" /> - - <TextView - android:id="@+id/receiver_lat_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/receiver_lat_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/receiver_lon_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/receiver_longitude_label" /> - - <TextView - android:id="@+id/receiver_lon_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/receiver_lon_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/max_height_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_height_label" /> - - <TextView - android:id="@+id/max_height_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_height_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/max_speed_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_speed_label" /> - - <TextView - android:id="@+id/max_speed_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_speed_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <TextView - android:id="@+id/max_accel_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/max_accel_label" /> - - <TextView - android:id="@+id/max_accel_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_below="@id/max_accel_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - -</LinearLayout>
\ No newline at end of file diff --git a/altosdroid/res/layout/tab_layout.xml b/altosdroid/res/layout/tab_layout.xml new file mode 100644 index 00000000..2c21c648 --- /dev/null +++ b/altosdroid/res/layout/tab_layout.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/customTabLayout" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:id="@+id/tabLabel" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:textSize="13dp" + android:textColor="#ffffff" + android:gravity="center_horizontal" + android:background="#808080" + android:layout_centerVertical="true" + android:layout_centerHorizontal="true" /> +</RelativeLayout> diff --git a/altosdroid/res/layout/tab_map.xml b/altosdroid/res/layout/tab_map.xml index f611ae48..952abd49 100644 --- a/altosdroid/res/layout/tab_map.xml +++ b/altosdroid/res/layout/tab_map.xml @@ -20,169 +20,182 @@ 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> diff --git a/altosdroid/res/layout/tab_pad.xml b/altosdroid/res/layout/tab_pad.xml index 38e61f83..88648c3b 100644 --- a/altosdroid/res/layout/tab_pad.xml +++ b/altosdroid/res/layout/tab_pad.xml @@ -15,306 +15,481 @@ with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - android:orientation="vertical" > - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" > - - <ImageView - android:id="@+id/battery_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/battery_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/battery_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/battery_redled" - android:contentDescription="@string/battery_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/battery_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/battery_greenled" - android:text="@string/battery_voltage_label" /> - - <TextView - android:id="@+id/battery_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/battery_voltage_label" - android:layout_toRightOf="@id/battery_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/apogee_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/apogee_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/apogee_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_redled" - android:contentDescription="@string/apogee_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/apogee_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/apogee_greenled" - android:text="@string/apogee_voltage_label" /> - - <TextView - android:id="@+id/apogee_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/apogee_voltage_label" - android:layout_toRightOf="@id/apogee_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/main_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/main_voltage_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/main_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_redled" - android:contentDescription="@string/main_voltage_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/main_voltage_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/main_greenled" - android:text="@string/main_voltage_label" /> - - <TextView - android:id="@+id/main_voltage_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/main_voltage_label" - android:layout_toRightOf="@id/main_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/logging_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/logging_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/logging_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/logging_redled" - android:contentDescription="@string/logging_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/logging_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/logging_greenled" - android:text="@string/logging_label" /> - - <TextView - android:id="@+id/logging_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/logging_label" - android:layout_toRightOf="@id/logging_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/gps_locked_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/gps_locked_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/gps_locked_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/gps_locked_redled" - android:contentDescription="@string/gps_locked_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/gps_locked_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/gps_locked_greenled" - android:text="@string/gps_locked_label" /> - - <TextView - android:id="@+id/gps_locked_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/gps_locked_label" - android:layout_toRightOf="@id/gps_locked_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingTop="5dp" > - - <ImageView - android:id="@+id/gps_ready_redled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:contentDescription="@string/gps_ready_label" - android:src="@drawable/grayled" /> - - <ImageView - android:id="@+id/gps_ready_greenled" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/gps_ready_redled" - android:contentDescription="@string/gps_ready_label" - android:paddingRight="5dp" - android:src="@drawable/grayled" /> - - <TextView - android:id="@+id/gps_ready_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/gps_ready_greenled" - android:text="@string/gps_ready_label" /> - - <TextView - android:id="@+id/gps_ready_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_below="@id/gps_ready_label" - android:layout_toRightOf="@id/gps_ready_greenled" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="69dp"> - - <TextView - android:id="@+id/pad_lat_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:width="100sp" - android:paddingRight="10sp" - android:layout_toRightOf="@id/gps_ready_greenled" - android:text="@string/pad_lat_label" /> - - <TextView - android:id="@+id/pad_lat_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/pad_lat_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="69dp"> - - <TextView - android:id="@+id/pad_lon_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:width="100sp" - android:paddingRight="10sp" - android:layout_toRightOf="@id/gps_ready_greenled" - android:text="@string/pad_lon_label" /> - - <TextView - android:id="@+id/pad_lon_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/pad_lon_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="69dp"> - - <TextView - android:id="@+id/pad_alt_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:width="100sp" - android:paddingRight="10sp" - android:layout_toRightOf="@id/gps_ready_greenled" - android:text="@string/pad_alt_label" /> - - <TextView - android:id="@+id/pad_alt_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toRightOf="@id/pad_alt_label" - android:text="" - android:textAppearance="?android:attr/textAppearanceSmall" /> - </RelativeLayout> - -</LinearLayout>
\ No newline at end of file + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TableLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:stretchColumns="2,3" + android:layout_weight="0" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TableRow + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/battery_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/battery_voltage_label" + android:src="@drawable/grayled" + /> + + <ImageView + android:id="@+id/battery_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/battery_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/battery_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/battery_voltage_label" /> + + <TextView + android:id="@+id/battery_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" + /> + </TableRow> + + <TableRow + android:id="@+id/receiver_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/receiver_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/receiver_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/receiver_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/receiver_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/receiver_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/receiver_voltage_label" /> + + <TextView + android:id="@+id/receiver_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/logging_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/logging_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/logging_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/logging_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/logging_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/logging_label" /> + + <TextView + android:id="@+id/logging_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/logging_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/gps_locked_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/gps_locked_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/gps_locked_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/gps_locked_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/gps_locked_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/gps_locked_label" /> + + <TextView + android:id="@+id/gps_locked_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/gps_locked_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/gps_ready_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/gps_ready_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/gps_ready_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/gps_ready_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/gps_ready_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/gps_ready_label" /> + + <TextView + android:id="@+id/gps_ready_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/gps_ready_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/apogee_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/apogee_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/apogee_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/apogee_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/apogee_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/apogee_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/apogee_voltage_label" /> + + <TextView + android:id="@+id/apogee_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/main_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/main_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/main_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/main_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/main_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/main_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/main_voltage_label" /> + + <TextView + android:id="@+id/main_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/ignite_a_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/ignite_a_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_a_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/ignite_a_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_a_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/ignite_a_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/ignite_a_voltage_label" /> + + <TextView + android:id="@+id/ignite_a_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/ignite_b_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/ignite_b_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_b_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/ignite_b_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_b_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/ignite_b_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/ignite_b_voltage_label" /> + + <TextView + android:id="@+id/ignite_b_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/ignite_c_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/ignite_c_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_c_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/ignite_c_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_c_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/ignite_c_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/ignite_c_voltage_label" /> + + <TextView + android:id="@+id/ignite_c_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:id="@+id/ignite_d_row" + android:visibility="gone" + android:layout_gravity="center" + android:layout_weight="1" + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <ImageView + android:id="@+id/ignite_d_redled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_d_voltage_label" + android:src="@drawable/grayled" /> + + <ImageView + android:id="@+id/ignite_d_greenled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:contentDescription="@string/ignite_d_voltage_label" + android:src="@drawable/grayled" /> + + <TextView + android:id="@+id/ignite_d_voltage_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/ignite_d_voltage_label" /> + + <TextView + android:id="@+id/ignite_d_voltage_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/receiver_lat_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="2" + android:text="@string/receiver_latitude_label" /> + + <TextView + android:id="@+id/receiver_lat_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/receiver_lon_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="2" + android:text="@string/receiver_longitude_label" /> + + <TextView + android:id="@+id/receiver_lon_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + + <TableRow + android:padding="2dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/receiver_alt_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_column="2" + android:text="@string/receiver_altitude_label" /> + + <TextView + android:id="@+id/receiver_alt_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </TableRow> + </TableLayout> +</LinearLayout> diff --git a/altosdroid/res/layout/tab_recover.xml b/altosdroid/res/layout/tab_recover.xml new file mode 100644 index 00000000..201f45ed --- /dev/null +++ b/altosdroid/res/layout/tab_recover.xml @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright © 2013 Mike Beattie <mike@ethernal.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_weight="0" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <TextView + android:id="@+id/bearing_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/bearing_label" /> + + <TextView + android:id="@+id/bearing_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@+id/bearing_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <TextView + android:id="@+id/direction_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/direction_label" /> + + <TextView + android:id="@+id/direction_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@+id/direction_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/distance_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/distance_label" /> + + <TextView + android:id="@+id/distance_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@+id/distance_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/target_lat_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/target_latitude_label" /> + + <TextView + android:id="@+id/target_lat_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/target_lat_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/target_lon_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/target_longitude_label" /> + + <TextView + android:id="@+id/target_lon_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/target_lon_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/receiver_lat_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/receiver_latitude_label" /> + + <TextView + android:id="@+id/receiver_lat_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/receiver_lat_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/receiver_lon_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/receiver_longitude_label" /> + + <TextView + android:id="@+id/receiver_lon_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/receiver_lon_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/max_height_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/max_height_label" /> + + <TextView + android:id="@+id/max_height_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/max_height_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/max_speed_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/max_speed_label" /> + + <TextView + android:id="@+id/max_speed_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/max_speed_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + + <RelativeLayout + android:layout_gravity="fill" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/max_accel_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/max_accel_label" /> + + <TextView + android:id="@+id/max_accel_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_below="@id/max_accel_label" + android:text="" + android:textAppearance="?android:attr/textAppearanceSmall" /> + </RelativeLayout> + </LinearLayout> +</LinearLayout> diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml index 3bd5a54e..7e08c803 100644 --- a/altosdroid/res/menu/option_menu.xml +++ b/altosdroid/res/menu/option_menu.xml @@ -1,25 +1,27 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project +<!-- + Copyright © 2015 Keith Packard <keithp@keithp.com> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. - http://www.apache.org/licenses/LICENSE-2.0 + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. --> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/connect_scan" android:icon="@android:drawable/ic_menu_search" android:title="@string/connect_device" /> - <item android:id="@+id/quit" - android:icon="@android:drawable/ic_menu_close_clear_cancel" - android:title="@string/quit" /> + <item android:id="@+id/disconnect" + android:icon="@android:drawable/ic_notification_clear_all" + android:title="@string/disconnect_device" /> <item android:id="@+id/select_freq" android:icon="@android:drawable/ic_menu_preferences" android:title="@string/select_freq" /> @@ -29,4 +31,22 @@ <item android:id="@+id/change_units" android:icon="@android:drawable/ic_menu_view" android:title="@string/change_units" /> + <item android:id="@+id/preload_maps" + android:icon="@android:drawable/ic_menu_mapmode" + android:title="@string/preload_maps" /> + <item android:id="@+id/map_type" + android:icon="@android:drawable/ic_menu_mapmode" + android:title="@string/map_type" /> + <item android:id="@+id/map_source" + android:icon="@android:drawable/ic_menu_mapmode" + android:title="@string/map_source" /> + <item android:id="@+id/select_tracker" + android:icon="@android:drawable/ic_menu_view" + android:title="@string/select_tracker"/> + <item android:id="@+id/delete_track" + android:icon="@android:drawable/ic_notification_clear_all" + android:title="@string/delete_track"/> + <item android:id="@+id/quit" + android:icon="@android:drawable/ic_menu_close_clear_cancel" + android:title="@string/quit" /> </menu> diff --git a/altosdroid/res/values/Colors.xml b/altosdroid/res/values/Colors.xml new file mode 100644 index 00000000..055c6df2 --- /dev/null +++ b/altosdroid/res/values/Colors.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright © 2015 Keith Packard <keithp@keithp.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +--> +<resources> + <color name="old_color">#ffff4040</color> +</resources> diff --git a/altosdroid/res/values/CustomTheme.xml b/altosdroid/res/values/CustomTheme.xml new file mode 100644 index 00000000..6c701ea2 --- /dev/null +++ b/altosdroid/res/values/CustomTheme.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <style name="CustomTheme" parent="android:Theme.Holo"> + </style> +</resources> diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml index 0cc99349..e7014fc9 100644 --- a/altosdroid/res/values/strings.xml +++ b/altosdroid/res/values/strings.xml @@ -27,10 +27,19 @@ <!-- Options Menu --> <string name="connect_device">Connect a device</string> + <string name="disconnect_device">Disconnect device</string> <string name="quit">Quit</string> <string name="select_freq">Select radio frequency</string> <string name="select_rate">Select data rate</string> <string name="change_units">Change units</string> + <string name="preload_maps">Load Maps</string> + <string name="select_tracker">Select Tracker</string> + <string name="delete_track">Delete Track</string> + <string name="map_type">Map Type</string> + <string name="map_source">Toggle Online/Offline maps</string> + + <!-- MapTypeActivity --> + <string name="map_type">Map Type</string> <!-- DeviceListActivity --> <string name="scanning">scanning for devices…</string> @@ -60,6 +69,7 @@ <string name="speed_label">Speed</string> <string name="accel_label">Acceleration</string> <string name="bearing_label">Bearing</string> + <string name="direction_label">Direction</string> <string name="elevation_label">Elevation</string> <string name="range_label">Range</string> <string name="distance_label">Distance</string> @@ -67,10 +77,15 @@ <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> @@ -79,8 +94,21 @@ <string name="target_longitude_label">Tar Lon</string> <string name="receiver_latitude_label">My Lat</string> <string name="receiver_longitude_label">My Lon</string> - <string name="pad_lat_label">Pad Lat</string> - <string name="pad_lon_label">Pad Lon</string> - <string name="pad_alt_label">Pad Alt</string> + <string name="receiver_altitude_label">My Alt</string> + + <!-- Map preload --> + <string name="preload_site_label">Known Launch Sites</string> + <string name="preload_latitude_label">Latitude</string> + <string name="preload_longitude_label">Longitude</string> + <string name="preload_types">Map Types</string> + <string name="preload_hybrid">Hybrid</string> + <string name="preload_satellite">Satellite</string> + <string name="preload_roadmap">Roadmap</string> + <string name="preload_terrain">Terrain</string> + <string name="preload_min_zoom">Minimum Zoom</string> + <string name="preload_max_zoom">Maximum Zoom</string> + <string name="preload_radius">Radius</string> + + <string name="preload_load">Load Map</string> </resources> diff --git a/altosdroid/res/xml/device_filter.xml b/altosdroid/res/xml/device_filter.xml new file mode 100644 index 00000000..84b09c06 --- /dev/null +++ b/altosdroid/res/xml/device_filter.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <usb-device vendor-id="65534" /> + <usb-device vendor-id="1027" /> +</resources> diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java index 3740f55d..976e64bb 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java @@ -29,236 +29,191 @@ import android.bluetooth.BluetoothSocket; //import android.os.Bundle; import android.os.Handler; //import android.os.Message; -import android.util.Log; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; -public class AltosBluetooth extends AltosLink { - - // Debugging - private static final String TAG = "AltosBluetooth"; - private static final boolean D = true; +public class AltosBluetooth extends AltosDroidLink { private ConnectThread connect_thread = null; - private Thread input_thread = null; - - private Handler handler; - private BluetoothAdapter adapter; - private BluetoothDevice device; + private BluetoothDevice device; private BluetoothSocket socket; private InputStream input; private OutputStream output; + private boolean pause; // Constructor - public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) { -// set_debug(D); - adapter = BluetoothAdapter.getDefaultAdapter(); - device = in_device; - handler = in_handler; + public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) { + super(handler); + this.device = device; + this.handler = handler; + this.pause = pause; - connect_thread = new ConnectThread(device); + connect_thread = new ConnectThread(); connect_thread.start(); - } - private class ConnectThread extends Thread { - private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); - - public ConnectThread(BluetoothDevice device) { - BluetoothSocket tmp_socket = null; - - try { - tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID); - } catch (IOException e) { - e.printStackTrace(); - } - socket = tmp_socket; + void connected() { + if (closed()) { + AltosDebug.debug("connected after closed"); + return; } - public void run() { - if (D) Log.d(TAG, "ConnectThread: BEGIN"); - setName("ConnectThread"); - - // Always cancel discovery because it will slow down a connection - adapter.cancelDiscovery(); - - synchronized (AltosBluetooth.this) { - // Make a connection to the BluetoothSocket - try { - // This is a blocking call and will only return on a - // successful connection or an exception - socket.connect(); - + AltosDebug.check_ui("connected\n"); + try { + synchronized(this) { + if (socket != null) { input = socket.getInputStream(); output = socket.getOutputStream(); - } catch (IOException e) { - // Close the socket - try { - socket.close(); - } catch (IOException e2) { - if (D) Log.e(TAG, "ConnectThread: Failed to close() socket after failed connection"); - } - input = null; - output = null; - AltosBluetooth.this.notifyAll(); - handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED).sendToTarget(); - if (D) Log.e(TAG, "ConnectThread: Failed to establish connection"); - return; + super.connected(); } + } + } catch (InterruptedException ie) { + connect_failed(); + } catch (IOException io) { + connect_failed(); + } + } - input_thread = new Thread(AltosBluetooth.this); - input_thread.start(); - - // Configure the newly connected device for telemetry - print("~\nE 0\n"); - set_monitor(false); - - // Let TelemetryService know we're connected - handler.obtainMessage(TelemetryService.MSG_CONNECTED).sendToTarget(); + private void connect_failed() { + if (closed()) { + AltosDebug.debug("connect_failed after closed"); + return; + } - // Notify other waiting threads, now that we're connected - AltosBluetooth.this.notifyAll(); + close_device(); + input = null; + output = null; + handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget(); + AltosDebug.error("ConnectThread: Failed to establish connection"); + } - // Reset the ConnectThread because we're done - connect_thread = null; + void close_device() { + BluetoothSocket tmp_socket; - if (D) Log.d(TAG, "ConnectThread: Connect completed"); - } + synchronized(this) { + tmp_socket = socket; + socket = null; } - public void cancel() { + if (tmp_socket != null) { try { - if (socket != null) - socket.close(); + tmp_socket.close(); } catch (IOException e) { - if (D) Log.e(TAG, "ConnectThread: close() of connect socket failed", e); + AltosDebug.error("close_socket failed"); } } } - public double frequency() { - return frequency; - } - - public int telemetry_rate() { - return telemetry_rate; - } - - public void save_frequency() { - AltosPreferences.set_frequency(0, frequency); + public void close() { + super.close(); + input = null; + output = null; } - public void save_telemetry_rate() { - AltosPreferences.set_telemetry_rate(0, telemetry_rate); - } + private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); - private synchronized void wait_connected() throws InterruptedException, IOException { - if (input == null) { - if (D) Log.d(TAG, "wait_connected..."); - wait(); - if (D) Log.d(TAG, "wait_connected done"); - if (input == null) throw new IOException(); - } - } + private void create_socket(BluetoothDevice device) { - private void connection_lost() { - if (D) Log.e(TAG, "Connection lost during I/O"); - handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget(); - } + BluetoothSocket tmp_socket = null; - public void print(String data) { - byte[] bytes = data.getBytes(); - if (D) Log.d(TAG, "print(): begin"); + AltosDebug.check_ui("create_socket\n"); try { - wait_connected(); - output.write(bytes); - if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'"); + tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID); } catch (IOException e) { - connection_lost(); - } catch (InterruptedException e) { - connection_lost(); + e.printStackTrace(); } - } - - public void putchar(byte c) { - byte[] bytes = { c }; - if (D) Log.d(TAG, "print(): begin"); - try { - wait_connected(); - output.write(bytes); - if (D) Log.d(TAG, "print(): Wrote byte: '" + c + "'"); - } catch (IOException e) { - connection_lost(); - } catch (InterruptedException e) { - connection_lost(); + if (socket != null) { + AltosDebug.debug("Socket already allocated %s", socket.toString()); + close_device(); + } + synchronized (this) { + socket = tmp_socket; } } - private static final int buffer_size = 1024; + private class ConnectThread extends Thread { - private byte[] buffer = new byte[buffer_size]; - private int buffer_len = 0; - private int buffer_off = 0; + public void run() { + AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause); + setName("ConnectThread"); - public int getchar() { - while (buffer_off == buffer_len) { + if (pause) { + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + } + } + + create_socket(device); + // Always cancel discovery because it will slow down a connection try { - wait_connected(); - buffer_len = input.read(buffer); - buffer_off = 0; - } catch (IOException e) { - connection_lost(); - return AltosLink.ERROR; - } catch (java.lang.InterruptedException e) { - connection_lost(); - return AltosLink.ERROR; + BluetoothAdapter.getDefaultAdapter().cancelDiscovery(); + } catch (Exception e) { + AltosDebug.debug("cancelDiscovery exception %s", e.toString()); } - } - return buffer[buffer_off++]; - } - public void close() { - if (D) Log.d(TAG, "close(): begin"); - synchronized(this) { - if (D) Log.d(TAG, "close(): synched"); + BluetoothSocket local_socket = null; - if (connect_thread != null) { - if (D) Log.d(TAG, "close(): stopping connect_thread"); - connect_thread.cancel(); - connect_thread = null; - } - if (D) Log.d(TAG, "close(): Closing socket"); - try { - socket.close(); - } catch (IOException e) { - if (D) Log.e(TAG, "close(): unable to close() socket"); + synchronized (AltosBluetooth.this) { + if (!closed()) + local_socket = socket; } - if (input_thread != null) { - if (D) Log.d(TAG, "close(): stopping input_thread"); + + if (local_socket != null) { try { - if (D) Log.d(TAG, "close(): input_thread.interrupt()....."); - input_thread.interrupt(); - if (D) Log.d(TAG, "close(): input_thread.join()....."); - input_thread.join(); - } catch (Exception e) {} - input_thread = null; + // Make a connection to the BluetoothSocket + // This is a blocking call and will only return on a + // successful connection or an exception + local_socket.connect(); + } catch (Exception e) { + AltosDebug.debug("Connect exception %s", e.toString()); + try { + local_socket.close(); + } catch (Exception ce) { + AltosDebug.debug("Close exception %s", ce.toString()); + } + local_socket = null; + } + } + + if (local_socket != null) { + connected(); + } else { + connect_failed(); } - input = null; - output = null; - notifyAll(); + + AltosDebug.debug("ConnectThread: completed"); } } + private synchronized void wait_connected() throws InterruptedException, IOException { + AltosDebug.check_ui("wait_connected\n"); + if (input == null && socket != null) { + AltosDebug.debug("wait_connected..."); + wait(); + AltosDebug.debug("wait_connected done"); + } + if (socket == null) + throw new IOException(); + } - // We override this method so that we can add some debugging. Not 100% elegant, but more useful - // than debugging one char at a time above in getchar()! - public void add_reply(AltosLine line) throws InterruptedException { - if (D) Log.d(TAG, String.format("Got REPLY: %s", line.line)); - super.add_reply(line); + int write(byte[] buffer, int len) { + try { + output.write(buffer, 0, len); + } catch (IOException ie) { + return -1; + } + return len; } - //public void flush_output() { super.flush_output(); } + int read(byte[] buffer, int len) { + try { + return input.read(buffer, 0, len); + } catch (IOException ie) { + return -1; + } + } // Stubs of required methods when extending AltosLink public boolean can_cancel_reply() { return false; } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java new file mode 100644 index 00000000..db63d810 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java @@ -0,0 +1,77 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +package org.altusmetrum.AltosDroid; + +import java.util.Arrays; +import java.io.*; +import java.lang.*; + +import org.altusmetrum.altoslib_8.*; + +import android.app.Activity; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.*; +import android.widget.*; +import android.location.Location; +import android.content.*; +import android.util.Log; +import android.os.*; +import android.content.pm.*; + +public class AltosDebug { + // Debugging + static final String TAG = "AltosDroid"; + + static boolean D = true; + + static void init(Context context) { + ApplicationInfo app_info = context.getApplicationInfo(); + + if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + Log.d(TAG, "Enable debugging\n"); + D = true; + } else { + Log.d(TAG, "Disable debugging\n"); + D = false; + } + } + + + static void info(String format, Object ... arguments) { + Log.i(TAG, String.format(format, arguments)); + } + + static void debug(String format, Object ... arguments) { + if (D) + Log.d(TAG, String.format(format, arguments)); + } + + static void error(String format, Object ... arguments) { + Log.e(TAG, String.format(format, arguments)); + } + + static void check_ui(String format, Object ... arguments) { + if (Looper.myLooper() == Looper.getMainLooper()) { + Log.e(TAG, String.format("ON UI THREAD " + format, arguments)); + for (StackTraceElement el : Thread.currentThread().getStackTrace()) + Log.e(TAG, "\t" + el.toString() + "\n"); + } + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java index 53963f25..3a07212a 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java @@ -18,11 +18,12 @@ package org.altusmetrum.AltosDroid; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Timer; -import java.util.TimerTask; +import java.text.*; +import java.util.*; +import java.io.*; import android.app.Activity; +import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Intent; @@ -36,28 +37,26 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; +import android.content.res.Resources; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.Window; -import android.view.View; -import android.widget.TabHost; -import android.widget.TextView; -import android.widget.RelativeLayout; -import android.widget.Toast; +import android.view.*; +import android.widget.*; import android.app.AlertDialog; import android.location.Location; +import android.hardware.usb.*; +import android.graphics.*; +import android.graphics.drawable.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosDroid extends FragmentActivity implements AltosUnitsListener { - // Debugging - static final String TAG = "AltosDroid"; - static final boolean D = true; + + // Actions sent to the telemetry server at startup time + + public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH"; + public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB"; // Message types received by our Handler @@ -67,14 +66,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { // Intent request codes public static final int REQUEST_CONNECT_DEVICE = 1; public static final int REQUEST_ENABLE_BT = 2; + public static final int REQUEST_PRELOAD_MAPS = 3; + public static final int REQUEST_MAP_TYPE = 4; + + public int map_type = AltosMap.maptype_hybrid; public static FragmentManager fm; private BluetoothAdapter mBluetoothAdapter = null; - // Layout Views - private TextView mTitle; - // Flight state values private TextView mCallsignView; private TextView mRSSIView; @@ -83,6 +83,14 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { private RelativeLayout mStateLayout; private TextView mStateView; private TextView mAgeView; + private boolean mAgeViewOld; + private int mAgeNewColor; + private int mAgeOldColor; + + public static final String tab_pad_name = "pad"; + public static final String tab_flight_name = "flight"; + public static final String tab_recover_name = "recover"; + public static final String tab_map_name = "map"; // field to display the version at the bottom of the screen private TextView mVersion; @@ -100,6 +108,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { // Timer and Saved flight state for Age calculation private Timer timer; AltosState saved_state; + TelemetryState telemetry_state; + Integer[] serials; + + UsbDevice pending_usb_device; + boolean start_with_usb; // Service private boolean mIsBound = false; @@ -120,17 +133,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { switch (msg.what) { case MSG_STATE: - if(D) Log.d(TAG, "MSG_STATE"); - TelemetryState telemetry_state = (TelemetryState) msg.obj; - if (telemetry_state == null) { - Log.d(TAG, "telemetry_state null!"); + if (msg.obj == null) { + AltosDebug.debug("telemetry_state null!"); return; } - - ad.update_state(telemetry_state); + ad.update_state((TelemetryState) msg.obj); break; case MSG_UPDATE_AGE: - if(D) Log.d(TAG, "MSG_UPDATE_AGE"); ad.update_age(); break; } @@ -148,6 +157,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } catch (RemoteException e) { // In this case the service has crashed before we could even do anything with it } + if (pending_usb_device != null) { + try { + mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device)); + pending_usb_device = null; + } catch (RemoteException e) { + } + } } public void onServiceDisconnected(ComponentName className) { @@ -201,17 +217,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400) str = str.concat(String.format(" %d bps", AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate])); - mTitle.setText(str); + setTitle(str); } else { - mTitle.setText(R.string.title_connected_to); + setTitle(R.string.title_connected_to); } break; case TelemetryState.CONNECT_CONNECTING: - mTitle.setText(R.string.title_connecting); + if (telemetry_state.address != null) + setTitle(String.format("Connecting to %s...", telemetry_state.address.name)); + else + setTitle("Connecting to something..."); break; - case TelemetryState.CONNECT_READY: + case TelemetryState.CONNECT_DISCONNECTED: case TelemetryState.CONNECT_NONE: - mTitle.setText(R.string.title_not_connected); + setTitle(R.string.title_not_connected); break; } } @@ -231,21 +250,73 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + int selected_serial = 0; + int current_serial; + long switch_time; + + void set_switch_time() { + switch_time = System.currentTimeMillis(); + selected_serial = 0; + } + boolean registered_units_listener; - void update_state(TelemetryState telemetry_state) { + void update_state(TelemetryState new_telemetry_state) { + + if (new_telemetry_state != null) + telemetry_state = new_telemetry_state; + + if (selected_serial != 0) + current_serial = selected_serial; + + if (current_serial == 0) + current_serial = telemetry_state.latest_serial; if (!registered_units_listener) { registered_units_listener = true; AltosPreferences.register_units_listener(this); } + serials = telemetry_state.states.keySet().toArray(new Integer[0]); + Arrays.sort(serials); + update_title(telemetry_state); - update_ui(telemetry_state.state, telemetry_state.location); - if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) - start_timer(); - else - stop_timer(); + + AltosState state = null; + boolean aged = true; + + if (telemetry_state.states.containsKey(current_serial)) { + state = telemetry_state.states.get(current_serial); + int age = state_age(state); + if (age < 20) + aged = false; + if (current_serial == selected_serial) + aged = false; + else if (switch_time != 0 && (switch_time - state.received_time) > 0) + aged = true; + } + + if (aged) { + AltosState newest_state = null; + int newest_age = 0; + + for (int serial : telemetry_state.states.keySet()) { + AltosState existing = telemetry_state.states.get(serial); + int existing_age = state_age(existing); + + if (newest_state == null || existing_age < newest_age) { + newest_state = existing; + newest_age = existing_age; + } + } + + if (newest_state != null) + state = newest_state; + } + + update_ui(telemetry_state, state, telemetry_state.location); + + start_timer(); } boolean same_string(String a, String b) { @@ -260,14 +331,55 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } - void update_age() { - if (saved_state != null) - mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000)); + + private int blend_component(int a, int b, double r, int shift, int mask) { + return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift; } + private int blend_color(int a, int b, double r) { + return (blend_component(a, b, r, 0, 0xff) | + blend_component(a, b, r, 8, 0xff) | + blend_component(a, b, r, 16, 0xff) | + blend_component(a, b, r, 24, 0xff)); + } + + int state_age(AltosState state) { + return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000); + } + + void set_screen_on(int age) { + if (age < 60) + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + else + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + void update_age() { + if (saved_state != null) { + int age = state_age(saved_state); + + double age_scale = age / 100.0; + + if (age_scale > 1.0) + age_scale = 1.0; - void update_ui(AltosState state, Location location) { + mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale)); - Log.d(TAG, "update_ui"); + set_screen_on(age); + + String text; + if (age < 60) + text = String.format("%ds", age); + else if (age < 60 * 60) + text = String.format("%dm", age / 60); + else if (age < 60 * 60 * 24) + text = String.format("%dh", age / (60 * 60)); + else + text = String.format("%dd", age / (24 * 60 * 60)); + mAgeView.setText(text); + } + } + + void update_ui(TelemetryState telem_state, AltosState state, Location location) { int prev_state = AltosLib.ao_flight_invalid; @@ -277,7 +389,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { prev_state = saved_state.state; if (state != null) { - Log.d(TAG, String.format("prev state %d new state %d\n", prev_state, state.state)); + set_screen_on(state_age(state)); + if (state.state == AltosLib.ao_flight_stateless) { boolean prev_locked = false; boolean locked = false; @@ -289,27 +402,23 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { if (prev_locked != locked) { String currentTab = mTabHost.getCurrentTabTag(); if (locked) { - if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent"); + if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name); } else { - if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad"); + if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_pad_name); } } } else { if (prev_state != state.state) { String currentTab = mTabHost.getCurrentTabTag(); - Log.d(TAG, "switch state"); switch (state.state) { case AltosLib.ao_flight_boost: - if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent"); - break; - case AltosLib.ao_flight_drogue: - if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent"); + if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name); break; case AltosLib.ao_flight_landed: - if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed"); + if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_recover_name); break; case AltosLib.ao_flight_stateless: - if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent"); + if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name); break; } } @@ -328,22 +437,18 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) { - Log.d(TAG, "update callsign"); mCallsignView.setText(state.callsign); } if (saved_state == null || state.serial != saved_state.serial) { - Log.d(TAG, "update serial"); mSerialView.setText(String.format("%d", state.serial)); } if (saved_state == null || state.flight != saved_state.flight) { - Log.d(TAG, "update flight"); if (state.flight == AltosLib.MISSING) mFlightView.setText(""); else mFlightView.setText(String.format("%d", state.flight)); } if (saved_state == null || state.state != saved_state.state) { - Log.d(TAG, "update state"); if (state.state == AltosLib.ao_flight_stateless) { mStateLayout.setVisibility(View.GONE); } else { @@ -352,16 +457,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } if (saved_state == null || state.rssi != saved_state.rssi) { - Log.d(TAG, "update rssi"); mRSSIView.setText(String.format("%d", state.rssi)); } } for (AltosDroidTab mTab : mTabs) - mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem()); + mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem()); - if (state != null) - mAltosVoice.tell(state, from_receiver); + if (mAltosVoice != null) + mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem()); saved_state = state; } @@ -398,26 +502,32 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { return String.format(format, value); } + private View create_tab_view(String label) { + LayoutInflater inflater = (LayoutInflater) this.getLayoutInflater(); + View tab_view = inflater.inflate(R.layout.tab_layout, null); + TextView text_view = (TextView) tab_view.findViewById (R.id.tabLabel); + text_view.setText(label); + return tab_view; + } + + public void set_map_source(int source) { + for (AltosDroidTab mTab : mTabs) + mTab.set_map_source(source); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if(D) Log.e(TAG, "+++ ON CREATE +++"); + AltosDebug.init(this); + AltosDebug.debug("+++ ON CREATE +++"); - // Get local Bluetooth adapter - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); - - // If the adapter is null, then Bluetooth is not supported - if (mBluetoothAdapter == null) { - Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); - finish(); - } + // Initialise preferences + AltosDroidPreferences.init(this); fm = getSupportFragmentManager(); // Set up the window layout - requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.altosdroid); - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title); // Create the Tabs and ViewPager mTabHost = (TabHost)findViewById(android.R.id.tabhost); @@ -428,37 +538,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager); - mTabsAdapter.addTab(mTabHost.newTabSpec("pad").setIndicator("Pad"), TabPad.class, null); - mTabsAdapter.addTab(mTabHost.newTabSpec("ascent").setIndicator("Ascent"), TabAscent.class, null); - mTabsAdapter.addTab(mTabHost.newTabSpec("descent").setIndicator("Descent"), TabDescent.class, null); - mTabsAdapter.addTab(mTabHost.newTabSpec("landed").setIndicator("Landed"), TabLanded.class, null); - mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null); - - - // Scale the size of the Tab bar for different screen densities - // This probably won't be needed when we start supporting ICS+ tabs. - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - int density = metrics.densityDpi; - - if (density==DisplayMetrics.DENSITY_XHIGH) - tabHeight = 65; - else if (density==DisplayMetrics.DENSITY_HIGH) - tabHeight = 45; - else if (density==DisplayMetrics.DENSITY_MEDIUM) - tabHeight = 35; - else if (density==DisplayMetrics.DENSITY_LOW) - tabHeight = 25; - else - tabHeight = 65; - - for (int i = 0; i < 5; i++) - mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight; - - // Set up the custom title - mTitle = (TextView) findViewById(R.id.title_left_text); - mTitle.setText(R.string.app_name); - mTitle = (TextView) findViewById(R.id.title_right_text); + mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null); + mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null); // Display the Version mVersion = (TextView) findViewById(R.id.version); @@ -473,58 +556,158 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { mStateLayout = (RelativeLayout) findViewById(R.id.state_container); mStateView = (TextView) findViewById(R.id.state_value); mAgeView = (TextView) findViewById(R.id.age_value); + mAgeNewColor = mAgeView.getTextColors().getDefaultColor(); + mAgeOldColor = getResources().getColor(R.color.old_color); + } + + private boolean ensureBluetooth() { + // Get local Bluetooth adapter + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + + // If the adapter is null, then Bluetooth is not supported + if (mBluetoothAdapter == null) { + Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); + return false; + } + + if (!mBluetoothAdapter.isEnabled()) { + Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT); + } + + return true; + } + + private boolean check_usb() { + UsbDevice device = AltosUsb.find_device(this, AltosLib.product_basestation); + + if (device != null) { + Intent i = new Intent(this, AltosDroid.class); + PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0); - mAltosVoice = new AltosVoice(this); + if (AltosUsb.request_permission(this, device, pi)) { + connectUsb(device); + } + start_with_usb = true; + return true; + } + + start_with_usb = false; + + return false; + } + + private void noticeIntent(Intent intent) { + + /* Ok, this is pretty convenient. + * + * When a USB device is plugged in, and our 'hotplug' + * intent registration fires, we get an Intent with + * EXTRA_DEVICE set. + * + * When we start up and see a usb device and request + * permission to access it, that queues a + * PendingIntent, which has the EXTRA_DEVICE added in, + * along with the EXTRA_PERMISSION_GRANTED field as + * well. + * + * So, in both cases, we get the device name using the + * same call. We check to see if access was granted, + * in which case we ignore the device field and do our + * usual startup thing. + */ + + UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); + + AltosDebug.debug("intent %s device %s granted %s", intent, device, granted); + + if (!granted) + device = null; + + if (device != null) { + AltosDebug.debug("intent has usb device " + device.toString()); + connectUsb(device); + } else { + + /* 'granted' is only false if this intent came + * from the request_permission call and + * permission was denied. In which case, we + * don't want to loop forever... + */ + if (granted) { + AltosDebug.debug("check for a USB device at startup"); + if (check_usb()) + return; + } + AltosDebug.debug("Starting by looking for bluetooth devices"); + if (ensureBluetooth()) + return; + finish(); + } } @Override public void onStart() { super.onStart(); - if(D) Log.e(TAG, "++ ON START ++"); + AltosDebug.debug("++ ON START ++"); + + set_switch_time(); + + noticeIntent(getIntent()); // Start Telemetry Service - startService(new Intent(AltosDroid.this, TelemetryService.class)); + String action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH; - if (!mBluetoothAdapter.isEnabled()) { - Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT); - } + startService(new Intent(action, null, AltosDroid.this, TelemetryService.class)); doBindService(); + if (mAltosVoice == null) + mAltosVoice = new AltosVoice(this); + } @Override - public synchronized void onResume() { + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + AltosDebug.debug("onNewIntent"); + noticeIntent(intent); + } + + @Override + public void onResume() { super.onResume(); - if(D) Log.e(TAG, "+ ON RESUME +"); + AltosDebug.debug("+ ON RESUME +"); } @Override - public synchronized void onPause() { + public void onPause() { super.onPause(); - if(D) Log.e(TAG, "- ON PAUSE -"); + AltosDebug.debug("- ON PAUSE -"); } @Override public void onStop() { super.onStop(); - if(D) Log.e(TAG, "-- ON STOP --"); - - doUnbindService(); + AltosDebug.debug("-- ON STOP --"); } @Override public void onDestroy() { super.onDestroy(); - if(D) Log.e(TAG, "--- ON DESTROY ---"); + AltosDebug.debug("--- ON DESTROY ---"); - if (mAltosVoice != null) mAltosVoice.stop(); + doUnbindService(); + if (mAltosVoice != null) { + mAltosVoice.stop(); + mAltosVoice = null; + } stop_timer(); } - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if(D) Log.d(TAG, "onActivityResult " + resultCode); + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + AltosDebug.debug("onActivityResult " + resultCode); switch (requestCode) { case REQUEST_CONNECT_DEVICE: // When DeviceListActivity returns with a device to connect to @@ -539,28 +722,64 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { //setupChat(); } else { // User did not enable Bluetooth or an error occured - Log.e(TAG, "BT not enabled"); + AltosDebug.error("BT not enabled"); stopService(new Intent(AltosDroid.this, TelemetryService.class)); Toast.makeText(this, R.string.bt_not_enabled, Toast.LENGTH_SHORT).show(); finish(); } break; + case REQUEST_MAP_TYPE: + if (resultCode == Activity.RESULT_OK) + set_map_type(data); + break; } } - private void connectDevice(String address) { + private void connectUsb(UsbDevice device) { + if (mService == null) + pending_usb_device = device; + else { + // Attempt to connect to the device + try { + mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device)); + AltosDebug.debug("Sent OPEN_USB message"); + } catch (RemoteException e) { + AltosDebug.debug("connect device message failed"); + } + } + } + + private void connectDevice(Intent data) { // Attempt to connect to the device try { - if (D) Log.d(TAG, "Connecting to " + address); - mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address)); + String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); + String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME); + + AltosDebug.debug("Connecting to " + address + " " + name); + DeviceAddress a = new DeviceAddress(address, name); + mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a)); + AltosDebug.debug("Sent connecting message"); } catch (RemoteException e) { + AltosDebug.debug("connect device message failed"); } } - private void connectDevice(Intent data) { - // Get the device MAC address - String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS); - connectDevice(address); + private void disconnectDevice() { + try { + mService.send(Message.obtain(null, TelemetryService.MSG_DISCONNECT, null)); + } catch (RemoteException e) { + } + } + + private void set_map_type(Intent data) { + int type = data.getIntExtra(MapTypeActivity.EXTRA_MAP_TYPE, -1); + + AltosDebug.debug("intent set_map_type %d\n", type); + if (type != -1) { + map_type = type; + for (AltosDroidTab mTab : mTabs) + mTab.set_map_type(map_type); + } } @Override @@ -573,20 +792,22 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { void setFrequency(double freq) { try { mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq)); + set_switch_time(); } catch (RemoteException e) { } } void setFrequency(String freq) { try { - setFrequency (Double.parseDouble(freq.substring(11, 17))); - } catch (NumberFormatException e) { + setFrequency (AltosParse.parse_double_net(freq.substring(11, 17))); + } catch (ParseException e) { } } void setBaud(int baud) { try { mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud)); + set_switch_time(); } catch (RemoteException e) { } } @@ -611,18 +832,77 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { } } + void select_tracker(int serial) { + int i; + + AltosDebug.debug("select tracker %d\n", serial); + + if (serial == selected_serial) { + AltosDebug.debug("%d already selected\n", serial); + return; + } + + if (serial != 0) { + for (i = 0; i < serials.length; i++) + if (serials[i] == serial) + break; + + if (i == serials.length) { + AltosDebug.debug("attempt to select unknown tracker %d\n", serial); + return; + } + } + + current_serial = selected_serial = serial; + update_state(null); + } + + void touch_trackers(Integer[] serials) { + AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this); + builder_tracker.setTitle("Select Tracker"); + final String[] trackers = new String[serials.length + 1]; + trackers[0] = "Auto"; + for (int i = 0; i < serials.length; i++) + trackers[i+1] = String.format("%d", serials[i]); + builder_tracker.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + if (item == 0) + select_tracker(0); + else + select_tracker(Integer.parseInt(trackers[item])); + } + }); + AlertDialog alert_tracker = builder_tracker.create(); + alert_tracker.show(); + } + + void delete_track(int serial) { + try { + mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial)); + } catch (Exception ex) { + } + } + @Override public boolean onOptionsItemSelected(MenuItem item) { Intent serverIntent = null; switch (item.getItemId()) { case R.id.connect_scan: - // Launch the DeviceListActivity to see devices and do scan - serverIntent = new Intent(this, DeviceListActivity.class); - startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); + if (ensureBluetooth()) { + // Launch the DeviceListActivity to see devices and do scan + serverIntent = new Intent(this, DeviceListActivity.class); + startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); + } + return true; + case R.id.disconnect: + /* Disconnect the device + */ + disconnectDevice(); return true; case R.id.quit: - Log.d(TAG, "R.id.quit"); - stopService(new Intent(AltosDroid.this, TelemetryService.class)); + AltosDebug.debug("R.id.quit"); + disconnectDevice(); finish(); return true; case R.id.select_freq: @@ -676,8 +956,92 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener { boolean imperial = AltosPreferences.imperial_units(); AltosPreferences.set_imperial_units(!imperial); return true; + case R.id.preload_maps: + serverIntent = new Intent(this, PreloadMapActivity.class); + startActivityForResult(serverIntent, REQUEST_PRELOAD_MAPS); + return true; + case R.id.map_type: + serverIntent = new Intent(this, MapTypeActivity.class); + startActivityForResult(serverIntent, REQUEST_MAP_TYPE); + return true; + case R.id.map_source: + int source = AltosDroidPreferences.map_source(); + int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE; + AltosDroidPreferences.set_map_source(new_source); + set_map_source(new_source); + return true; + case R.id.select_tracker: + if (serials != null) { + String[] trackers = new String[serials.length+1]; + trackers[0] = "Auto"; + for (int i = 0; i < serials.length; i++) + trackers[i+1] = String.format("%d", serials[i]); + AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); + builder_serial.setTitle("Select a tracker"); + builder_serial.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + if (item == 0) + select_tracker(0); + else + select_tracker(serials[item-1]); + } + }); + AlertDialog alert_serial = builder_serial.create(); + alert_serial.show(); + + } + return true; + case R.id.delete_track: + if (serials != null) { + String[] trackers = new String[serials.length]; + for (int i = 0; i < serials.length; i++) + trackers[i] = String.format("%d", serials[i]); + AlertDialog.Builder builder_serial = new AlertDialog.Builder(this); + builder_serial.setTitle("Delete a track"); + builder_serial.setItems(trackers, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + delete_track(serials[item]); + } + }); + AlertDialog alert_serial = builder_serial.create(); + alert_serial.show(); + + } + return true; } return false; } + static String direction(AltosGreatCircle from_receiver, + Location receiver) { + if (from_receiver == null) + return null; + + if (receiver == null) + return null; + + if (!receiver.hasBearing()) + return null; + + float bearing = receiver.getBearing(); + float heading = (float) from_receiver.bearing - bearing; + + while (heading <= -180.0f) + heading += 360.0f; + while (heading > 180.0f) + heading -= 360.0f; + + int iheading = (int) (heading + 0.5f); + + if (-1 < iheading && iheading < 1) + return "ahead"; + else if (iheading < -179 || 179 < iheading) + return "backwards"; + else if (iheading < 0) + return String.format("left %d°", -iheading); + else + return String.format("right %d°", iheading); + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java new file mode 100644 index 00000000..7cbba794 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java @@ -0,0 +1,219 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; + +import android.os.Handler; + +import org.altusmetrum.altoslib_8.*; + +public abstract class AltosDroidLink extends AltosLink { + + Handler handler; + + Thread input_thread = null; + + public double frequency() { + return frequency; + } + + public int telemetry_rate() { + return telemetry_rate; + } + + public void save_frequency() { + AltosPreferences.set_frequency(0, frequency); + } + + public void save_telemetry_rate() { + AltosPreferences.set_telemetry_rate(0, telemetry_rate); + } + + Object closed_lock = new Object(); + boolean closing = false; + boolean closed = false; + + public boolean closed() { + synchronized(closed_lock) { + return closing; + } + } + + void connected() throws InterruptedException { + input_thread = new Thread(this); + input_thread.start(); + + // Configure the newly connected device for telemetry + print("~\nE 0\n"); + set_monitor(false); + AltosDebug.debug("ConnectThread: connected"); + + /* Let TelemetryService know we're connected + */ + handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget(); + + /* Notify other waiting threads that we're connected now + */ + notifyAll(); + } + + public void closing() { + synchronized(closed_lock) { + AltosDebug.debug("Marked closing true"); + closing = true; + } + } + + private boolean actually_closed() { + synchronized(closed_lock) { + return closed; + } + } + + abstract void close_device(); + + public void close() { + AltosDebug.debug("close(): begin"); + + closing(); + + flush_output(); + + synchronized (closed_lock) { + AltosDebug.debug("Marked closed true"); + closed = true; + } + + close_device(); + + synchronized(this) { + + if (input_thread != null) { + AltosDebug.debug("close(): stopping input_thread"); + try { + AltosDebug.debug("close(): input_thread.interrupt()....."); + input_thread.interrupt(); + AltosDebug.debug("close(): input_thread.join()....."); + input_thread.join(); + } catch (Exception e) {} + input_thread = null; + } + notifyAll(); + } + } + + abstract int write(byte[] buffer, int len); + + abstract int read(byte[] buffer, int len); + + private static final int buffer_size = 64; + + private byte[] in_buffer = new byte[buffer_size]; + private byte[] out_buffer = new byte[buffer_size]; + private int buffer_len = 0; + private int buffer_off = 0; + private int out_buffer_off = 0; + + private byte[] debug_chars = new byte[buffer_size]; + private int debug_off; + + private void debug_input(byte b) { + if (b == '\n') { + AltosDebug.debug(" " + new String(debug_chars, 0, debug_off)); + debug_off = 0; + } else { + if (debug_off < buffer_size) + debug_chars[debug_off++] = b; + } + } + + private void disconnected() { + if (closed()) { + AltosDebug.debug("disconnected after closed"); + return; + } + + AltosDebug.debug("Sending disconnected message"); + handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget(); + } + + public int getchar() { + + if (actually_closed()) + return ERROR; + + while (buffer_off == buffer_len) { + buffer_len = read(in_buffer, buffer_size); + if (buffer_len < 0) { + AltosDebug.debug("ERROR returned from getchar()"); + disconnected(); + return ERROR; + } + buffer_off = 0; + } + if (AltosDebug.D) + debug_input(in_buffer[buffer_off]); + return in_buffer[buffer_off++]; + } + + public void flush_output() { + super.flush_output(); + + if (actually_closed()) { + out_buffer_off = 0; + return; + } + + while (out_buffer_off != 0) { + int sent = write(out_buffer, out_buffer_off); + + if (sent <= 0) { + AltosDebug.debug("flush_output() failed"); + out_buffer_off = 0; + break; + } + + if (sent < out_buffer_off) + System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent); + + out_buffer_off -= sent; + } + } + + public void putchar(byte c) { + out_buffer[out_buffer_off++] = c; + if (out_buffer_off == buffer_size) + flush_output(); + } + + public void print(String data) { + byte[] bytes = data.getBytes(); + AltosDebug.debug("print(): begin"); + for (byte b : bytes) + putchar(b); + AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'"); + } + + public AltosDroidLink(Handler handler) { + this.handler = handler; + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java new file mode 100644 index 00000000..5f6ff198 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java @@ -0,0 +1,33 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; +import android.location.Location; +import org.altusmetrum.altoslib_8.*; + +public interface AltosDroidMapInterface { + public void onCreateView(AltosDroid altos_droid); + + public void set_visible(boolean visible); + + public void center(double lat, double lon, double accuracy); + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver); +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java index 7ab70147..a4e27006 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java @@ -17,14 +17,23 @@ package org.altusmetrum.AltosDroid; import android.content.Context; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosDroidPreferences extends AltosPreferences { /* Active device preference name */ - final static String activeDevicePreference = "ACTIVE-DEVICE"; + final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS"; + final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME"; - static String active_device_address; + static DeviceAddress active_device_address; + + /* Map source preference name */ + final static String mapSourcePreference = "MAP-SOURCE"; + + static final int MAP_SOURCE_OFFLINE = 0; + static final int MAP_SOURCE_ONLINE = 1; + + static int map_source; public static void init(Context context) { if (backend != null) @@ -32,20 +41,41 @@ public class AltosDroidPreferences extends AltosPreferences { AltosPreferences.init(new AltosDroidPreferencesBackend(context)); - active_device_address = backend.getString(activeDevicePreference, null); + String address = backend.getString(activeDeviceAddressPreference, null); + String name = backend.getString(activeDeviceNamePreference, null); + + if (address != null && name != null) + active_device_address = new DeviceAddress (address, name); + + map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE); } - public static void set_active_device(String address) { + public static void set_active_device(DeviceAddress address) { synchronized(backend) { active_device_address = address; - backend.putString(activeDevicePreference, active_device_address); + backend.putString(activeDeviceAddressPreference, active_device_address.address); + backend.putString(activeDeviceNamePreference, active_device_address.name); flush_preferences(); } } - public static String active_device() { + public static DeviceAddress active_device() { synchronized(backend) { return active_device_address; } } + + public static void set_map_source(int map_source) { + synchronized(backend) { + AltosDroidPreferences.map_source = map_source; + backend.putInt(mapSourcePreference, map_source); + flush_preferences(); + } + } + + public static int map_source() { + synchronized(backend) { + return map_source; + } + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java index bc5300fd..2ff711f5 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java @@ -24,7 +24,7 @@ import android.content.SharedPreferences; import android.os.Environment; import android.util.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { public final static String NAME = "org.altusmetrum.AltosDroid"; @@ -44,7 +44,12 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { public String[] keys() { Map<String, ?> all = prefs.getAll(); - return (String[])all.keySet().toArray(); + Object[] ao = all.keySet().toArray(); + + String[] as = new String[ao.length]; + for (int i = 0; i < ao.length; i++) + as[i] = (String) ao[i]; + return as; } public AltosPreferencesBackend node(String key) { @@ -104,6 +109,7 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend { } public void remove(String key) { + AltosDebug.debug("remove preference %s\n", key); editor.remove(key); } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java index cbb20045..9d612a1e 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import android.location.Location; import android.app.Activity; import android.graphics.Color; @@ -26,21 +26,28 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentManager; import android.location.Location; -import android.util.Log; import android.widget.TextView; public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener { + TelemetryState last_telem_state; AltosState last_state; AltosGreatCircle last_from_receiver; Location last_receiver; + AltosDroid altos_droid; - public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver); + public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver); public abstract String tab_name(); + public void set_map_type(int map_type) { + } + + public void set_map_source(int map_source) { + } + public void units_changed(boolean imperial_units) { - if (!isHidden() && last_state != null) - show(last_state, last_from_receiver, last_receiver); + if (!isHidden()) + show(last_telem_state, last_state, last_from_receiver, last_receiver); } public void set_value(TextView text_view, @@ -55,29 +62,46 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen public void set_visible(boolean visible) { FragmentTransaction ft = AltosDroid.fm.beginTransaction(); + AltosDebug.debug("set visible %b %s\n", visible, tab_name()); if (visible) { - AltosState state = last_state; - AltosGreatCircle from_receiver = last_from_receiver; - Location receiver = last_receiver; - - show(state, from_receiver, receiver); ft.show(this); + show(last_telem_state, last_state, last_from_receiver, last_receiver); } else ft.hide(this); - ft.commit(); + ft.commitAllowingStateLoss(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + altos_droid = (AltosDroid) activity; + altos_droid.registerTab(this); } - public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) { + @Override + public void onDetach() { + super.onDetach(); + altos_droid.unregisterTab(this); + altos_droid = null; + } + + @Override + public void onResume() { + super.onResume(); + AltosDebug.debug("onResume tab %s\n", tab_name()); + set_visible(true); + } + + public void update_ui(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver, boolean is_current) + { + last_telem_state = telem_state; last_state = state; last_from_receiver = from_receiver; last_receiver = receiver; - if (is_current) { - if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name())); - - show(state, from_receiver, receiver); - } else { - if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name())); + if (is_current) + show(telem_state, state, from_receiver, receiver); + else return; - } } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java new file mode 100644 index 00000000..eb059901 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java @@ -0,0 +1,517 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; + +import org.altusmetrum.altoslib_8.*; + +import android.app.Activity; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.view.*; +import android.widget.*; +import android.location.Location; +import android.content.*; +import android.util.*; + +class Rocket implements Comparable { + AltosLatLon position; + String name; + int serial; + long last_packet; + boolean active; + AltosMapOffline map_offline; + + void paint() { + map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y); + map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4); + } + + void set_position(AltosLatLon position, long last_packet) { + this.position = position; + this.last_packet = last_packet; + } + + void set_active(boolean active) { + this.active = active; + } + + public int compareTo(Object o) { + Rocket other = (Rocket) o; + + if (active && !other.active) + return 1; + if (other.active && !active) + return -1; + + long diff = last_packet - other.last_packet; + + if (diff > 0) + return 1; + if (diff < 0) + return -1; + return 0; + } + + Rocket(int serial, AltosMapOffline map_offline) { + this.serial = serial; + this.name = String.format("%d", serial); + this.map_offline = map_offline; + } +} + +public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface { + ScaleGestureDetector scale_detector; + boolean scaling; + AltosMap map; + AltosDroid altos_droid; + + AltosLatLon here; + AltosLatLon there; + AltosLatLon pad; + + Canvas canvas; + Paint paint; + + Bitmap pad_bitmap; + int pad_off_x, pad_off_y; + Bitmap rocket_bitmap; + int rocket_off_x, rocket_off_y; + Bitmap here_bitmap; + int here_off_x, here_off_y; + + static final int WHITE = 0xffffffff; + static final int RED = 0xffff0000; + static final int PINK = 0xffff8080; + static final int YELLOW= 0xffffff00; + static final int CYAN = 0xff00ffff; + static final int BLUE = 0xff0000ff; + static final int BLACK = 0xff000000; + + public static final int stateColors[] = { + WHITE, // startup + WHITE, // idle + WHITE, // pad + RED, // boost + PINK, // fast + YELLOW, // coast + CYAN, // drogue + BLUE, // main + BLACK, // landed + BLACK, // invalid + CYAN, // stateless + }; + + /* AltosMapInterface */ + public void debug(String format, Object ... arguments) { + AltosDebug.debug(format, arguments); + } + + class MapTile extends AltosMapTile { + public void paint(AltosMapTransform t) { + AltosPointInt pt = new AltosPointInt(t.screen(upper_left)); + + if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA)) + return; + + AltosImage altos_image = cache.get(this, store, px_size, px_size); + + MapImage map_image = (MapImage) altos_image; + + Bitmap bitmap = null; + + if (map_image != null) + bitmap = map_image.bitmap; + + if (bitmap != null) { + canvas.drawBitmap(bitmap, pt.x, pt.y, paint); + } else { + paint.setColor(0xff808080); + canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint); + if (t.has_location()) { + String message = null; + switch (status) { + case AltosMapTile.loading: + message = "Loading..."; + break; + case AltosMapTile.bad_request: + message = "Internal error"; + break; + case AltosMapTile.failed: + message = "Network error, check connection"; + break; + case AltosMapTile.forbidden: + message = "Too many requests, try later"; + break; + } + if (message != null) { + Rect bounds = new Rect(); + paint.getTextBounds(message, 0, message.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = pt.x + px_size / 2.0f; + float y = pt.y + px_size / 2.0f; + x = x - width / 2.0f; + y = y + height / 2.0f; + paint.setColor(0xff000000); + canvas.drawText(message, 0, message.length(), x, y, paint); + } + } + } + } + + public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(listener, upper_left, center, zoom, maptype, px_size, 2); + } + + } + + public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new MapTile(listener, upper_left, center, zoom, maptype, px_size); + } + + public AltosMapPath new_path() { + return null; + } + + public AltosMapLine new_line() { + return null; + } + + class MapImage implements AltosImage { + public Bitmap bitmap; + + public void flush() { + if (bitmap != null) { + bitmap.recycle(); + bitmap = null; + } + } + + public MapImage(File file) { + bitmap = BitmapFactory.decodeFile(file.getPath()); + } + } + + public AltosImage load_image(File file) throws Exception { + return new MapImage(file); + } + + class MapMark extends AltosMapMark { + public void paint(AltosMapTransform t) { + } + + MapMark(double lat, double lon, int state) { + super(lat, lon, state); + } + } + + public AltosMapMark new_mark(double lat, double lon, int state) { + return new MapMark(lat, lon, state); + } + + public int width() { + return getWidth(); + } + + public int height() { + return getHeight(); + } + + public void repaint() { + postInvalidate(); + } + + public void repaint(AltosRectangle damage) { + postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height); + } + + public void set_zoom_label(String label) { + } + + public void select_object(AltosLatLon latlon) { + if (map.transform == null) + return; + ArrayList<Integer> near = new ArrayList<Integer>(); + + for (Rocket rocket : sorted_rockets()) { + if (rocket.position == null) { + debug("rocket %d has no position\n", rocket.serial); + continue; + } + double distance = map.transform.hypot(latlon, rocket.position); + debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth()); + if (distance < rocket_bitmap.getWidth() * 2.0) { + debug("selecting %d\n", rocket.serial); + near.add(rocket.serial); + } + } + if (near.size() != 0) + altos_droid.touch_trackers(near.toArray(new Integer[0])); + } + + class Line { + AltosLatLon a, b; + + void paint() { + if (a != null && b != null) { + AltosPointDouble a_screen = map.transform.screen(a); + AltosPointDouble b_screen = map.transform.screen(b); + paint.setColor(0xff8080ff); + canvas.drawLine((float) a_screen.x, (float) a_screen.y, + (float) b_screen.x, (float) b_screen.y, + paint); + } + } + + void set_a(AltosLatLon a) { + this.a = a; + } + + void set_b(AltosLatLon b) { + this.b = b; + } + + Line() { + } + } + + Line line = new Line(); + + int stroke_width = 20; + + void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) { + if (lat_lon != null && map != null && map.transform != null) { + AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = pt.x; + float y = pt.y; + x = x - width / 2.0f - off_x; + y = y + height / 2.0f - off_y; + paint.setColor(0xff000000); + canvas.drawText(text, 0, text.length(), x, y, paint); + } + } + + HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>(); + + void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) { + if (lat_lon != null && map != null && map.transform != null) { + AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon)); + + canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint); + } + } + + private Rocket[] sorted_rockets() { + Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]); + + Arrays.sort(rocket_array); + return rocket_array; + } + + private void draw_positions() { + line.set_a(there); + line.set_b(here); + line.paint(); + draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y); + + for (Rocket rocket : sorted_rockets()) + rocket.paint(); + draw_bitmap(here, here_bitmap, here_off_x, here_off_y); + } + + @Override public void invalidate() { + Rect r = new Rect(); + getDrawingRect(r); + super.invalidate(); + } + + @Override public void invalidate(int l, int t, int r, int b) { + Rect rect = new Rect(); + getDrawingRect(rect); + super.invalidate(); + } + + @Override + protected void onDraw(Canvas view_canvas) { + if (map == null) { + debug("MapView draw without map\n"); + return; + } + canvas = view_canvas; + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStrokeWidth(stroke_width); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setTextSize(40); + map.paint(); + draw_positions(); + canvas = null; + } + + public boolean onScale(ScaleGestureDetector detector) { + float f = detector.getScaleFactor(); + + if (f <= 0.8) { + map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); + return true; + } + if (f >= 1.2) { + map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY())); + return true; + } + return false; + } + + public boolean onScaleBegin(ScaleGestureDetector detector) { + return true; + } + + public void onScaleEnd(ScaleGestureDetector detector) { + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + scale_detector.onTouchEvent(event); + + if (scale_detector.isInProgress()) { + scaling = true; + } + + if (scaling) { + if (event.getAction() == MotionEvent.ACTION_UP) { + scaling = false; + } + return true; + } + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + map.touch_start((int) event.getX(), (int) event.getY(), true); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + map.touch_continue((int) event.getX(), (int) event.getY(), true); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + map.touch_stop((int) event.getX(), (int) event.getY(), true); + } + return true; + } + + double mapAccuracy; + + public void center(double lat, double lon, double accuracy) { + if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) { + if (map != null) + map.maybe_centre(lat, lon); + mapAccuracy = accuracy; + } + } + + public void set_visible(boolean visible) { + if (visible) + setVisibility(VISIBLE); + else + setVisibility(GONE); + } + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { + if (state != null) { + map.show(state, null); + if (state.pad_lat != AltosLib.MISSING && pad == null) + pad = new AltosLatLon(state.pad_lat, state.pad_lon); + } + + if (telem_state != null) { + Integer[] old_serial = rockets.keySet().toArray(new Integer[0]); + Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]); + + /* remove deleted keys */ + for (int serial : old_serial) { + if (!telem_state.states.containsKey(serial)) + rockets.remove(serial); + } + + /* set remaining keys */ + + for (int serial : new_serial) { + Rocket rocket; + AltosState t_state = telem_state.states.get(serial); + if (rockets.containsKey(serial)) + rocket = rockets.get(serial); + else { + rocket = new Rocket(serial, this); + rockets.put(serial, rocket); + } + if (t_state.gps != null) { + AltosLatLon latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon); + rocket.set_position(latlon, t_state.received_time); + if (state.serial == serial) + there = latlon; + } + if (state != null) + rocket.set_active(state.serial == serial); + } + } + if (receiver != null) { + here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + } + } + + public void onCreateView(AltosDroid altos_droid) { + this.altos_droid = altos_droid; + map = new AltosMap(this); + map.set_maptype(altos_droid.map_type); + + pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad); + /* arrow at the bottom of the launchpad image */ + pad_off_x = pad_bitmap.getWidth() / 2; + pad_off_y = pad_bitmap.getHeight(); + + rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket); + /* arrow at the bottom of the rocket image */ + rocket_off_x = rocket_bitmap.getWidth() / 2; + rocket_off_y = rocket_bitmap.getHeight(); + + here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position); + /* Center of the dot */ + here_off_x = here_bitmap.getWidth() / 2; + here_off_y = here_bitmap.getHeight() / 2; + } + + public void set_map_type(int map_type) { + if (map != null) + map.set_maptype(map_type); + } + + public AltosMapOffline(Context context, AttributeSet attrs) { + super(context, attrs); + this.altos_droid = altos_droid; + scale_detector = new ScaleGestureDetector(context, this); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java new file mode 100644 index 00000000..4ac95c0b --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java @@ -0,0 +1,327 @@ +/* + * Copyright © 2013 Mike Beattie <mike@ethernal.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; + +import org.altusmetrum.altoslib_8.*; + +import com.google.android.gms.maps.*; +import com.google.android.gms.maps.model.*; + +import android.app.Activity; +import android.graphics.Color; +import android.graphics.*; +import android.os.Bundle; +import android.support.v4.app.Fragment; +//import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.location.Location; +import android.content.*; + +class RocketOnline implements Comparable { + Marker marker; + int serial; + long last_packet; + int size; + + void set_position(AltosLatLon position, long last_packet) { + marker.setPosition(new LatLng(position.lat, position.lon)); + this.last_packet = last_packet; + } + + private Bitmap rocket_bitmap(Context context, String text) { + + /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/ + */ + Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket); + Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + paint.setTextSize(40); + paint.setColor(0xff000000); + + Rect bounds = new Rect(); + paint.getTextBounds(text, 0, text.length(), bounds); + + int width = bounds.right - bounds.left; + int height = bounds.bottom - bounds.top; + + float x = bitmap.getWidth() / 2.0f - width / 2.0f; + float y = bitmap.getHeight() / 2.0f - height / 2.0f; + + size = bitmap.getWidth(); + + canvas.drawText(text, 0, text.length(), x, y, paint); + return bitmap; + } + + public void remove() { + marker.remove(); + } + + public int compareTo(Object o) { + RocketOnline other = (RocketOnline) o; + + long diff = last_packet - other.last_packet; + + if (diff > 0) + return 1; + if (diff < 0) + return -1; + return 0; + } + + RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) { + this.serial = serial; + String name = String.format("%d", serial); + this.marker = map.addMarker(new MarkerOptions() + .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name))) + .position(new LatLng(lat, lon)) + .visible(true)); + this.last_packet = last_packet; + } +} + +public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener { + public SupportMapFragment mMapFragment; + private GoogleMap mMap; + private boolean mapLoaded = false; + Context context; + + private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>(); + private Marker mPadMarker; + private boolean pad_set; + private Polyline mPolyline; + + private View map_view; + + private double mapAccuracy = -1; + + private AltosLatLon my_position = null; + private AltosLatLon target_position = null; + + private AltosDroid altos_droid; + + public void onCreateView(AltosDroid altos_droid) { + this.altos_droid = altos_droid; + final int map_type = altos_droid.map_type; + mMapFragment = new SupportMapFragment() { + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setupMap(map_type); + } + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + map_view = super.onCreateView(inflater, container, savedInstanceState); + return map_view; + } + @Override + public void onDestroyView() { + super.onDestroyView(); + map_view = null; + } + }; + } + +// public void onActivityCreated() { +// getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit(); +// } + + private double pixel_distance(LatLng a, LatLng b) { + Projection projection = mMap.getProjection(); + + Point a_pt = projection.toScreenLocation(a); + Point b_pt = projection.toScreenLocation(b); + + return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y)); + } + + private RocketOnline[] sorted_rockets() { + RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]); + + Arrays.sort(rocket_array); + return rocket_array; + } + + public void onMapClick(LatLng lat_lng) { + ArrayList<Integer> near = new ArrayList<Integer>(); + + for (RocketOnline rocket : sorted_rockets()) { + LatLng pos = rocket.marker.getPosition(); + + if (pos == null) + continue; + + double distance = pixel_distance(lat_lng, pos); + if (distance < rocket.size * 2) + near.add(rocket.serial); + } + + if (near.size() != 0) + altos_droid.touch_trackers(near.toArray(new Integer[0])); + } + + public boolean onMarkerClick(Marker marker) { + onMapClick(marker.getPosition()); + return true; + } + + public void setupMap(int map_type) { + mMap = mMapFragment.getMap(); + if (mMap != null) { + set_map_type(map_type); + mMap.setMyLocationEnabled(true); + mMap.getUiSettings().setTiltGesturesEnabled(false); + mMap.getUiSettings().setZoomControlsEnabled(false); + mMap.setOnMarkerClickListener(this); + mMap.setOnMapClickListener(this); + + mPadMarker = mMap.addMarker( + new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad)) + .position(new LatLng(0,0)) + .visible(false) + ); + + mPolyline = mMap.addPolyline( + new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0)) + .width(20) + .color(Color.BLUE) + .visible(false) + ); + + mapLoaded = true; + } + } + + public void center(double lat, double lon, double accuracy) { + if (mMap == null) + return; + + if (mapAccuracy < 0 || accuracy < mapAccuracy/10) { + mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14)); + mapAccuracy = accuracy; + } + } + + private void set_rocket(int serial, AltosState state) { + RocketOnline rocket; + + if (state.gps == null || state.gps.lat == AltosLib.MISSING) + return; + + if (mMap == null) + return; + + if (rockets.containsKey(serial)) { + rocket = rockets.get(serial); + rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time); + } else { + rocket = new RocketOnline(context, + serial, + mMap, state.gps.lat, state.gps.lon, + state.received_time); + rockets.put(serial, rocket); + } + } + + private void remove_rocket(int serial) { + RocketOnline rocket = rockets.get(serial); + rocket.remove(); + rockets.remove(serial); + } + + public void set_visible(boolean visible) { + if (map_view == null) + return; + if (visible) + map_view.setVisibility(View.VISIBLE); + else + map_view.setVisibility(View.GONE); + } + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { + + if (telem_state != null) { + for (int serial : rockets.keySet()) { + if (!telem_state.states.containsKey(serial)) + remove_rocket(serial); + } + + for (int serial : telem_state.states.keySet()) { + set_rocket(serial, telem_state.states.get(serial)); + } + } + + if (state != null) { + if (mapLoaded) { + if (!pad_set && state.pad_lat != AltosLib.MISSING) { + pad_set = true; + mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon)); + mPadMarker.setVisible(true); + } + } + if (state.gps != null) { + + target_position = new AltosLatLon(state.gps.lat, state.gps.lon); + if (state.gps.locked && state.gps.nsat >= 4) + center (state.gps.lat, state.gps.lon, 10); + } + } + + if (receiver != null) { + double accuracy; + + if (receiver.hasAccuracy()) + accuracy = receiver.getAccuracy(); + else + accuracy = 1000; + + my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude()); + center (my_position.lat, my_position.lon, accuracy); + } + + if (my_position != null && target_position != null && mPolyline != null) { + mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon))); + mPolyline.setVisible(true); + } + + } + + public void set_map_type(int map_type) { + if (mMap != null) { + if (map_type == AltosMap.maptype_hybrid) + mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); + else if (map_type == AltosMap.maptype_satellite) + mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); + else if (map_type == AltosMap.maptype_terrain) + mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN); + else + mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); + } + } + + public AltosMapOnline(Context context) { + this.context = context; + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java new file mode 100644 index 00000000..b7eb76a5 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java @@ -0,0 +1,230 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; +import java.util.HashMap; + +import android.content.Context; +import android.hardware.usb.*; +import android.app.*; +import android.os.Handler; + +import org.altusmetrum.altoslib_8.*; + +public class AltosUsb extends AltosDroidLink { + + private Thread input_thread = null; + + private Handler handler; + + private UsbManager manager; + private UsbDevice device; + private UsbDeviceConnection connection; + private UsbInterface iface; + private UsbEndpoint in, out; + + private InputStream input; + private OutputStream output; + + // Constructor + public AltosUsb(Context context, UsbDevice device, Handler handler) { + super(handler); +// set_debug(D); + this.handler = handler; + + iface = null; + in = null; + out = null; + + int niface = device.getInterfaceCount(); + + for (int i = 0; i < niface; i++) { + + iface = device.getInterface(i); + + in = null; + out = null; + + int nendpoints = iface.getEndpointCount(); + + for (int e = 0; e < nendpoints; e++) { + UsbEndpoint endpoint = iface.getEndpoint(e); + + if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { + switch (endpoint.getDirection()) { + case UsbConstants.USB_DIR_OUT: + out = endpoint; + break; + case UsbConstants.USB_DIR_IN: + in = endpoint; + break; + } + } + } + + if (in != null && out != null) + break; + } + + if (in != null && out != null) { + AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString()); + + manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + + if (manager == null) { + AltosDebug.debug("USB_SERVICE failed"); + return; + } + + connection = manager.openDevice(device); + + if (connection == null) { + AltosDebug.debug("openDevice failed"); + return; + } + + connection.claimInterface(iface, true); + + input_thread = new Thread(this); + input_thread.start(); + + // Configure the newly connected device for telemetry + print("~\nE 0\n"); + set_monitor(false); + } + } + + static private boolean isAltusMetrum(UsbDevice device) { + if (device.getVendorId() != AltosLib.vendor_altusmetrum) + return false; + if (device.getProductId() < AltosLib.product_altusmetrum_min) + return false; + if (device.getProductId() > AltosLib.product_altusmetrum_max) + return false; + return true; + } + + static boolean matchProduct(int want_product, UsbDevice device) { + + if (!isAltusMetrum(device)) + return false; + + if (want_product == AltosLib.product_any) + return true; + + int have_product = device.getProductId(); + + if (want_product == AltosLib.product_basestation) + return have_product == AltosLib.product_teledongle || + have_product == AltosLib.product_teleterra || + have_product == AltosLib.product_telebt || + have_product == AltosLib.product_megadongle; + + if (want_product == AltosLib.product_altimeter) + return have_product == AltosLib.product_telemetrum || + have_product == AltosLib.product_telemega || + have_product == AltosLib.product_easymega || + have_product == AltosLib.product_telegps || + have_product == AltosLib.product_easymini || + have_product == AltosLib.product_telemini; + + if (have_product == AltosLib.product_altusmetrum) /* old devices match any request */ + return true; + + if (want_product == have_product) + return true; + + return false; + } + + static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) { + UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + +// if (manager.hasPermission(device)) +// return true; + + AltosDebug.debug("request permission for USB device " + device.toString()); + + manager.requestPermission(device, pi); + return false; + } + + static public UsbDevice find_device(Context context, int match_product) { + UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + + HashMap<String,UsbDevice> devices = manager.getDeviceList(); + + for (UsbDevice device : devices.values()) { + int vendor = device.getVendorId(); + int product = device.getProductId(); + + if (matchProduct(match_product, device)) { + AltosDebug.debug("found USB device " + device.toString()); + return device; + } + } + + return null; + } + + private void disconnected() { + if (closed()) { + AltosDebug.debug("disconnected after closed"); + return; + } + + AltosDebug.debug("Sending disconnected message"); + handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget(); + } + + void close_device() { + UsbDeviceConnection tmp_connection; + + synchronized(this) { + tmp_connection = connection; + connection = null; + } + + if (tmp_connection != null) { + AltosDebug.debug("Closing USB device"); + tmp_connection.close(); + } + } + + int read(byte[] buffer, int len) { + int ret = connection.bulkTransfer(in, buffer, len, -1); + AltosDebug.debug("read(%d) = %d\n", len, ret); + return ret; + } + + int write(byte[] buffer, int len) { + int ret = connection.bulkTransfer(out, buffer, len, -1); + AltosDebug.debug("write(%d) = %d\n", len, ret); + return ret; + } + + // Stubs of required methods when extending AltosLink + public boolean can_cancel_reply() { return false; } + public boolean show_reply_timeout() { return true; } + public void hide_reply_timeout() { } + +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java index 223ae75a..f22298d7 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java @@ -34,14 +34,19 @@ public class AltosViewPager extends ViewPager { @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { - if(v.getClass() != null && - v.getClass().getPackage() != null && - v.getClass().getPackage().getName() != null && - v.getClass().getPackage().getName().startsWith("maps.")) - { - return true; - } - return super.canScroll(v, checkV, dx, x, y); + + if (v.getClass() != null && + v.getClass().getName() != null && + v.getClass().getName().endsWith("MapOffline")) + return true; + + if(v.getClass() != null && + v.getClass().getPackage() != null && + v.getClass().getPackage().getName() != null && + v.getClass().getPackage().getName().startsWith("maps.")) + return true; + + return super.canScroll(v, checkV, dx, x, y); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java index 2d32dc07..325b89d2 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java @@ -20,201 +20,305 @@ package org.altusmetrum.AltosDroid; import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech.OnInitListener; +import android.location.Location; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosVoice { private TextToSpeech tts = null; private boolean tts_enabled = false; - private IdleThread idle_thread = null; + static final int TELL_MODE_NONE = 0; + static final int TELL_MODE_PAD = 1; + static final int TELL_MODE_FLIGHT = 2; + static final int TELL_MODE_RECOVER = 3; - private AltosState old_state = null; + static final int TELL_FLIGHT_NONE = 0; + static final int TELL_FLIGHT_STATE = 1; + static final int TELL_FLIGHT_SPEED = 2; + static final int TELL_FLIGHT_HEIGHT = 3; + static final int TELL_FLIGHT_TRACK = 4; - public AltosVoice(AltosDroid a) { + private int last_tell_mode; + private int last_tell_serial = AltosLib.MISSING; + private int last_state; + private AltosGPS last_gps; + private double last_height = AltosLib.MISSING; + private Location last_receiver; + private long last_speak_time; + private int last_flight_tell = TELL_FLIGHT_NONE; + + private long now() { + return System.currentTimeMillis(); + } + + private void reset_last() { + last_tell_mode = TELL_MODE_NONE; + last_speak_time = now() - 100 * 1000; + last_gps = null; + last_height = AltosLib.MISSING; + last_receiver = null; + last_state = AltosLib.ao_flight_invalid; + last_flight_tell = TELL_FLIGHT_NONE; + } + public AltosVoice(AltosDroid a) { tts = new TextToSpeech(a, new OnInitListener() { public void onInit(int status) { if (status == TextToSpeech.SUCCESS) tts_enabled = true; - if (tts_enabled) { - idle_thread = new IdleThread(); - } } }); + reset_last(); + } + public synchronized void set_enable(boolean enable) { + tts_enabled = enable; } public synchronized void speak(String s) { if (!tts_enabled) return; + last_speak_time = now(); tts.speak(s, TextToSpeech.QUEUE_ADD, null); } + public synchronized long time_since_speak() { + return now() - last_speak_time; + } + + public synchronized void speak(String format, Object ... arguments) { + speak(String.format(format, arguments)); + } + + public synchronized boolean is_speaking() { + return tts.isSpeaking(); + } + public void stop() { - if (tts != null) tts.shutdown(); - if (idle_thread != null) { - idle_thread.interrupt(); - idle_thread = null; + if (tts != null) { + tts.stop(); + tts.shutdown(); } } - public void tell(AltosState state, AltosGreatCircle from_receiver) { - if (!tts_enabled) return; + private boolean last_apogee_good; + private boolean last_main_good; + private boolean last_gps_good; - boolean spoke = false; - if (old_state == null || old_state.state != state.state) { - if (state.state != AltosLib.ao_flight_stateless) - speak(state.state_name()); - if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) && - state.state > AltosLib.ao_flight_boost) { - if (state.max_speed() != AltosLib.MISSING) - speak(String.format("Max speed: %s.", - AltosConvert.speed.say_units(state.max_speed()))); - spoke = true; - } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) && - state.state >= AltosLib.ao_flight_drogue) { - if (state.max_height() != AltosLib.MISSING) - speak(String.format("Max height: %s.", - AltosConvert.height.say_units(state.max_height()))); - spoke = true; - } + private boolean tell_gonogo(String name, + boolean current, + boolean previous, + boolean new_mode) { + if (current != previous || new_mode) + speak("%s %s.", name, current ? "ready" : "not ready"); + return current; + } + + private boolean tell_pad(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver) { + + if (state == null) + return false; + + if (state.apogee_voltage != AltosLib.MISSING) + last_apogee_good = tell_gonogo("apogee", + state.apogee_voltage >= AltosLib.ao_igniter_good, + last_apogee_good, + last_tell_mode != TELL_MODE_PAD); + + if (state.main_voltage != AltosLib.MISSING) + last_main_good = tell_gonogo("main", + state.main_voltage >= AltosLib.ao_igniter_good, + last_main_good, + last_tell_mode != TELL_MODE_PAD); + + if (state.gps != null) + last_gps_good = tell_gonogo("G P S", + state.gps_ready, + last_gps_good, + last_tell_mode != TELL_MODE_PAD); + return true; + } + + + private boolean descending(int state) { + return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed; + } + + private boolean target_moved(AltosState state) { + if (last_gps != null && state != null && state.gps != null) { + AltosGreatCircle moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt, + state.gps.lat, state.gps.lon, state.gps.alt); + double height_change = 0; + double height = state.height(); + + if (height != AltosLib.MISSING && last_height != AltosLib.MISSING) + height_change = Math.abs(last_height - height); + + if (moved.range < 10 && height_change < 10) + return false; } - if (old_state == null || old_state.gps_ready != state.gps_ready) { - if (state.gps_ready) { - speak("GPS ready"); - spoke = true; - } else if (old_state != null) { - speak("GPS lost"); - spoke = true; - } + return true; + } + + private boolean receiver_moved(Location receiver) { + if (last_receiver != null && receiver != null) { + AltosGreatCircle moved = new AltosGreatCircle(last_receiver.getLatitude(), + last_receiver.getLongitude(), + last_receiver.getAltitude(), + receiver.getLatitude(), + receiver.getLongitude(), + receiver.getAltitude()); + if (moved.range < 10) + return false; } - old_state = state; - if (idle_thread != null) - idle_thread.notice(state, from_receiver, spoke); + return true; } + private boolean tell_flight(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver) { - class IdleThread extends Thread { - boolean started; - private AltosState state; - private AltosGreatCircle from_receiver; - int reported_landing; - int report_interval; - long report_time; + boolean spoken = false; - public synchronized void report(boolean last) { - if (state == null) - return; + if (state == null) + return false; - /* reset the landing count once we hear about a new flight */ - if (state.state < AltosLib.ao_flight_drogue) - reported_landing = 0; + if (last_tell_mode != TELL_MODE_FLIGHT) + last_flight_tell = TELL_FLIGHT_NONE; - /* Shut up once the rocket is on the ground */ - if (reported_landing > 2) { - return; + if (state.state != last_state && AltosLib.ao_flight_boost <= state.state && state.state <= AltosLib.ao_flight_landed) { + speak(state.state_name()); + if (descending(state.state) && !descending(last_state)) { + if (state.max_height() != AltosLib.MISSING) { + speak("max height: %s.", + AltosConvert.height.say_units(state.max_height())); + } } + last_flight_tell = TELL_FLIGHT_STATE; + return true; + } - /* If the rocket isn't on the pad, then report location */ - if ((AltosLib.ao_flight_drogue <= state.state && - state.state < AltosLib.ao_flight_landed) || - state.state == AltosLib.ao_flight_stateless) - { - AltosGreatCircle position; - - if (from_receiver != null) - position = from_receiver; - else - position = state.from_pad; - - if (position != null) { - speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n", - AltosConvert.height.say_units(state.height()), - position.bearing_words( - AltosGreatCircle.BEARING_VOICE), - (int) (position.bearing + 0.5), - (int) (position.elevation + 0.5), - AltosConvert.distance.say_units(position.range))); - } - } else if (state.state > AltosLib.ao_flight_pad) { - if (state.height() != AltosLib.MISSING) - speak(AltosConvert.height.say_units(state.height())); + if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) { + if (time_since_speak() < 10 * 1000) + return false; + if (!target_moved(state) && !receiver_moved(receiver)) + return false; + } + + double speed; + double height; + + if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) { + last_flight_tell = TELL_FLIGHT_SPEED; + + if (state.state <= AltosLib.ao_flight_coast) { + speed = state.speed(); } else { - reported_landing = 0; + speed = state.gps_speed(); + if (speed == AltosLib.MISSING) + speed = state.speed(); } - /* If the rocket is coming down, check to see if it has landed; - * either we've got a landed report or we haven't heard from it in - * a long time - */ - if (state.state >= AltosLib.ao_flight_drogue && - (last || - System.currentTimeMillis() - state.received_time >= 15000 || - state.state == AltosLib.ao_flight_landed)) - { - if (Math.abs(state.speed()) < 20 && state.height() < 100) - speak("rocket landed safely"); - else - speak("rocket may have crashed"); - if (state.from_pad != null) - speak(String.format("Bearing %d degrees, range %s.", - (int) (state.from_pad.bearing + 0.5), - AltosConvert.distance.say_units(state.from_pad.distance))); - ++reported_landing; + if (speed != AltosLib.MISSING) { + speak("speed: %s.", AltosConvert.speed.say_units(speed)); + return true; } } - long now () { - return System.currentTimeMillis(); - } + if (last_flight_tell == TELL_FLIGHT_SPEED) { + last_flight_tell = TELL_FLIGHT_HEIGHT; + height = state.height(); - void set_report_time() { - report_time = now() + report_interval; + if (height != AltosLib.MISSING) { + speak("height: %s.", AltosConvert.height.say_units(height)); + return true; + } } - public void run () { - try { - for (;;) { - set_report_time(); - for (;;) { - synchronized (this) { - long sleep_time = report_time - now(); - if (sleep_time <= 0) - break; - wait(sleep_time); - } - } - report(false); - } - } catch (InterruptedException ie) { + if (last_flight_tell == TELL_FLIGHT_HEIGHT) { + last_flight_tell = TELL_FLIGHT_TRACK; + if (from_receiver != null) { + speak("bearing %s %d, elevation %d, range %s.", + from_receiver.bearing_words( + AltosGreatCircle.BEARING_VOICE), + (int) (from_receiver.bearing + 0.5), + (int) (from_receiver.elevation + 0.5), + AltosConvert.distance.say(from_receiver.range)); + return true; } } - public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) { - AltosState old_state = state; - state = new_state; - from_receiver = new_from_receiver; - if (!started && state.state > AltosLib.ao_flight_pad) { - started = true; - start(); - } + return spoken; + } - if (state.state < AltosLib.ao_flight_drogue) - report_interval = 10000; - else - report_interval = 20000; - if (old_state != null && old_state.state != state.state) { - report_time = now(); - this.notify(); - } else if (spoken) - set_report_time(); - } + private boolean tell_recover(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver) { + + if (from_receiver == null) + return false; - public IdleThread() { - state = null; - reported_landing = 0; - report_interval = 10000; + if (last_tell_mode == TELL_MODE_RECOVER) { + if (!target_moved(state) && !receiver_moved(receiver)) + return false; + if (time_since_speak() <= 10 * 1000) + return false; } + + String direction = AltosDroid.direction(from_receiver, receiver); + if (direction == null) + direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5)); + + speak("%s, range %s.", direction, + AltosConvert.distance.say_units(from_receiver.distance)); + + return true; } + public void tell(TelemetryState telem_state, AltosState state, + AltosGreatCircle from_receiver, Location receiver, + AltosDroidTab tab) { + + boolean spoken = false; + + if (!tts_enabled) return; + + if (is_speaking()) return; + + int tell_serial = last_tell_serial; + + if (state != null) + tell_serial = state.serial; + + if (tell_serial != last_tell_serial) + reset_last(); + + int tell_mode = TELL_MODE_NONE; + + if (tab.tab_name().equals(AltosDroid.tab_pad_name)) + tell_mode = TELL_MODE_PAD; + else if (tab.tab_name().equals(AltosDroid.tab_flight_name)) + tell_mode = TELL_MODE_FLIGHT; + else + tell_mode = TELL_MODE_RECOVER; + + if (tell_mode == TELL_MODE_PAD) + spoken = tell_pad(telem_state, state, from_receiver, receiver); + else if (tell_mode == TELL_MODE_FLIGHT) + spoken = tell_flight(telem_state, state, from_receiver, receiver); + else + spoken = tell_recover(telem_state, state, from_receiver, receiver); + + if (spoken) { + last_tell_mode = tell_mode; + last_tell_serial = tell_serial; + if (state != null) { + last_state = state.state; + last_height = state.height(); + if (state.gps != null) + last_gps = state.gps; + } + if (receiver != null) + last_receiver = receiver; + } + } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java new file mode 100644 index 00000000..673d72dd --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +public class DeviceAddress { + public String address; + public String name; + + public DeviceAddress(String address, String name) { + this.address = address; + this.name = name; + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java index 71692122..f36ef267 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.util.Log; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; @@ -45,12 +44,10 @@ import android.widget.AdapterView.OnItemClickListener; * Activity in the result Intent. */ public class DeviceListActivity extends Activity { - // Debugging - private static final String TAG = "DeviceListActivity"; - private static final boolean D = true; // Return Intent extra - public static String EXTRA_DEVICE_ADDRESS = "device_address"; + public static final String EXTRA_DEVICE_ADDRESS = "device_address"; + public static final String EXTRA_DEVICE_NAME = "device_name"; // Member fields private BluetoothAdapter mBtAdapter; @@ -136,7 +133,7 @@ public class DeviceListActivity extends Activity { * Start device discover with the BluetoothAdapter */ private void doDiscovery() { - if (D) Log.d(TAG, "doDiscovery()"); + AltosDebug.debug("doDiscovery()"); // Indicate scanning in the title setProgressBarIndeterminateVisibility(true); @@ -164,9 +161,20 @@ public class DeviceListActivity extends Activity { String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); + int newline = info.indexOf('\n'); + + String name = null; + if (newline > 0) + name = info.substring(0, newline); + else + name = info; + + AltosDebug.debug("******* selected item '%s'", info); + // Create the result Intent and include the MAC address Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); + intent.putExtra(EXTRA_DEVICE_NAME, name); // Set result and finish this Activity setResult(Activity.RESULT_OK, intent); @@ -183,14 +191,22 @@ public class DeviceListActivity extends Activity { // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { - // Get the BluetoothDevice object from the Intent + + /* Get the BluetoothDevice object from the Intent + */ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); - // If it's already paired, skip it, because it's been listed already - if ( device.getBondState() != BluetoothDevice.BOND_BONDED - && device.getName().startsWith("TeleBT") ) { - mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); + + /* If it's already paired, skip it, because it's been listed already + */ + if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED) + { + String name = device.getName(); + if (name != null && name.startsWith("TeleBT")) + mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } - // When discovery is finished, change the Activity title + + /* When discovery is finished, change the Activity title + */ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); setTitle(R.string.select_device); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java index 267c90f8..6cecbdf1 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java @@ -52,27 +52,14 @@ public class GoNoGoLights { missing = m; set = true; if (missing) { - hide(); red.setImageDrawable(dGray); green.setImageDrawable(dGray); } else if (state) { red.setImageDrawable(dGray); green.setImageDrawable(dGreen); - show(); } else { red.setImageDrawable(dRed); green.setImageDrawable(dGray); - show(); } } - - public void show() { - red.setVisibility(View.VISIBLE); - green.setVisibility(View.VISIBLE); - } - - public void hide() { - red.setVisibility(View.GONE); - green.setVisibility(View.GONE); - } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java new file mode 100644 index 00000000..8846e56c --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java @@ -0,0 +1,84 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import org.altusmetrum.AltosDroid.R; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.widget.*; +import android.widget.AdapterView.*; + +import org.altusmetrum.altoslib_8.*; + +public class MapTypeActivity extends Activity { + private Button hybrid; + private Button satellite; + private Button roadmap; + private Button terrain; + private int selected_type; + + public static final String EXTRA_MAP_TYPE = "map_type"; + + private void done(int type) { + + Intent intent = new Intent(); + intent.putExtra(EXTRA_MAP_TYPE, type); + setResult(Activity.RESULT_OK, intent); + finish(); + } + + public void selectType(View view) { + AltosDebug.debug("selectType %s", view.toString()); + if (view == hybrid) + done(AltosMap.maptype_hybrid); + if (view == satellite) + done(AltosMap.maptype_satellite); + if (view == roadmap) + done(AltosMap.maptype_roadmap); + if (view == terrain) + done(AltosMap.maptype_terrain); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.map_type); + + hybrid = (Button) findViewById(R.id.map_type_hybrid); + satellite = (Button) findViewById(R.id.map_type_satellite); + roadmap = (Button) findViewById(R.id.map_type_roadmap); + terrain = (Button) findViewById(R.id.map_type_terrain); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java new file mode 100644 index 00000000..d7462089 --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java @@ -0,0 +1,418 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import java.util.*; +import java.io.*; +import java.text.*; + +import org.altusmetrum.AltosDroid.R; + +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.View.OnClickListener; +import android.widget.*; +import android.widget.AdapterView.*; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationListener; +import android.location.Criteria; + +import org.altusmetrum.altoslib_8.*; + +/** + * This Activity appears as a dialog. It lists any paired devices and + * devices detected in the area after discovery. When a device is chosen + * by the user, the MAC address of the device is sent back to the parent + * Activity in the result Intent. + */ +public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapInterface, AltosMapLoaderListener, LocationListener { + + private ArrayAdapter<AltosLaunchSite> known_sites_adapter; + + private CheckBox hybrid; + private CheckBox satellite; + private CheckBox roadmap; + private CheckBox terrain; + + private Spinner known_sites_spinner; + private Spinner min_zoom; + private Spinner max_zoom; + private TextView radius_label; + private Spinner radius; + + private EditText latitude; + private EditText longitude; + + private ProgressBar progress; + + /* AltosMapLoaderListener interfaces */ + public void loader_start(final int max) { + this.runOnUiThread(new Runnable() { + public void run() { + progress.setMax(max); + progress.setProgress(0); + } + }); + } + + public void loader_notify(final int cur, final int max, final String name) { + this.runOnUiThread(new Runnable() { + public void run() { + progress.setProgress(cur); + } + }); + } + + public void loader_done(int max) { + this.runOnUiThread(new Runnable() { + public void run() { + progress.setProgress(0); + finish(); + } + }); + } + + /* AltosLaunchSiteListener interface */ + public void notify_launch_sites(final List<AltosLaunchSite> sites) { + this.runOnUiThread(new Runnable() { + public void run() { + for (AltosLaunchSite site : sites) + known_sites_adapter.add(site); + } + }); + } + + AltosMap map; + AltosMapLoader loader; + + class PreloadMapImage implements AltosImage { + public void flush() { + } + + public PreloadMapImage(File file) { + } + } + + public AltosMapPath new_path() { + return null; + } + + public AltosMapLine new_line() { + return null; + } + + public AltosImage load_image(File file) throws Exception { + return new PreloadMapImage(file); + } + + public AltosMapMark new_mark(double lat, double lon, int state) { + return null; + } + + class PreloadMapTile extends AltosMapTile { + public void paint(AltosMapTransform t) { + } + + public PreloadMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(listener, upper_left, center, zoom, maptype, px_size, 2); + } + + } + + public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new PreloadMapTile(listener, upper_left, center, zoom, maptype, px_size); + } + + public int width() { + return AltosMap.px_size; + } + + public int height() { + return AltosMap.px_size; + } + + public void repaint() { + } + + public void repaint(AltosRectangle damage) { + } + + public void set_zoom_label(String label) { + } + + public void select_object(AltosLatLon latlon) { + } + + public void debug(String format, Object ... arguments) { + AltosDebug.debug(format, arguments); + } + + /* LocationProvider interface */ + + AltosLaunchSite current_location_site; + + public void onLocationChanged(Location location) { + AltosDebug.debug("location changed"); + if (current_location_site == null) { + AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem(); + + current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude()); + known_sites_adapter.insert(current_location_site, 0); + + if (selected_item != null) + known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item)); + else { + latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude))); + longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude))); + } + } else { + current_location_site.latitude = location.getLatitude(); + current_location_site.longitude = location.getLongitude(); + } + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + } + + public void onProviderEnabled(String provider) { + } + + public void onProviderDisabled(String provider) { + } + + private double text(EditText view) throws ParseException { + return AltosParse.parse_double_locale(view.getEditableText().toString()); + } + + private double latitude() throws ParseException { + return text(latitude); + } + + private double longitude() throws ParseException { + return text(longitude); + } + + private int value(Spinner spinner) { + return (Integer) spinner.getSelectedItem(); + } + + private int min_z() { + return value(min_zoom); + } + + private int max_z() { + return value(max_zoom); + } + + private double value_distance(Spinner spinner) { + return (Double) spinner.getSelectedItem(); + } + + private double radius() { + double r = value_distance(radius); + if (AltosPreferences.imperial_units()) + r = AltosConvert.distance.inverse(r); + else + r = r * 1000; + return r; + } + + private int bit(CheckBox box, int value) { + if (box.isChecked()) + return 1 << value; + return 0; + } + + private int types() { + return (bit(hybrid, AltosMap.maptype_hybrid) | + bit(satellite, AltosMap.maptype_satellite) | + bit(roadmap, AltosMap.maptype_roadmap) | + bit(terrain, AltosMap.maptype_terrain)); + } + + private void load() { + try { + double lat = latitude(); + double lon = longitude(); + int min = min_z(); + int max = max_z(); + double r = radius(); + int t = types(); + + AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n", + lat, lon, min, max, r, t); + loader.load(lat, lon, min, max, r, t); + } catch (ParseException e) { + AltosDebug.debug("PreloadMap load raised exception %s", e.toString()); + } + } + + private void add_numbers(Spinner spinner, int min, int max, int def) { + + ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item); + + int spinner_def = 0; + int pos = 0; + + for (int i = min; i <= max; i++) { + adapter.add(new Integer(i)); + if (i == def) + spinner_def = pos; + pos++; + } + + spinner.setAdapter(adapter); + spinner.setSelection(spinner_def); + } + + + private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) { + + ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item); + + int spinner_def = 0; + int pos = 0; + + double[] distances; + double def; + if (AltosPreferences.imperial_units()) { + distances = distances_mi; + def = def_mi; + } else { + distances = distances_km; + def = def_km; + } + + for (int i = 0; i < distances.length; i++) { + adapter.add(distances[i]); + if (distances[i] == def) + spinner_def = pos; + pos++; + } + + spinner.setAdapter(adapter); + spinner.setSelection(spinner_def); + } + + + + class SiteListListener implements OnItemSelectedListener { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos); + latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude))); + longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude))); + } + public void onNothingSelected(AdapterView<?> parent) { + } + + public SiteListListener() { + } + } + + double[] radius_mi = { 1, 2, 5, 10, 20 }; + double radius_def_mi = 2; + double[] radius_km = { 1, 2, 5, 10, 20, 30 }; + double radius_def_km = 2; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Setup the window + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setContentView(R.layout.map_preload); + + // Set result CANCELED incase the user backs out + setResult(Activity.RESULT_CANCELED); + + // Initialize the button to perform device discovery + Button loadButton = (Button) findViewById(R.id.preload_load); + loadButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + load(); + } + }); + + latitude = (EditText) findViewById(R.id.preload_latitude); + longitude = (EditText) findViewById(R.id.preload_longitude); + + hybrid = (CheckBox) findViewById(R.id.preload_hybrid); + satellite = (CheckBox) findViewById(R.id.preload_satellite); + roadmap = (CheckBox) findViewById(R.id.preload_roadmap); + terrain = (CheckBox) findViewById(R.id.preload_terrain); + + hybrid.setChecked(true); + + min_zoom = (Spinner) findViewById(R.id.preload_min_zoom); + add_numbers(min_zoom, + AltosMap.min_zoom - AltosMap.default_zoom, + AltosMap.max_zoom - AltosMap.default_zoom, -2); + max_zoom = (Spinner) findViewById(R.id.preload_max_zoom); + add_numbers(max_zoom, + AltosMap.min_zoom - AltosMap.default_zoom, + AltosMap.max_zoom - AltosMap.default_zoom, 2); + radius_label = (TextView) findViewById(R.id.preload_radius_label); + radius = (Spinner) findViewById(R.id.preload_radius); + if (AltosPreferences.imperial_units()) + radius_label.setText("Radius (miles)"); + else + radius_label.setText("Radius (km)"); + add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi); + + progress = (ProgressBar) findViewById(R.id.preload_progress); + + // Initialize array adapters. One for already paired devices and + // one for newly discovered devices + known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list); + + known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item); + + known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + + known_sites_spinner.setAdapter(known_sites_adapter); + known_sites_spinner.setOnItemSelectedListener(new SiteListListener()); + + map = new AltosMap(this); + + loader = new AltosMapLoader(map, this); + + // Listen for GPS and Network position updates + LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); + + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this); + + new AltosLaunchSites(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + // Stop listening for location updates + ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this); + } +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java deleted file mode 100644 index 23de9622..00000000 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2013 Mike Beattie <mike@ethernal.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.AltosDroid; - -import org.altusmetrum.altoslib_6.*; - -import android.app.Activity; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import android.location.Location; - -public class TabAscent extends AltosDroidTab { - AltosDroid mAltosDroid; - - private TextView mHeightView; - private TextView mMaxHeightView; - private TextView mSpeedView; - private TextView mMaxSpeedView; - private TextView mAccelView; - private TextView mMaxAccelView; - private TextView mLatitudeView; - private TextView mLongitudeView; - private TextView mApogeeVoltageView; - private GoNoGoLights mApogeeLights; - private TextView mMainVoltageView; - private GoNoGoLights mMainLights; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.tab_ascent, container, false); - - mHeightView = (TextView) v.findViewById(R.id.height_value); - mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value); - mSpeedView = (TextView) v.findViewById(R.id.speed_value); - mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value); - mAccelView = (TextView) v.findViewById(R.id.accel_value); - mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value); - mLatitudeView = (TextView) v.findViewById(R.id.lat_value); - mLongitudeView = (TextView) v.findViewById(R.id.lon_value); - - mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value); - mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), - (ImageView) v.findViewById(R.id.apogee_greenled), - getResources()); - - mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value); - mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), - (ImageView) v.findViewById(R.id.main_greenled), - getResources()); - - return v; - } - - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - - public String tab_name() { - return "ascent"; - } - - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { - if (state != null) { - set_value(mHeightView, AltosConvert.height, 6, state.height()); - set_value(mHeightView, AltosConvert.height, 6, state.height()); - set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height()); - set_value(mSpeedView, AltosConvert.speed, 6, state.speed()); - set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed()); - set_value(mAccelView, AltosConvert.accel, 6, state.acceleration()); - set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration()); - - if (state.gps != null) { - mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S")); - mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W")); - } else { - mLatitudeView.setText(""); - mLongitudeView.setText(""); - } - - mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); - mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING); - - mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage)); - mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING); - } - } -} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java deleted file mode 100644 index 4ec6f409..00000000 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright © 2013 Mike Beattie <mike@ethernal.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.AltosDroid; - -import org.altusmetrum.altoslib_6.*; - -import android.app.Activity; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import android.location.Location; - -public class TabDescent extends AltosDroidTab { - AltosDroid mAltosDroid; - - private TextView mSpeedView; - private TextView mHeightView; - private TextView mElevationView; - private TextView mRangeView; - private TextView mBearingView; - private TextView mCompassView; - private TextView mDistanceView; - private TextView mLatitudeView; - private TextView mLongitudeView; - private TextView mApogeeVoltageView; - private GoNoGoLights mApogeeLights; - private TextView mMainVoltageView; - private GoNoGoLights mMainLights; - - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.tab_descent, container, false); - - mSpeedView = (TextView) v.findViewById(R.id.speed_value); - mHeightView = (TextView) v.findViewById(R.id.height_value); - mElevationView = (TextView) v.findViewById(R.id.elevation_value); - mRangeView = (TextView) v.findViewById(R.id.range_value); - mBearingView = (TextView) v.findViewById(R.id.bearing_value); - mCompassView = (TextView) v.findViewById(R.id.compass_value); - mDistanceView = (TextView) v.findViewById(R.id.distance_value); - mLatitudeView = (TextView) v.findViewById(R.id.lat_value); - mLongitudeView = (TextView) v.findViewById(R.id.lon_value); - - mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value); - mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), - (ImageView) v.findViewById(R.id.apogee_greenled), - getResources()); - - mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value); - mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), - (ImageView) v.findViewById(R.id.main_greenled), - getResources()); - - return v; - } - - - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - - public String tab_name() { return "descent"; } - - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { - if (state != null) { - set_value(mSpeedView, AltosConvert.speed, 6, state.speed()); - set_value(mHeightView, AltosConvert.height, 6, state.height()); - if (from_receiver != null) { - mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation)); - set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range); - mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing)); - mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG)); - set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance); - } else { - mElevationView.setText("<unknown>"); - mRangeView.setText("<unknown>"); - mBearingView.setText("<unknown>"); - mCompassView.setText("<unknown>"); - mDistanceView.setText("<unknown>"); - } - if (state.gps != null) { - mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S")); - mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W")); - } - - mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); - mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING); - - mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage)); - mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING); - } - } - -} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java new file mode 100644 index 00000000..a503f1bc --- /dev/null +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java @@ -0,0 +1,127 @@ +/* + * Copyright © 2013 Mike Beattie <mike@ethernal.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.AltosDroid; + +import org.altusmetrum.altoslib_8.*; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.*; +import android.widget.*; +import android.location.Location; + +public class TabFlight extends AltosDroidTab { + private TextView speed_view; + private TextView height_view; + private TextView max_speed_view; + private TextView max_height_view; + private TextView elevation_view; + private TextView range_view; + private TextView bearing_view; + private TextView compass_view; + private TextView distance_view; + private TextView latitude_view; + private TextView longitude_view; + private View apogee_view; + private TextView apogee_voltage_view; + private TextView apogee_voltage_label; + private GoNoGoLights apogee_lights; + private View main_view; + private TextView main_voltage_view; + private TextView main_voltage_label; + private GoNoGoLights main_lights; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.tab_flight, container, false); + + speed_view = (TextView) v.findViewById(R.id.speed_value); + height_view = (TextView) v.findViewById(R.id.height_value); + max_speed_view = (TextView) v.findViewById(R.id.max_speed_value); + max_height_view= (TextView) v.findViewById(R.id.max_height_value); + elevation_view = (TextView) v.findViewById(R.id.elevation_value); + range_view = (TextView) v.findViewById(R.id.range_value); + bearing_view = (TextView) v.findViewById(R.id.bearing_value); + compass_view = (TextView) v.findViewById(R.id.compass_value); + distance_view = (TextView) v.findViewById(R.id.distance_value); + latitude_view = (TextView) v.findViewById(R.id.lat_value); + longitude_view = (TextView) v.findViewById(R.id.lon_value); + + apogee_view = v.findViewById(R.id.apogee_view); + apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value); + apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), + (ImageView) v.findViewById(R.id.apogee_greenled), + getResources()); + apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label); + + main_view = v.findViewById(R.id.main_view); + main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value); + main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), + (ImageView) v.findViewById(R.id.main_greenled), + getResources()); + main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label); + + return v; + } + + public String tab_name() { return AltosDroid.tab_flight_name; } + + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { + if (state != null) { + set_value(speed_view, AltosConvert.speed, 6, state.speed()); + set_value(height_view, AltosConvert.height, 6, state.height()); + set_value(max_speed_view, AltosConvert.speed, 6, state.max_speed()); + set_value(max_height_view, AltosConvert.speed, 6, state.max_height()); + if (from_receiver != null) { + elevation_view.setText(AltosDroid.number("%3.0f°", from_receiver.elevation)); + set_value(range_view, AltosConvert.distance, 6, from_receiver.range); + bearing_view.setText(AltosDroid.number("%3.0f°", from_receiver.bearing)); + compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG)); + set_value(distance_view, AltosConvert.distance, 6, from_receiver.distance); + } else { + elevation_view.setText("<unknown>"); + range_view.setText("<unknown>"); + bearing_view.setText("<unknown>"); + compass_view.setText("<unknown>"); + distance_view.setText("<unknown>"); + } + if (state.gps != null) { + latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S")); + longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W")); + } + + if (state.apogee_voltage == AltosLib.MISSING) { + apogee_view.setVisibility(View.GONE); + } else { + apogee_voltage_view.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); + apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING); + apogee_view.setVisibility(View.VISIBLE); + } + + if (state.main_voltage == AltosLib.MISSING) { + main_view.setVisibility(View.GONE); + } else { + main_voltage_view.setText(AltosDroid.number("%4.2f V", state.main_voltage)); + main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING); + main_view.setVisibility(View.VISIBLE); + } + } + } + +} diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java index 871b94a1..54ccd18f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java @@ -17,186 +17,159 @@ 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() { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java index 0ac78219..4d04316f 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java @@ -17,155 +17,222 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; +import android.view.*; +import android.widget.*; import android.location.Location; public class TabPad extends AltosDroidTab { - AltosDroid mAltosDroid; - - private TextView mBatteryVoltageView; - private TextView mBatteryVoltageLabel; - private GoNoGoLights mBatteryLights; - private TextView mApogeeVoltageView; - private TextView mApogeeVoltageLabel; - private GoNoGoLights mApogeeLights; - private TextView mMainVoltageView; - private TextView mMainVoltageLabel; - private GoNoGoLights mMainLights; - private TextView mDataLoggingView; - private GoNoGoLights mDataLoggingLights; - private TextView mGPSLockedView; - private GoNoGoLights mGPSLockedLights; - private TextView mGPSReadyView; - private GoNoGoLights mGPSReadyLights; - private TextView mPadLatitudeView; - private TextView mPadLongitudeView; - private TextView mPadAltitudeView; + private TextView battery_voltage_view; + private GoNoGoLights battery_lights; + + private TableRow receiver_row; + private TextView receiver_voltage_view; + private TextView receiver_voltage_label; + private GoNoGoLights receiver_voltage_lights; + + private TableRow apogee_row; + private TextView apogee_voltage_view; + private TextView apogee_voltage_label; + private GoNoGoLights apogee_lights; + + private TableRow main_row; + private TextView main_voltage_view; + private TextView main_voltage_label; + private GoNoGoLights main_lights; + + private TextView data_logging_view; + private GoNoGoLights data_logging_lights; + + private TextView gps_locked_view; + private GoNoGoLights gps_locked_lights; + + private TextView gps_ready_view; + private GoNoGoLights gps_ready_lights; + + private TextView receiver_latitude_view; + private TextView receiver_longitude_view; + private TextView receiver_altitude_view; + + private TableRow[] ignite_row = new TableRow[4]; + private TextView[] ignite_voltage_view = new TextView[4]; + private TextView[] ignite_voltage_label = new TextView[4]; + private GoNoGoLights[] ignite_lights = new GoNoGoLights[4]; - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mAltosDroid = (AltosDroid) activity; - mAltosDroid.registerTab(this); - } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.tab_pad, container, false); - mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value); - mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label); - mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled), + battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value); + battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled), (ImageView) v.findViewById(R.id.battery_greenled), getResources()); - mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value); - mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label); - mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), + receiver_row = (TableRow) v.findViewById(R.id.receiver_row); + receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value); + receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label); + receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled), + (ImageView) v.findViewById(R.id.receiver_greenled), + getResources()); + + apogee_row = (TableRow) v.findViewById(R.id.apogee_row); + apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value); + apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label); + apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled), (ImageView) v.findViewById(R.id.apogee_greenled), getResources()); - mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value); - mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label); - mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), + main_row = (TableRow) v.findViewById(R.id.main_row); + main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value); + main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label); + main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled), (ImageView) v.findViewById(R.id.main_greenled), getResources()); - mDataLoggingView = (TextView) v.findViewById(R.id.logging_value); - mDataLoggingLights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled), + data_logging_view = (TextView) v.findViewById(R.id.logging_value); + data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled), (ImageView) v.findViewById(R.id.logging_greenled), getResources()); - mGPSLockedView = (TextView) v.findViewById(R.id.gps_locked_value); - mGPSLockedLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled), + gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value); + gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled), (ImageView) v.findViewById(R.id.gps_locked_greenled), getResources()); - mGPSReadyView = (TextView) v.findViewById(R.id.gps_ready_value); - mGPSReadyLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled), + gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value); + gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled), (ImageView) v.findViewById(R.id.gps_ready_greenled), getResources()); - mPadLatitudeView = (TextView) v.findViewById(R.id.pad_lat_value); - mPadLongitudeView = (TextView) v.findViewById(R.id.pad_lon_value); - mPadAltitudeView = (TextView) v.findViewById(R.id.pad_alt_value); - return v; - } + for (int i = 0; i < 4; i++) { + int row_id, view_id, label_id, lights_id; + int red_id, green_id; + switch (i) { + case 0: + default: + row_id = R.id.ignite_a_row; + view_id = R.id.ignite_a_voltage_value; + label_id = R.id.ignite_a_voltage_label; + red_id = R.id.ignite_a_redled; + green_id = R.id.ignite_a_greenled; + break; + case 1: + row_id = R.id.ignite_b_row; + view_id = R.id.ignite_b_voltage_value; + label_id = R.id.ignite_b_voltage_label; + red_id = R.id.ignite_b_redled; + green_id = R.id.ignite_b_greenled; + break; + case 2: + row_id = R.id.ignite_c_row; + view_id = R.id.ignite_c_voltage_value; + label_id = R.id.ignite_c_voltage_label; + red_id = R.id.ignite_c_redled; + green_id = R.id.ignite_c_greenled; + break; + case 3: + row_id = R.id.ignite_d_row; + view_id = R.id.ignite_d_voltage_value; + label_id = R.id.ignite_d_voltage_label; + red_id = R.id.ignite_d_redled; + green_id = R.id.ignite_d_greenled; + break; + } + ignite_row[i] = (TableRow) v.findViewById(row_id); + ignite_voltage_view[i] = (TextView) v.findViewById(view_id); + ignite_voltage_label[i] = (TextView) v.findViewById(label_id); + ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id), + (ImageView) v.findViewById(green_id), + getResources()); + } - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; + receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value); + receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value); + receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value); + return v; } - public String tab_name() { return "pad"; } + public String tab_name() { return AltosDroid.tab_pad_name; } - public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) { + public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) { if (state != null) { - mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage)); - mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING); + battery_voltage_view.setText(AltosDroid.number(" %4.2f V", state.battery_voltage)); + battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING); if (state.apogee_voltage == AltosLib.MISSING) { - mApogeeVoltageView.setVisibility(View.GONE); - mApogeeVoltageLabel.setVisibility(View.GONE); + apogee_row.setVisibility(View.GONE); } else { - mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage)); - mApogeeVoltageView.setVisibility(View.VISIBLE); - mApogeeVoltageLabel.setVisibility(View.VISIBLE); + apogee_voltage_view.setText(AltosDroid.number(" %4.2f V", state.apogee_voltage)); + apogee_row.setVisibility(View.VISIBLE); } - mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING); + apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING); if (state.main_voltage == AltosLib.MISSING) { - mMainVoltageView.setVisibility(View.GONE); - mMainVoltageLabel.setVisibility(View.GONE); + main_row.setVisibility(View.GONE); } else { - mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage)); - mMainVoltageView.setVisibility(View.VISIBLE); - mMainVoltageLabel.setVisibility(View.VISIBLE); + main_voltage_view.setText(AltosDroid.number(" %4.2f V", state.main_voltage)); + main_row.setVisibility(View.VISIBLE); + } + main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING); + + int num_igniter = state.ignitor_voltage == null ? 0 : state.ignitor_voltage.length; + + for (int i = 0; i < 4; i++) { + double voltage = i >= num_igniter ? AltosLib.MISSING : state.ignitor_voltage[i]; + if (voltage == AltosLib.MISSING) { + ignite_row[i].setVisibility(View.GONE); + } else { + ignite_voltage_view[i].setText(AltosDroid.number(" %4.2f V", voltage)); + ignite_row[i].setVisibility(View.VISIBLE); + } + ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING); } - mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING); if (state.flight != 0) { if (state.state <= AltosLib.ao_flight_pad) - mDataLoggingView.setText("Ready to record"); + data_logging_view.setText("Ready to record"); else if (state.state < AltosLib.ao_flight_landed) - mDataLoggingView.setText("Recording data"); + data_logging_view.setText("Recording data"); else - mDataLoggingView.setText("Recorded data"); + data_logging_view.setText("Recorded data"); } else { - mDataLoggingView.setText("Storage full"); + data_logging_view.setText("Storage full"); } - mDataLoggingLights.set(state.flight != 0, state.flight == AltosLib.MISSING); + data_logging_lights.set(state.flight != 0, state.flight == AltosLib.MISSING); if (state.gps != null) { int soln = state.gps.nsat; int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0; - mGPSLockedView.setText(String.format("%4d in soln, %4d in view", soln, nsat)); - mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4, false); + gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat)); + gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false); if (state.gps_ready) - mGPSReadyView.setText("Ready"); + gps_ready_view.setText("Ready"); else - mGPSReadyView.setText(AltosDroid.integer("Waiting %d", state.gps_waiting)); + gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting)); } else - mGPSLockedLights.set(false, true); - mGPSReadyLights.set(state.gps_ready, state.gps == null); + gps_locked_lights.set(false, true); + gps_ready_lights.set(state.gps_ready, state.gps == null); + } + + if (telem_state != null) { + if (telem_state.receiver_battery == AltosLib.MISSING) { + receiver_row.setVisibility(View.GONE); + } else { + receiver_voltage_view.setText(AltosDroid.number(" %4.2f V", telem_state.receiver_battery)); + receiver_row.setVisibility(View.VISIBLE); + } + receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING); } if (receiver != null) { double altitude = AltosLib.MISSING; if (receiver.hasAltitude()) altitude = receiver.getAltitude(); - mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S")); - mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W")); - set_value(mPadAltitudeView, AltosConvert.height, 6, altitude); + receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S")); + receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W")); + set_value(receiver_altitude_view, AltosConvert.height, 1, altitude); } } - } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java index 4c69d869..19bb79d3 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java @@ -17,7 +17,7 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import android.app.Activity; import android.os.Bundle; @@ -28,10 +28,9 @@ import android.view.ViewGroup; import android.widget.TextView; import android.location.Location; -public class TabLanded extends AltosDroidTab { - AltosDroid mAltosDroid; - +public class TabRecover extends AltosDroidTab { private TextView mBearingView; + private TextView mDirectionView; private TextView mDistanceView; private TextView mTargetLatitudeView; private TextView mTargetLongitudeView; @@ -41,19 +40,12 @@ public class TabLanded extends AltosDroidTab { 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); + 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); @@ -66,19 +58,17 @@ public class TabLanded extends AltosDroidTab { return v; } - @Override - public void onDestroy() { - super.onDestroy(); - mAltosDroid.unregisterTab(this); - mAltosDroid = null; - } - - public String tab_name() { return "landed"; } + public String tab_name() { return AltosDroid.tab_recover_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 (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")); diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java index 1ac34f9d..b34a25b6 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java @@ -29,7 +29,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TabHost; import android.widget.TabWidget; -import android.util.Log; /** * This is a helper class that implements the management of tabs and all @@ -106,7 +105,7 @@ public class TabsAdapter extends FragmentPagerAdapter @Override public Fragment getItem(int position) { TabInfo info = mTabs.get(position); - Log.d(AltosDroid.TAG, String.format("TabsAdapter.getItem(%d)", position)); + AltosDebug.debug("TabsAdapter.getItem(%d)", position); info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args); return info.fragment; } @@ -131,7 +130,7 @@ public class TabsAdapter extends FragmentPagerAdapter if (cur_frag != null) { cur_frag.set_visible(true); } - Log.d(AltosDroid.TAG, String.format("TabsAdapter.onTabChanged(%s) = %d", tabId, position)); + AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position); mViewPager.setCurrentItem(position); } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java index 9764ab72..79020c16 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java @@ -1,18 +1,14 @@ package org.altusmetrum.AltosDroid; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Environment; -import android.util.Log; public class TelemetryLogger { - private static final String TAG = "TelemetryLogger"; - private static final boolean D = true; - private Context context = null; private AltosLink link = null; private AltosLog logger = null; @@ -33,21 +29,21 @@ public class TelemetryLogger { private void close() { if (logger != null) { - if (D) Log.d(TAG, "Shutting down Telemetry Logging"); + AltosDebug.debug("Shutting down Telemetry Logging"); logger.close(); logger = null; } } - + void handleExternalStorageState() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { if (logger == null) { - if (D) Log.d(TAG, "Starting up Telemetry Logging"); + AltosDebug.debug("Starting up Telemetry Logging"); logger = new AltosLog(link); } } else { - if (D) Log.d(TAG, "External Storage not present - stopping"); + AltosDebug.debug("External Storage not present - stopping"); close(); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java index 4e4408d5..3199f252 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java @@ -21,42 +21,32 @@ package org.altusmetrum.AltosDroid; import java.text.*; import java.io.*; +import java.util.*; import java.util.concurrent.*; -import android.util.Log; import android.os.Handler; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class TelemetryReader extends Thread { - private static final String TAG = "TelemetryReader"; - private static final boolean D = true; - int crc_errors; Handler handler; AltosLink link; - AltosState state = null; LinkedBlockingQueue<AltosLine> telemQueue; - public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException { + public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException { AltosLine l = telemQueue.take(); if (l.line == null) throw new IOException("IO error"); AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line); - if (state == null) - state = new AltosState(); - else - state = state.clone(); - telem.update_state(state); - return state; + return telem; } public void close() { - state = null; link.remove_monitor(telemQueue); link = null; telemQueue.clear(); @@ -64,16 +54,14 @@ public class TelemetryReader extends Thread { } public void run() { - AltosState state = null; - try { - if (D) Log.d(TAG, "starting loop"); + AltosDebug.debug("starting loop"); while (telemQueue != null) { try { - state = read(); - handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget(); + AltosTelemetry telem = read(); + handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget(); } catch (ParseException pp) { - Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage())); + AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()); } catch (AltosCRCException ce) { ++crc_errors; handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget(); @@ -81,21 +69,22 @@ public class TelemetryReader extends Thread { } } catch (InterruptedException ee) { } catch (IOException ie) { + AltosDebug.error("IO exception in telemetry reader"); + handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget(); } finally { close(); } } - public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) { - if (D) Log.d(TAG, "connected TelemetryReader create started"); + public TelemetryReader (AltosLink in_link, Handler in_handler) { + AltosDebug.debug("connected TelemetryReader create started"); link = in_link; handler = in_handler; - state = in_state; telemQueue = new LinkedBlockingQueue<AltosLine>(); link.add_monitor(telemQueue); link.set_telemetry(AltosLib.ao_telemetry_standard); - if (D) Log.d(TAG, "connected TelemetryReader created"); + AltosDebug.debug("connected TelemetryReader created"); } } diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java index d4ac66aa..4a056d95 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java @@ -18,10 +18,8 @@ package org.altusmetrum.AltosDroid; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.concurrent.TimeoutException; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; import android.app.Notification; //import android.app.NotificationManager; @@ -29,6 +27,7 @@ import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothAdapter; +import android.hardware.usb.*; import android.content.Intent; import android.content.Context; import android.os.Bundle; @@ -38,55 +37,50 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.os.Looper; -import android.util.Log; import android.widget.Toast; import android.location.Location; import android.location.LocationManager; import android.location.LocationListener; import android.location.Criteria; -import org.altusmetrum.altoslib_6.*; - +import org.altusmetrum.altoslib_8.*; public class TelemetryService extends Service implements LocationListener { - private static final String TAG = "TelemetryService"; - private static final boolean D = true; - static final int MSG_REGISTER_CLIENT = 1; static final int MSG_UNREGISTER_CLIENT = 2; static final int MSG_CONNECT = 3; - static final int MSG_CONNECTED = 4; - static final int MSG_CONNECT_FAILED = 5; - static final int MSG_DISCONNECTED = 6; - static final int MSG_TELEMETRY = 7; - static final int MSG_SETFREQUENCY = 8; - static final int MSG_CRC_ERROR = 9; - static final int MSG_SETBAUD = 10; + static final int MSG_OPEN_USB = 4; + static final int MSG_CONNECTED = 5; + static final int MSG_CONNECT_FAILED = 6; + static final int MSG_DISCONNECTED = 7; + static final int MSG_TELEMETRY = 8; + static final int MSG_SETFREQUENCY = 9; + static final int MSG_CRC_ERROR = 10; + static final int MSG_SETBAUD = 11; + static final int MSG_DISCONNECT = 12; + static final int MSG_DELETE_SERIAL = 13; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. private int NOTIFICATION = R.string.telemetry_service_label; //private NotificationManager mNM; - // Timer - we wake up every now and then to decide if the service should stop - private Timer timer = new Timer(); - - ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients. - final Handler mHandler = new IncomingHandler(this); - final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler. + ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients. + final Handler handler = new IncomingHandler(this); + final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler. // Name of the connected device - String address; - private AltosBluetooth mAltosBluetooth = null; - private TelemetryReader mTelemetryReader = null; - private TelemetryLogger mTelemetryLogger = null; - // Local Bluetooth adapter - private BluetoothAdapter mBluetoothAdapter = null; + DeviceAddress address; + private AltosDroidLink altos_link = null; + private TelemetryReader telemetry_reader = null; + private TelemetryLogger telemetry_logger = null; - private TelemetryState telemetry_state; + // Local Bluetooth adapter + private BluetoothAdapter bluetooth_adapter = null; // Last data seen; send to UI when it starts + private TelemetryState telemetry_state; // Handler of incoming messages from clients. static class IncomingHandler extends Handler { @@ -96,83 +90,121 @@ public class TelemetryService extends Service implements LocationListener { @Override public void handleMessage(Message msg) { TelemetryService s = service.get(); + AltosDroidLink bt = null; if (s == null) return; switch (msg.what) { + + /* Messages from application */ case MSG_REGISTER_CLIENT: - s.mClients.add(msg.replyTo); - try { - // Now we try to send the freshly connected UI any relavant information about what - // we're talking to - msg.replyTo.send(s.message()); - } catch (RemoteException e) { - s.mClients.remove(msg.replyTo); - } - if (D) Log.d(TAG, "Client bound to service"); + s.add_client(msg.replyTo); break; case MSG_UNREGISTER_CLIENT: - s.mClients.remove(msg.replyTo); - if (D) Log.d(TAG, "Client unbound from service"); + s.remove_client(msg.replyTo); break; case MSG_CONNECT: - if (D) Log.d(TAG, "Connect command received"); - String address = (String) msg.obj; + AltosDebug.debug("Connect command received"); + DeviceAddress address = (DeviceAddress) msg.obj; AltosDroidPreferences.set_active_device(address); - s.startAltosBluetooth(address); - break; - case MSG_CONNECTED: - if (D) Log.d(TAG, "Connected to device"); - try { - s.connected(); - } catch (InterruptedException ie) { - } + s.start_altos_bluetooth(address, false); break; - case MSG_CONNECT_FAILED: - if (D) Log.d(TAG, "Connection failed... retrying"); - if (s.address != null) - s.startAltosBluetooth(s.address); + case MSG_OPEN_USB: + AltosDebug.debug("Open USB command received"); + UsbDevice device = (UsbDevice) msg.obj; + s.start_usb(device); break; - case MSG_DISCONNECTED: - Log.d(TAG, "MSG_DISCONNECTED"); - s.stopAltosBluetooth(); + case MSG_DISCONNECT: + AltosDebug.debug("Disconnect command received"); + s.address = null; + s.disconnect(true); break; - case MSG_TELEMETRY: - // forward telemetry messages - s.telemetry_state.state = (AltosState) msg.obj; - if (s.telemetry_state.state != null) { - if (D) Log.d(TAG, "Save state"); - AltosPreferences.set_state(0, s.telemetry_state.state, null); - } - if (D) Log.d(TAG, "MSG_TELEMETRY"); - s.sendMessageToClients(); - break; - case MSG_CRC_ERROR: - // forward crc error messages - s.telemetry_state.crc_errors = (Integer) msg.obj; - if (D) Log.d(TAG, "MSG_CRC_ERROR"); - s.sendMessageToClients(); + case MSG_DELETE_SERIAL: + AltosDebug.debug("Delete Serial command received"); + s.delete_serial((Integer) msg.obj); break; case MSG_SETFREQUENCY: - if (D) Log.d(TAG, "MSG_SETFREQUENCY"); + AltosDebug.debug("MSG_SETFREQUENCY"); s.telemetry_state.frequency = (Double) msg.obj; if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) { try { - s.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency); - s.mAltosBluetooth.save_frequency(); + s.altos_link.set_radio_frequency(s.telemetry_state.frequency); + s.altos_link.save_frequency(); } catch (InterruptedException e) { } catch (TimeoutException e) { } } - s.sendMessageToClients(); + s.send_to_clients(); break; case MSG_SETBAUD: - if (D) Log.d(TAG, "MSG_SETBAUD"); + AltosDebug.debug("MSG_SETBAUD"); s.telemetry_state.telemetry_rate = (Integer) msg.obj; if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) { - s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate); - s.mAltosBluetooth.save_telemetry_rate(); + s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate); + s.altos_link.save_telemetry_rate(); + } + s.send_to_clients(); + break; + + /* + *Messages from AltosBluetooth + */ + case MSG_CONNECTED: + AltosDebug.debug("MSG_CONNECTED"); + bt = (AltosDroidLink) msg.obj; + + if (bt != s.altos_link) { + AltosDebug.debug("Stale message"); + break; + } + AltosDebug.debug("Connected to device"); + try { + s.connected(); + } catch (InterruptedException ie) { + } + break; + case MSG_CONNECT_FAILED: + AltosDebug.debug("MSG_CONNECT_FAILED"); + bt = (AltosDroidLink) msg.obj; + + if (bt != s.altos_link) { + AltosDebug.debug("Stale message"); + break; + } + if (s.address != null) { + AltosDebug.debug("Connection failed... retrying"); + s.start_altos_bluetooth(s.address, true); + } else { + s.disconnect(true); } - s.sendMessageToClients(); + break; + case MSG_DISCONNECTED: + + /* This can be sent by either AltosDroidLink or TelemetryReader */ + AltosDebug.debug("MSG_DISCONNECTED"); + bt = (AltosDroidLink) msg.obj; + + if (bt != s.altos_link) { + AltosDebug.debug("Stale message"); + break; + } + if (s.address != null) { + AltosDebug.debug("Connection lost... retrying"); + s.start_altos_bluetooth(s.address, true); + } else { + s.disconnect(true); + } + break; + + /* + * Messages from TelemetryReader + */ + case MSG_TELEMETRY: + s.telemetry((AltosTelemetry) msg.obj); + break; + case MSG_CRC_ERROR: + // forward crc error messages + s.telemetry_state.crc_errors = (Integer) msg.obj; + s.send_to_clients(); break; default: super.handleMessage(msg); @@ -180,161 +212,288 @@ public class TelemetryService extends Service implements LocationListener { } } + /* Handle telemetry packet + */ + private void telemetry(AltosTelemetry telem) { + AltosState state; + + if (telemetry_state.states.containsKey(telem.serial)) + state = telemetry_state.states.get(telem.serial).clone(); + else + state = new AltosState(); + telem.update_state(state); + telemetry_state.states.put(telem.serial, state); + if (state != null) { + AltosPreferences.set_state(telem.serial, state, null); + } + send_to_clients(); + } + + /* Construct the message to deliver to clients + */ private Message message() { if (telemetry_state == null) - Log.d(TAG, "telemetry_state null!"); - if (telemetry_state.state == null) - Log.d(TAG, "telemetry_state.state null!"); + AltosDebug.debug("telemetry_state null!"); + if (telemetry_state.states == null) + AltosDebug.debug("telemetry_state.states null!"); return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state); } - private void sendMessageToClients() { - Message m = message(); - if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size())); - for (int i=mClients.size()-1; i>=0; i--) { - try { - if (D) Log.d(TAG, String.format("Send message to client %d", i)); - mClients.get(i).send(m); - } catch (RemoteException e) { - mClients.remove(i); - } + /* A new friend has connected + */ + private void add_client(Messenger client) { + + clients.add(client); + AltosDebug.debug("Client bound to service"); + + /* On connect, send the current state to the new client + */ + send_to_client(client, message()); + + /* If we've got an address from a previous session, then + * go ahead and try to reconnect to the device + */ + if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) { + AltosDebug.debug("Reconnecting now..."); + start_altos_bluetooth(address, false); } } - private void stopAltosBluetooth() { - if (D) Log.d(TAG, "stopAltosBluetooth(): begin"); - telemetry_state.connect = TelemetryState.CONNECT_READY; - if (mTelemetryReader != null) { - if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader"); - mTelemetryReader.interrupt(); + /* A client has disconnected, clean up + */ + private void remove_client(Messenger client) { + clients.remove(client); + AltosDebug.debug("Client unbound from service"); + + /* When the list of clients is empty, stop the service if + * we have no current telemetry source + */ + + if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) { + AltosDebug.debug("No clients, no connection. Stopping\n"); + stopSelf(); + } + } + + private void send_to_client(Messenger client, Message m) { + try { + client.send(m); + } catch (RemoteException e) { + AltosDebug.error("Client %s disappeared", client.toString()); + remove_client(client); + } + } + + private void send_to_clients() { + Message m = message(); + for (Messenger client : clients) + send_to_client(client, m); + } + + private void disconnect(boolean notify) { + AltosDebug.debug("disconnect(): begin"); + + telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; + telemetry_state.address = null; + + if (altos_link != null) + altos_link.closing(); + + stop_receiver_voltage_timer(); + + if (telemetry_reader != null) { + AltosDebug.debug("disconnect(): stopping TelemetryReader"); + telemetry_reader.interrupt(); try { - mTelemetryReader.join(); + telemetry_reader.join(); } catch (InterruptedException e) { } - mTelemetryReader = null; + telemetry_reader = null; } - if (mTelemetryLogger != null) { - if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryLogger"); - mTelemetryLogger.stop(); - mTelemetryLogger = null; + if (telemetry_logger != null) { + AltosDebug.debug("disconnect(): stopping TelemetryLogger"); + telemetry_logger.stop(); + telemetry_logger = null; } - if (mAltosBluetooth != null) { - if (D) Log.d(TAG, "stopAltosBluetooth(): stopping AltosBluetooth"); - mAltosBluetooth.close(); - mAltosBluetooth = null; + if (altos_link != null) { + AltosDebug.debug("disconnect(): stopping AltosDroidLink"); + altos_link.close(); + altos_link = null; } telemetry_state.config = null; - if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients"); - sendMessageToClients(); + if (notify) { + AltosDebug.debug("disconnect(): send message to clients"); + send_to_clients(); + if (clients.isEmpty()) { + AltosDebug.debug("disconnect(): no clients, terminating"); + stopSelf(); + } + } } - private void startAltosBluetooth(String address) { + private void start_usb(UsbDevice device) { + AltosUsb d = new AltosUsb(this, device, handler); + + if (d != null) { + disconnect(false); + altos_link = d; + try { + connected(); + } catch (InterruptedException ie) { + } + } + } + + private void delete_serial(int serial) { + telemetry_state.states.remove((Integer) serial); + AltosPreferences.remove_state(serial); + send_to_clients(); + } + + private void start_altos_bluetooth(DeviceAddress address, boolean pause) { // Get the BLuetoothDevice object - BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); + BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address); + disconnect(false); this.address = address; - if (mAltosBluetooth == null) { - if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress())); - mAltosBluetooth = new AltosBluetooth(device, mHandler); - telemetry_state.connect = TelemetryState.CONNECT_CONNECTING; - sendMessageToClients(); - } else { - // This is a bit of a hack - if it appears we're still connected, we treat this as a restart. - // So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message - // to ourselves in a few seconds time that will ultimately call this method again. - // ... then we tear down the existing connection. - // We do it this way around so that we don't lose a reference to the device when this method - // is called on reception of MSG_CONNECT_FAILED in the handler above. - mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, address), 3000); - stopAltosBluetooth(); + AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()); + altos_link = new AltosBluetooth(device, handler, pause); + telemetry_state.connect = TelemetryState.CONNECT_CONNECTING; + telemetry_state.address = address; + send_to_clients(); + } + + // Timer for receiver battery voltage monitoring + Timer receiver_voltage_timer; + + private void update_receiver_voltage() { + if (altos_link != null) { + try { + double voltage = altos_link.monitor_battery(); + telemetry_state.receiver_battery = voltage; + } catch (InterruptedException ie) { + } + } + } + + private void stop_receiver_voltage_timer() { + if (receiver_voltage_timer != null) { + receiver_voltage_timer.cancel(); + receiver_voltage_timer.purge(); + receiver_voltage_timer = null; + } + } + + private void start_receiver_voltage_timer() { + if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) { + receiver_voltage_timer = new Timer(); + receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L); } } private void connected() throws InterruptedException { - if (D) Log.d(TAG, "connected top"); + AltosDebug.debug("connected top"); + AltosDebug.check_ui("connected\n"); try { - if (mAltosBluetooth == null) + if (altos_link == null) throw new InterruptedException("no bluetooth"); - telemetry_state.config = mAltosBluetooth.config_data(); - mAltosBluetooth.set_radio_frequency(telemetry_state.frequency); - mAltosBluetooth.set_telemetry_rate(telemetry_state.telemetry_rate); + telemetry_state.config = altos_link.config_data(); + altos_link.set_radio_frequency(telemetry_state.frequency); + altos_link.set_telemetry_rate(telemetry_state.telemetry_rate); } catch (TimeoutException e) { // If this timed out, then we really want to retry it, but // probably safer to just retry the connection from scratch. - mHandler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget(); + AltosDebug.debug("connected timeout"); + if (address != null) { + AltosDebug.debug("connected timeout, retrying"); + start_altos_bluetooth(address, true); + } else { + handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget(); + disconnect(true); + } return; } - if (D) Log.d(TAG, "connected bluetooth configured"); + AltosDebug.debug("connected bluetooth configured"); telemetry_state.connect = TelemetryState.CONNECT_CONNECTED; + telemetry_state.address = address; - mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler, telemetry_state.state); - mTelemetryReader.start(); + telemetry_reader = new TelemetryReader(altos_link, handler); + telemetry_reader.start(); - if (D) Log.d(TAG, "connected TelemetryReader started"); + AltosDebug.debug("connected TelemetryReader started"); - mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth); + telemetry_logger = new TelemetryLogger(this, altos_link); - if (D) Log.d(TAG, "Notify UI of connection"); + start_receiver_voltage_timer(); - sendMessageToClients(); - } + AltosDebug.debug("Notify UI of connection"); - private void onTimerTick() { - if (D) Log.d(TAG, "Timer wakeup"); - try { - if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) { - stopSelf(); - } - } catch (Throwable t) { - Log.e(TAG, "Timer failed: ", t); - } + send_to_clients(); } @Override public void onCreate() { + + AltosDebug.init(this); + + // Initialise preferences + AltosDroidPreferences.init(this); + // Get local Bluetooth adapter - mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + bluetooth_adapter = BluetoothAdapter.getDefaultAdapter(); // If the adapter is null, then Bluetooth is not supported - if (mBluetoothAdapter == null) { + if (bluetooth_adapter == null) { Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show(); } - // Initialise preferences - AltosDroidPreferences.init(this); - telemetry_state = new TelemetryState(); // Create a reference to the NotificationManager so that we can update our notifcation text later //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - telemetry_state.connect = TelemetryState.CONNECT_READY; + telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED; + telemetry_state.address = null; - AltosSavedState saved_state = AltosPreferences.state(0); + /* Pull the saved state information out of the preferences database + */ + ArrayList<Integer> serials = AltosPreferences.list_states(); - if (saved_state != null) { - if (D) Log.d(TAG, String.format("recovered old state flight %d\n", saved_state.state.flight)); - telemetry_state.state = saved_state.state; - } + telemetry_state.latest_serial = AltosPreferences.latest_state(); - // Start our timer - first event in 10 seconds, then every 10 seconds after that. - timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L); + for (int serial : serials) { + AltosSavedState saved_state = AltosPreferences.state(serial); + if (saved_state != null) { + if (serial == 0) { + serial = saved_state.state.serial; + AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state); + AltosPreferences.remove_state(0); + } + if (telemetry_state.latest_serial == 0) + telemetry_state.latest_serial = serial; + + AltosDebug.debug("recovered old state serial %d flight %d\n", + serial, + saved_state.state.flight); + if (saved_state.state.gps != null) + AltosDebug.debug("\tposition %f,%f\n", + saved_state.state.gps.lat, + saved_state.state.gps.lon); + telemetry_state.states.put(serial, saved_state.state); + } + } // Listen for GPS and Network position updates LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this); - - String address = AltosDroidPreferences.active_device(); - if (address != null) - startAltosBluetooth(address); } @Override public int onStartCommand(Intent intent, int flags, int startId) { - Log.i("TelemetryService", "Received start id " + startId + ": " + intent); + AltosDebug.debug("Received start id %d: %s", startId, intent); CharSequence text = getText(R.string.telemetry_service_started); @@ -354,6 +513,20 @@ public class TelemetryService extends Service implements LocationListener { // Move us into the foreground. startForeground(NOTIFICATION, notification); + /* Start bluetooth if we don't have a connection already */ + if (intent != null && + (telemetry_state.connect == TelemetryState.CONNECT_NONE || + telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED)) + { + String action = intent.getAction(); + + if (action.equals(AltosDroid.ACTION_BLUETOOTH)) { + DeviceAddress address = AltosDroidPreferences.active_device(); + if (address != null && !address.address.startsWith("USB")) + start_altos_bluetooth(address, false); + } + } + // We want this service to continue running until it is explicitly // stopped, so return sticky. return START_STICKY; @@ -366,28 +539,25 @@ public class TelemetryService extends Service implements LocationListener { ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this); // Stop the bluetooth Comms threads - stopAltosBluetooth(); + disconnect(true); // Demote us from the foreground, and cancel the persistent notification. stopForeground(true); - // Stop our timer - if (timer != null) {timer.cancel();} - // Tell the user we stopped. Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show(); } @Override public IBinder onBind(Intent intent) { - return mMessenger.getBinder(); + return messenger.getBinder(); } public void onLocationChanged(Location location) { telemetry_state.location = location; - if (D) Log.d(TAG, "location changed"); - sendMessageToClients(); + AltosDebug.debug("location changed"); + send_to_clients(); } public void onStatusChanged(String provider, int status, Bundle extras) { diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java index 862847d2..f9191a32 100644 --- a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java +++ b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java @@ -17,29 +17,36 @@ 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); } diff --git a/altoslib/.gitignore b/altoslib/.gitignore index ff0fd710..dc8b7e5e 100644 --- a/altoslib/.gitignore +++ b/altoslib/.gitignore @@ -1,3 +1,4 @@ bin classaltoslib.stamp altoslib*.jar +AltosVersion.java diff --git a/altoslib/AltosAccel.java b/altoslib/AltosAccel.java index b11dc3a1..e4e7df90 100644 --- a/altoslib/AltosAccel.java +++ b/altoslib/AltosAccel.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosCRCException.java b/altoslib/AltosCRCException.java index 4167aecf..eaff0808 100644 --- a/altoslib/AltosCRCException.java +++ b/altoslib/AltosCRCException.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosCRCException extends Exception { public int rssi; diff --git a/altoslib/AltosCSV.java b/altoslib/AltosCSV.java index 02f7806f..edb23e69 100644 --- a/altoslib/AltosCSV.java +++ b/altoslib/AltosCSV.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosCompanion.java b/altoslib/AltosCompanion.java index 8992b018..86b23eb6 100644 --- a/altoslib/AltosCompanion.java +++ b/altoslib/AltosCompanion.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index d8f4d945..8e052934 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.*; import java.text.*; @@ -75,6 +75,7 @@ public class AltosConfigData implements Iterable<String> { /* HAS_APRS */ public int aprs_interval; public int aprs_ssid; + public int aprs_format; /* HAS_BEEP */ public int beep; @@ -270,6 +271,7 @@ public class AltosConfigData implements Iterable<String> { aprs_interval = -1; aprs_ssid = -1; + aprs_format = -1; beep = -1; @@ -370,6 +372,7 @@ public class AltosConfigData implements Iterable<String> { /* HAS_APRS */ try { aprs_interval = get_int(line, "APRS interval:"); } catch (Exception e) {} try { aprs_ssid = get_int(line, "APRS SSID:"); } catch (Exception e) {} + try { aprs_format = get_int(line, "APRS format:"); } catch (Exception e) {} /* HAS_BEEP */ try { beep = get_int(line, "Beeper setting:"); } catch (Exception e) {} @@ -518,6 +521,8 @@ public class AltosConfigData implements Iterable<String> { aprs_interval = source.aprs_interval(); if (aprs_ssid >= 0) aprs_ssid = source.aprs_ssid(); + if (aprs_format >= 0) + aprs_format = source.aprs_format(); /* HAS_BEEP */ if (beep >= 0) @@ -572,6 +577,7 @@ public class AltosConfigData implements Iterable<String> { dest.set_pyro_firing_time(pyro_firing_time); dest.set_aprs_interval(aprs_interval); dest.set_aprs_ssid(aprs_ssid); + dest.set_aprs_format(aprs_format); dest.set_beep(beep); dest.set_tracker_motion(tracker_motion); dest.set_tracker_interval(tracker_interval); @@ -674,6 +680,8 @@ public class AltosConfigData implements Iterable<String> { link.printf("c A %d\n", aprs_interval); if (aprs_ssid >= 0) link.printf("c S %d\n", aprs_ssid); + if (aprs_format >= 0) + link.printf("c C %d\n", aprs_format); /* HAS_BEEP */ if (beep >= 0) diff --git a/altoslib/AltosConfigDataException.java b/altoslib/AltosConfigDataException.java index 11aa4d24..da11336d 100644 --- a/altoslib/AltosConfigDataException.java +++ b/altoslib/AltosConfigDataException.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosConfigDataException extends Exception { diff --git a/altoslib/AltosConfigValues.java b/altoslib/AltosConfigValues.java index cfe9fc8b..f8a2fb14 100644 --- a/altoslib/AltosConfigValues.java +++ b/altoslib/AltosConfigValues.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosConfigValues { /* set and get all of the dialog values */ @@ -91,6 +91,10 @@ public interface AltosConfigValues { public abstract void set_aprs_ssid(int new_aprs_ssid); + public abstract int aprs_format() throws AltosConfigDataException; + + public abstract void set_aprs_format(int new_aprs_format); + public abstract int beep() throws AltosConfigDataException; public abstract void set_beep(int new_beep); diff --git a/altoslib/AltosConvert.java b/altoslib/AltosConvert.java index a5eb7ff8..fd2f5750 100644 --- a/altoslib/AltosConvert.java +++ b/altoslib/AltosConvert.java @@ -18,7 +18,7 @@ /* * Sensor data conversion functions */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosConvert { /* @@ -230,6 +230,12 @@ public class AltosConvert { return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0; } + static double tele_bt_3_battery(int raw) { + if (raw == AltosLib.MISSING) + return AltosLib.MISSING; + return 3.3 * mega_adc(raw) * (5.1 + 10.0) / 10.0; + } + static double easy_mini_voltage(int sensor, int serial) { double supply = 3.3; double diode_offset = 0.0; diff --git a/altoslib/AltosDebug.java b/altoslib/AltosDebug.java index ef5edc6a..16dcf6f5 100644 --- a/altoslib/AltosDebug.java +++ b/altoslib/AltosDebug.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosDistance.java b/altoslib/AltosDistance.java index b0666f33..a05fb7e2 100644 --- a/altoslib/AltosDistance.java +++ b/altoslib/AltosDistance.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosDistance extends AltosUnits { diff --git a/altoslib/AltosEeprom.java b/altoslib/AltosEeprom.java index 777988e7..194b10e8 100644 --- a/altoslib/AltosEeprom.java +++ b/altoslib/AltosEeprom.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromChunk.java b/altoslib/AltosEepromChunk.java index 19a8807d..9174c6a9 100644 --- a/altoslib/AltosEepromChunk.java +++ b/altoslib/AltosEepromChunk.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.util.concurrent.*; diff --git a/altoslib/AltosEepromDownload.java b/altoslib/AltosEepromDownload.java index 9598bd93..baaeb993 100644 --- a/altoslib/AltosEepromDownload.java +++ b/altoslib/AltosEepromDownload.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromFile.java b/altoslib/AltosEepromFile.java index c8443549..33d4f63b 100644 --- a/altoslib/AltosEepromFile.java +++ b/altoslib/AltosEepromFile.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromGPS.java b/altoslib/AltosEepromGPS.java index 96cfc0e1..aeb61661 100644 --- a/altoslib/AltosEepromGPS.java +++ b/altoslib/AltosEepromGPS.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromHeader.java b/altoslib/AltosEepromHeader.java index fc4e9caa..95ecc32c 100644 --- a/altoslib/AltosEepromHeader.java +++ b/altoslib/AltosEepromHeader.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromIterable.java b/altoslib/AltosEepromIterable.java index 94487ab5..c3cd5d86 100644 --- a/altoslib/AltosEepromIterable.java +++ b/altoslib/AltosEepromIterable.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromList.java b/altoslib/AltosEepromList.java index 39768a21..6dc6a3d6 100644 --- a/altoslib/AltosEepromList.java +++ b/altoslib/AltosEepromList.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromLog.java b/altoslib/AltosEepromLog.java index 0fc82ab4..538c102b 100644 --- a/altoslib/AltosEepromLog.java +++ b/altoslib/AltosEepromLog.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.util.concurrent.*; diff --git a/altoslib/AltosEepromMega.java b/altoslib/AltosEepromMega.java index c2edcf23..d5f0e0e0 100644 --- a/altoslib/AltosEepromMega.java +++ b/altoslib/AltosEepromMega.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromMetrum2.java b/altoslib/AltosEepromMetrum2.java index 39425e10..1b4cb04a 100644 --- a/altoslib/AltosEepromMetrum2.java +++ b/altoslib/AltosEepromMetrum2.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromMini.java b/altoslib/AltosEepromMini.java index ef13d0a7..8d0343b8 100644 --- a/altoslib/AltosEepromMini.java +++ b/altoslib/AltosEepromMini.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromMonitor.java b/altoslib/AltosEepromMonitor.java index 35ed5a6e..1c1d1010 100644 --- a/altoslib/AltosEepromMonitor.java +++ b/altoslib/AltosEepromMonitor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosEepromMonitor { diff --git a/altoslib/AltosEepromTM.java b/altoslib/AltosEepromTM.java index 2bdd64f0..44b98e49 100644 --- a/altoslib/AltosEepromTM.java +++ b/altoslib/AltosEepromTM.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosEepromTm.java b/altoslib/AltosEepromTm.java index 36500393..0a37a2aa 100644 --- a/altoslib/AltosEepromTm.java +++ b/altoslib/AltosEepromTm.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosFile.java b/altoslib/AltosFile.java index a79216a2..cab38d6d 100644 --- a/altoslib/AltosFile.java +++ b/altoslib/AltosFile.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.File; import java.util.*; diff --git a/altoslib/AltosFlash.java b/altoslib/AltosFlash.java index e58b652e..e4b980b0 100644 --- a/altoslib/AltosFlash.java +++ b/altoslib/AltosFlash.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosFlashListener.java b/altoslib/AltosFlashListener.java index 1c7cd77d..6a7351f3 100644 --- a/altoslib/AltosFlashListener.java +++ b/altoslib/AltosFlashListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosFlashListener { public void position(String label, int percent); diff --git a/altosuilib/AltosFlightDisplay.java b/altoslib/AltosFlightDisplay.java index ac65c49e..33d71fc3 100644 --- a/altosuilib/AltosFlightDisplay.java +++ b/altoslib/AltosFlightDisplay.java @@ -15,9 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; - -import org.altusmetrum.altoslib_6.*; +package org.altusmetrum.altoslib_8; public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener { void reset(); diff --git a/altoslib/AltosFlightReader.java b/altoslib/AltosFlightReader.java index 03c53ff4..fca0f20b 100644 --- a/altoslib/AltosFlightReader.java +++ b/altoslib/AltosFlightReader.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.io.*; diff --git a/altoslib/AltosFlightStats.java b/altoslib/AltosFlightStats.java index 82e477f8..b837ba84 100644 --- a/altoslib/AltosFlightStats.java +++ b/altoslib/AltosFlightStats.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altosuilib/AltosFontListener.java b/altoslib/AltosFontListener.java index 93625278..78049350 100644 --- a/altosuilib/AltosFontListener.java +++ b/altoslib/AltosFontListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; public interface AltosFontListener { void font_size_changed(int font_size); diff --git a/altoslib/AltosFrequency.java b/altoslib/AltosFrequency.java index 1dd4819d..6d2bb8d0 100644 --- a/altoslib/AltosFrequency.java +++ b/altoslib/AltosFrequency.java @@ -15,12 +15,25 @@ * 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); diff --git a/altoslib/AltosGPS.java b/altoslib/AltosGPS.java index 2139efb2..a2584e77 100644 --- a/altoslib/AltosGPS.java +++ b/altoslib/AltosGPS.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.util.concurrent.*; @@ -195,10 +195,10 @@ public class AltosGPS implements Cloneable, Serializable { lon = AltosParse.parse_coord(words[i++]); alt = AltosParse.parse_int(words[i++]); if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { - ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); + ground_speed = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(H)")); course = AltosParse.parse_int(words[i++]); - climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); - hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); + climb_rate = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(V)")); + hdop = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "(hdop)")); h_error = AltosParse.parse_int(words[i++]); v_error = AltosParse.parse_int(words[i++]); } diff --git a/altoslib/AltosGPSSat.java b/altoslib/AltosGPSSat.java index 57491f4d..44782003 100644 --- a/altoslib/AltosGPSSat.java +++ b/altoslib/AltosGPSSat.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosGPSSat { public int svid; diff --git a/altoslib/AltosGreatCircle.java b/altoslib/AltosGreatCircle.java index c48755be..e13eca48 100644 --- a/altoslib/AltosGreatCircle.java +++ b/altoslib/AltosGreatCircle.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.lang.Math; import java.io.*; @@ -28,8 +28,8 @@ public class AltosGreatCircle implements Cloneable, Serializable { double sqr(double a) { return a * a; } - static final double rad = Math.PI / 180; - static final double earth_radius = 6371.2 * 1000; /* in meters */ + public static final double rad = Math.PI / 180; + public static final double earth_radius = 6371.2 * 1000; /* in meters */ public static final int BEARING_LONG = AltosConvert.BEARING_LONG; public static final int BEARING_SHORT = AltosConvert.BEARING_SHORT; diff --git a/altoslib/AltosHeight.java b/altoslib/AltosHeight.java index c4419ae6..bc20ef24 100644 --- a/altoslib/AltosHeight.java +++ b/altoslib/AltosHeight.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosHeight extends AltosUnits { diff --git a/altoslib/AltosHexfile.java b/altoslib/AltosHexfile.java index 9f45d65a..ece822d8 100644 --- a/altoslib/AltosHexfile.java +++ b/altoslib/AltosHexfile.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.LinkedList; diff --git a/altoslib/AltosHexsym.java b/altoslib/AltosHexsym.java index 0c61fd02..e1561c7d 100644 --- a/altoslib/AltosHexsym.java +++ b/altoslib/AltosHexsym.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosHexsym { String name; diff --git a/altoslib/AltosIMU.java b/altoslib/AltosIMU.java index 8c219d9f..7a4a705d 100644 --- a/altoslib/AltosIMU.java +++ b/altoslib/AltosIMU.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.*; import java.io.*; diff --git a/altoslib/AltosIdle.java b/altoslib/AltosIdle.java index 82b18ca2..0e2576cd 100644 --- a/altoslib/AltosIdle.java +++ b/altoslib/AltosIdle.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosIdleFetch.java b/altoslib/AltosIdleFetch.java index 4c5e8285..6c0d130b 100644 --- a/altoslib/AltosIdleFetch.java +++ b/altoslib/AltosIdleFetch.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; @@ -146,6 +146,7 @@ public class AltosIdleFetch implements AltosStateUpdate { state.set_callsign(config_data.callsign); state.set_ground_accel(config_data.accel_cal_plus); state.set_accel_g(config_data.accel_cal_plus, config_data.accel_cal_minus); + state.set_product(config_data.product); for (AltosIdler idler : idlers) { if (idler.matches(config_data)) { idler.update_state(state, link, config_data); diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java index 4d0968bf..1b62f385 100644 --- a/altoslib/AltosIdleMonitor.java +++ b/altoslib/AltosIdleMonitor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.concurrent.*; diff --git a/altoslib/AltosIdleMonitorListener.java b/altoslib/AltosIdleMonitorListener.java index fdf4be9d..1d06c4d9 100644 --- a/altoslib/AltosIdleMonitorListener.java +++ b/altoslib/AltosIdleMonitorListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosIdleMonitorListener { public void update(AltosState state, AltosListenerState listener_state); diff --git a/altoslib/AltosIgnite.java b/altoslib/AltosIgnite.java index ffb6ed15..39f792db 100644 --- a/altoslib/AltosIgnite.java +++ b/altoslib/AltosIgnite.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.*; import java.io.*; diff --git a/altoslib/AltosImage.java b/altoslib/AltosImage.java new file mode 100644 index 00000000..fc0192a3 --- /dev/null +++ b/altoslib/AltosImage.java @@ -0,0 +1,25 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; + +public interface AltosImage { + /* Discard storage for image */ + public abstract void flush(); +} diff --git a/altoslib/AltosKML.java b/altoslib/AltosKML.java index aa80fc21..81433958 100644 --- a/altoslib/AltosKML.java +++ b/altoslib/AltosKML.java @@ -15,14 +15,25 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; +import java.util.*; + +class KMLWriter extends PrintWriter { + public PrintWriter printf(String format, Object ... arguments) { + return printf(Locale.ROOT, format, arguments); + } + + public KMLWriter(File name) throws FileNotFoundException { + super(name); + } +} public class AltosKML implements AltosWriter { File name; - PrintStream out; + PrintWriter out; int flight_state = -1; AltosState prev = null; double gps_start_altitude; @@ -137,6 +148,10 @@ public class AltosKML implements AltosWriter { end(); prev = null; } + if (out != null) { + out.close(); + out = null; + } } public void write(AltosState state) { @@ -177,7 +192,7 @@ public class AltosKML implements AltosWriter { public AltosKML(File in_name) throws FileNotFoundException { name = in_name; - out = new PrintStream(name); + out = new KMLWriter(name); } public AltosKML(String in_string) throws FileNotFoundException { diff --git a/altosuilib/AltosUILatLon.java b/altoslib/AltosLatLon.java index 72ff74d8..e438a787 100644 --- a/altosuilib/AltosUILatLon.java +++ b/altoslib/AltosLatLon.java @@ -15,29 +15,31 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -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 class AltosLatLon { public double lat; public double lon; - public boolean equals(AltosUILatLon other) { - if (other == null) + 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 AltosUILatLon(double lat, double lon) { + public String toString() { + return String.format("%f/%f", lat, lon); + } + + public AltosLatLon(double lat, double lon) { this.lat = lat; this.lon = lon; } diff --git a/altoslib/AltosLatitude.java b/altoslib/AltosLatitude.java index 191a46ee..7091ce9c 100644 --- a/altoslib/AltosLatitude.java +++ b/altoslib/AltosLatitude.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosLatitude extends AltosLocation { public String pos() { return "N"; } diff --git a/altoslib/AltosLaunchSite.java b/altoslib/AltosLaunchSite.java new file mode 100644 index 00000000..0fa9bbd4 --- /dev/null +++ b/altoslib/AltosLaunchSite.java @@ -0,0 +1,57 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.*; +import java.util.*; +import java.text.*; +import java.util.concurrent.*; + +public class AltosLaunchSite { + public String name; + public double latitude; + public double longitude; + + public String toString() { + return name; + } + + public AltosLaunchSite(String in_name, double in_latitude, double in_longitude) { + name = in_name; + latitude = in_latitude; + longitude = in_longitude; + } + + public AltosLaunchSite(String line) throws ParseException { + String[] elements = line.split(":"); + + if (elements.length < 3) + throw new ParseException(String.format("Invalid site line %s", line), 0); + + name = elements[0]; + + try { + latitude = AltosParse.parse_double_net(elements[1]); + longitude = AltosParse.parse_double_net(elements[2]); + } catch (ParseException pe) { + throw new ParseException(String.format("Invalid site line %s", line), 0); + } + } +} + diff --git a/altoslib/AltosLaunchSiteListener.java b/altoslib/AltosLaunchSiteListener.java new file mode 100644 index 00000000..f4658660 --- /dev/null +++ b/altoslib/AltosLaunchSiteListener.java @@ -0,0 +1,26 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.*; +import java.util.*; +import java.util.concurrent.*; + +public interface AltosLaunchSiteListener { + public abstract void notify_launch_sites(List<AltosLaunchSite> sites); +} diff --git a/altoslib/AltosLaunchSites.java b/altoslib/AltosLaunchSites.java new file mode 100644 index 00000000..fa4026ba --- /dev/null +++ b/altoslib/AltosLaunchSites.java @@ -0,0 +1,73 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.*; +import java.util.*; +import java.util.concurrent.*; +import java.net.*; +import java.text.*; + +public class AltosLaunchSites extends Thread { + URL url; + LinkedList<AltosLaunchSite> sites; + AltosLaunchSiteListener listener; + + void notify_complete() { + listener.notify_launch_sites(sites); + } + + void add(AltosLaunchSite site) { + sites.add(site); + } + + void add(String line) { + try { + add(new AltosLaunchSite(line)); + } catch (ParseException pe) { + System.out.printf("parse exception %s\n", pe.toString()); + } + } + + public void run() { + try { + url = new URL(AltosLib.launch_sites_url); + URLConnection uc = url.openConnection(); + + InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set); + BufferedReader in = new BufferedReader(in_stream); + + for (;;) { + String line = in.readLine(); + if (line == null) + break; + add(line); + } + } catch (Exception e) { + } finally { + notify_complete(); + } + } + + public AltosLaunchSites(AltosLaunchSiteListener listener) { + sites = new LinkedList<AltosLaunchSite>(); + this.listener = listener; + start(); + } +} diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java index b19f9f52..e82b1c73 100644 --- a/altoslib/AltosLib.java +++ b/altoslib/AltosLib.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.*; import java.io.*; @@ -190,6 +190,13 @@ public class AltosLib { 38400, 9600, 2400 }; + public static final int ao_aprs_format_compressed = 0; + public static final int ao_aprs_format_uncompressed = 1; + + public static final String[] ao_aprs_format_name = { + "Compressed", "Uncompressed" + }; + public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt"; // public static final String launch_sites_url = "file:///home/keithp/misc/text/altusmetrum/AltOS/launch-sites.txt"; diff --git a/altoslib/AltosLine.java b/altoslib/AltosLine.java index a65ba593..b18aa965 100644 --- a/altoslib/AltosLine.java +++ b/altoslib/AltosLine.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosLine { public String line; diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java index 01c37864..f8bf4702 100644 --- a/altoslib/AltosLink.java +++ b/altoslib/AltosLink.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.concurrent.*; @@ -86,10 +86,17 @@ public abstract class AltosLink implements Runnable { public boolean reply_abort; public int in_reply; + boolean cancel_enable = true; + + public void set_cancel_enable(boolean e) { + cancel_enable = e; + } boolean reply_timeout_shown = false; private boolean check_reply_timeout() { + if (!cancel_enable) + return false; if (!reply_timeout_shown) reply_timeout_shown = show_reply_timeout(); return reply_abort; @@ -354,7 +361,7 @@ public abstract class AltosLink implements Runnable { if (frequency == 0) return; if (has_frequency) - set_radio_freq((int) Math.floor (frequency * 1000)); + set_radio_freq((int) Math.floor (frequency * 1000 + 0.5)); else if (has_setting) set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal)); else @@ -546,7 +553,15 @@ public abstract class AltosLink implements Runnable { } if (monitor_batt == AltosLib.MISSING) return AltosLib.MISSING; - return AltosConvert.cc_battery_to_voltage(monitor_batt); + + double volts = AltosLib.MISSING; + if (config_data.product.startsWith("TeleBT-v3")) { + volts = AltosConvert.tele_bt_3_battery(monitor_batt); + } else { + volts = AltosConvert.cc_battery_to_voltage(monitor_batt); + } + + return volts; } public AltosLink() { diff --git a/altoslib/AltosListenerState.java b/altoslib/AltosListenerState.java index d7e18008..9d7922d5 100644 --- a/altoslib/AltosListenerState.java +++ b/altoslib/AltosListenerState.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosLocation.java b/altoslib/AltosLocation.java index b21014db..231cd94b 100644 --- a/altoslib/AltosLocation.java +++ b/altoslib/AltosLocation.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public abstract class AltosLocation extends AltosUnits { diff --git a/altoslib/AltosLog.java b/altoslib/AltosLog.java index 9241581a..b89bac38 100644 --- a/altoslib/AltosLog.java +++ b/altoslib/AltosLog.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.text.*; diff --git a/altoslib/AltosLongitude.java b/altoslib/AltosLongitude.java index ff4f0c2b..e3e53282 100644 --- a/altoslib/AltosLongitude.java +++ b/altoslib/AltosLongitude.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosLongitude extends AltosLocation { public String pos() { return "E"; } diff --git a/altoslib/AltosMag.java b/altoslib/AltosMag.java index f7595639..351125bf 100644 --- a/altoslib/AltosMag.java +++ b/altoslib/AltosMag.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.*; import java.io.*; diff --git a/altoslib/AltosMap.java b/altoslib/AltosMap.java new file mode 100644 index 00000000..8a3266c9 --- /dev/null +++ b/altoslib/AltosMap.java @@ -0,0 +1,485 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.*; +import java.util.*; +import java.util.concurrent.*; + +public class AltosMap implements AltosMapTileListener, AltosMapStoreListener { + + public static final int px_size = 512; + + public static final int maptype_hybrid = 0; + public static final int maptype_roadmap = 1; + public static final int maptype_satellite = 2; + public static final int maptype_terrain = 3; + public static final int maptype_default = maptype_hybrid; + + public static final int default_zoom = 15; + public static final int min_zoom = 3; + public static final int max_zoom = 21; + + public static final String[] maptype_names = { + "hybrid", + "roadmap", + "satellite", + "terrain" + }; + + public static final String[] maptype_labels = { + "Hybrid", + "Roadmap", + "Satellite", + "Terrain" + }; + + AltosMapInterface map_interface; + + AltosMapCache cache; + + public AltosMapCache cache() { return cache; } + + LinkedList<AltosMapMark> marks = new LinkedList<AltosMapMark>(); + + AltosMapPath path; + AltosMapLine line; + public AltosLatLon last_position; + + boolean have_boost = false; + boolean have_landed = false; + + ConcurrentHashMap<AltosPointInt,AltosMapTile> tiles = new ConcurrentHashMap<AltosPointInt,AltosMapTile>(); + int load_radius; + AltosLatLon load_centre = null; + AltosMapTileListener load_listener; + + int zoom = AltosMap.default_zoom; + int maptype = AltosMap.maptype_default; + + long user_input_time; + + /* Milliseconds to wait after user action before auto-scrolling + */ + static final long auto_scroll_delay = 20 * 1000; + + public AltosMapTransform transform; + AltosLatLon centre; + + public void reset() { + // nothing + } + + /* MapInterface wrapping functions */ + + public void repaint(int x, int y, int w, int h) { + map_interface.repaint(new AltosRectangle(x, y, w, h)); + } + + public void repaint(AltosMapRectangle damage, int pad) { + AltosRectangle r = transform.screen(damage); + repaint(r.x - pad, r.y - pad, r.width + pad * 2, r.height + pad * 2); + } + + public void repaint() { + map_interface.repaint(); + } + + public int width() { + return map_interface.width(); + } + + public int height() { + return map_interface.height(); + } + + public void debug(String format, Object ... arguments) { + map_interface.debug(format, arguments); + } + + static public AltosPointInt floor(AltosPointDouble point) { + return new AltosPointInt ((int) Math.floor(point.x / AltosMap.px_size) * AltosMap.px_size, + (int) Math.floor(point.y / AltosMap.px_size) * AltosMap.px_size); + } + + static public AltosPointInt ceil(AltosPointDouble point) { + return new AltosPointInt ((int) Math.ceil(point.x / AltosMap.px_size) * AltosMap.px_size, + (int) Math.ceil(point.y / AltosMap.px_size) * AltosMap.px_size); + } + + public void notice_user_input() { + user_input_time = System.currentTimeMillis(); + } + + public boolean recent_user_input() { + return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay; + } + + public boolean has_centre() { + return centre != null; + } + + public boolean far_from_centre(AltosLatLon lat_lon) { + + if (centre == null || transform == null) + return true; + + AltosPointDouble screen = transform.screen(lat_lon); + + int width = width(); + int dx = Math.abs ((int) (double) screen.x - width/2); + + if (dx > width / 4) + return true; + + int height = height(); + int dy = Math.abs ((int) (double) screen.y - height/2); + + if (dy > height / 4) + return true; + + return false; + } + + public void set_transform() { + if (centre != null) { + transform = new AltosMapTransform(width(), height(), zoom, centre); + repaint(); + } + } + + private void set_zoom_label() { + map_interface.set_zoom_label(String.format("Zoom %d", get_zoom() - default_zoom)); + } + + + public boolean set_zoom(int zoom) { + notice_user_input(); + if (AltosMap.min_zoom <= zoom && zoom <= AltosMap.max_zoom && zoom != this.zoom) { + this.zoom = zoom; + tiles.clear(); + set_transform(); + set_zoom_label(); + return true; + } + return false; + } + + public boolean set_zoom_centre(int zoom, AltosPointInt centre) { + AltosLatLon mouse_lat_lon = null; + boolean ret; + + if (transform != null) + mouse_lat_lon = transform.screen_lat_lon(centre); + + ret = set_zoom(zoom); + + if (ret && mouse_lat_lon != null) { + AltosPointDouble new_mouse = transform.screen(mouse_lat_lon); + + double dx = width()/2.0 - centre.x; + double dy = height()/2.0 - centre.y; + + AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointDouble(new_mouse.x + dx, new_mouse.y + dy)); + + centre(new_centre); + } + + return ret; + } + + public int get_zoom() { + return zoom; + } + + public boolean set_maptype(int maptype) { + if (maptype != this.maptype) { + this.maptype = maptype; + tiles.clear(); + repaint(); + return true; + } + return false; + } + + public void show(AltosState state, AltosListenerState listener_state) { + + /* If insufficient gps data, nothing to update + */ + AltosGPS gps = state.gps; + + if (gps == null) + return; + + if (!gps.locked && gps.nsat < 4) + return; + + switch (state.state) { + case AltosLib.ao_flight_boost: + if (!have_boost) { + add_mark(gps.lat, gps.lon, state.state); + have_boost = true; + } + break; + case AltosLib.ao_flight_landed: + if (!have_landed) { + add_mark(gps.lat, gps.lon, state.state); + have_landed = true; + } + break; + } + + if (path != null) { + AltosMapRectangle damage = path.add(gps.lat, gps.lon, state.state); + + if (damage != null) + repaint(damage, AltosMapPath.stroke_width); + } + + last_position = new AltosLatLon(gps.lat, gps.lon); + + maybe_centre(gps.lat, gps.lon); + } + + public void centre(AltosLatLon lat_lon) { + centre = lat_lon; + set_transform(); + } + + public void centre(double lat, double lon) { + centre(new AltosLatLon(lat, lon)); + } + + public void centre(AltosState state) { + if (!state.gps.locked && state.gps.nsat < 4) + return; + centre(state.gps.lat, state.gps.lon); + } + + public void maybe_centre(double lat, double lon) { + AltosLatLon lat_lon = new AltosLatLon(lat, lon); + if (centre == null || (!recent_user_input() && far_from_centre(lat_lon))) + centre(lat_lon); + } + + public void add_mark(double lat, double lon, int state) { + synchronized(marks) { + AltosMapMark mark = map_interface.new_mark(lat, lon, state); + if (mark != null) + marks.add(mark); + } + repaint(); + } + + public void clear_marks() { + synchronized(marks) { + marks.clear(); + } + } + + private void make_tiles() { + AltosPointInt upper_left; + AltosPointInt lower_right; + + if (load_centre != null) { + AltosPointInt centre = floor(transform.point(load_centre)); + + upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size, + centre.y - load_radius * AltosMap.px_size); + lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size, + centre.y + load_radius * AltosMap.px_size); + } else { + upper_left = floor(transform.screen_point(new AltosPointInt(0, 0))); + lower_right = floor(transform.screen_point(new AltosPointInt(width(), height()))); + } + for (AltosPointInt point : tiles.keySet()) { + if (point.x < upper_left.x || lower_right.x < point.x || + point.y < upper_left.y || lower_right.y < point.y) { + tiles.remove(point); + } + } + + cache.set_cache_size((width() / AltosMap.px_size + 2) * (height() / AltosMap.px_size + 2)); + + for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) { + for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) { + AltosPointInt point = new AltosPointInt(x, y); + + if (!tiles.containsKey(point)) { + AltosLatLon ul = transform.lat_lon(point); + AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2)); + AltosMapTile tile = map_interface.new_tile(this, ul, center, zoom, maptype, px_size); + tiles.put(point, tile); + } + } + } + } + + public void set_load_params(int new_zoom, int new_type, double lat, double lon, int radius, AltosMapTileListener listener) { + if (AltosMap.min_zoom <= new_zoom && new_zoom <= AltosMap.max_zoom) + zoom = new_zoom; + maptype = new_type; + load_centre = new AltosLatLon(lat, lon); + load_radius = radius; + load_listener = listener; + centre(lat, lon); + tiles.clear(); + make_tiles(); + for (AltosMapTile tile : tiles.values()) { + tile.add_store_listener(this); + if (tile.store_status() != AltosMapTile.loading) + listener.notify_tile(tile, tile.store_status()); + } + repaint(); + } + + public String getName() { + return "Map"; + } + + public void paint() { + if (centre != null) + make_tiles(); + + if (transform == null) + return; + + for (AltosMapTile tile : tiles.values()) + tile.paint(transform); + + synchronized(marks) { + for (AltosMapMark mark : marks) + mark.paint(transform); + } + + if (path != null) + path.paint(transform); + + if (line != null) + line.paint(transform); + } + + /* AltosMapTileListener methods */ + public synchronized void notify_tile(AltosMapTile tile, int status) { + for (AltosPointInt point : tiles.keySet()) { + if (tile == tiles.get(point)) { + AltosPointInt screen = transform.screen(point); + repaint(screen.x, screen.y, AltosMap.px_size, AltosMap.px_size); + } + } + } + + /* AltosMapStoreListener methods */ + public synchronized void notify_store(AltosMapStore store, int status) { + if (load_listener != null) { + for (AltosMapTile tile : tiles.values()) + if (store.equals(tile.store)) + load_listener.notify_tile(tile, status); + } + } + + /* UI elements */ + + AltosPointInt drag_start; + + boolean dragged; + + static final double drag_far = 20; + + private void drag(int x, int y) { + if (drag_start == null) + return; + + int dx = x - drag_start.x; + int dy = y - drag_start.y; + + double distance = Math.hypot(dx, dy); + + if (distance > drag_far) + dragged = true; + + if (transform == null) { + debug("Transform not set in drag\n"); + return; + } + + AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointInt(width() / 2 - dx, height() / 2 - dy)); + centre(new_centre); + drag_start = new AltosPointInt(x, y); + } + + private void drag_start(int x, int y) { + drag_start = new AltosPointInt(x, y); + dragged = false; + } + + private void drag_stop(int x, int y) { + if (!dragged) { + if (transform == null) { + debug("Transform not set in stop\n"); + return; + } + map_interface.select_object (transform.screen_lat_lon(new AltosPointInt(x,y))); + } + } + + private void line_start(int x, int y) { + if (line != null) { + line.pressed(new AltosPointInt(x, y), transform); + repaint(); + } + } + + private void line(int x, int y) { + if (line != null) { + line.dragged(new AltosPointInt(x, y), transform); + repaint(); + } + } + + public void touch_start(int x, int y, boolean is_drag) { + notice_user_input(); + if (is_drag) + drag_start(x, y); + else + line_start(x, y); + } + + public void touch_continue(int x, int y, boolean is_drag) { + notice_user_input(); + if (is_drag) + drag(x, y); + else + line(x, y); + } + + public void touch_stop(int x, int y, boolean is_drag) { + notice_user_input(); + if (is_drag) + drag_stop(x, y); + } + + public AltosMap(AltosMapInterface map_interface) { + this.map_interface = map_interface; + cache = new AltosMapCache(map_interface); + line = map_interface.new_line(); + path = map_interface.new_path(); + set_zoom_label(); + } +} diff --git a/altoslib/AltosMapCache.java b/altoslib/AltosMapCache.java new file mode 100644 index 00000000..f0ad433c --- /dev/null +++ b/altoslib/AltosMapCache.java @@ -0,0 +1,207 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.net.*; + +public class AltosMapCache implements AltosMapCacheListener { + + /* An entry in the MapCache */ + class MapCacheElement implements AltosMapStoreListener { + + AltosMapTile tile; /* Notify when image has been loaded */ + AltosImage image; + AltosMapStore store; + long used; + + class loader implements Runnable { + public void run() { + if (image != null) + tile.notify_image(image); + try { + image = map_interface.load_image(store.file); + } catch (Exception ex) { + } + if (image == null) + tile.set_status(AltosMapTile.failed); + else + tile.set_status(AltosMapTile.success); + tile.notify_image(image); + } + } + + private void load() { + loader l = new loader(); + Thread lt = new Thread(l); + lt.start(); + } + + public void flush() { + if (image != null) { + image.flush(); + image = null; + } + } + + public boolean has_map() { + return store.status() == AltosMapTile.success; + } + + public synchronized void notify_store(AltosMapStore store, int status) { + switch (status) { + case AltosMapTile.loading: + break; + case AltosMapTile.success: + load(); + break; + default: + tile.set_status(status); + tile.notify_image(null); + } + } + + public MapCacheElement(AltosMapTile tile, AltosMapStore store) throws IOException { + this.tile = tile; + this.image = null; + this.store = store; + this.used = 0; + + int status = store.status(); + switch (status) { + case AltosMapTile.loading: + store.add_listener(this); + break; + case AltosMapTile.success: + load(); + break; + default: + tile.set_status(status); + tile.notify_image(null); + break; + } + } + } + + int min_cache_size; /* configured minimum cache size */ + int cache_size; /* current cache size */ + int requested_cache_size; /* cache size computed by application */ + + private Object fetch_lock = new Object(); + private Object cache_lock = new Object(); + + AltosMapInterface map_interface; + + MapCacheElement[] elements = new MapCacheElement[cache_size]; + + long used; + + public void set_cache_size(int new_size) { + + requested_cache_size = new_size; + + if (new_size < min_cache_size) + new_size = min_cache_size; + + if (new_size == cache_size) + return; + + synchronized(cache_lock) { + MapCacheElement[] new_elements = new MapCacheElement[new_size]; + + for (int i = 0; i < cache_size; i++) { + if (i < new_size) + new_elements[i] = elements[i]; + else if (elements[i] != null) + elements[i].flush(); + } + elements = new_elements; + cache_size = new_size; + } + } + + public AltosImage get(AltosMapTile tile, AltosMapStore store, int width, int height) { + int oldest = -1; + long age = used; + + synchronized(cache_lock) { + MapCacheElement element = null; + for (int i = 0; i < cache_size; i++) { + element = elements[i]; + + if (element == null) { + oldest = i; + break; + } + if (store.equals(element.store)) { + element.used = used++; + return element.image; + } + if (element.used < age) { + oldest = i; + age = element.used; + } + } + + try { + element = new MapCacheElement(tile, store); + element.used = used++; + if (elements[oldest] != null) + elements[oldest].flush(); + + elements[oldest] = element; + + if (element.image == null) + tile.set_status(AltosMapTile.loading); + else + tile.set_status(AltosMapTile.success); + + return element.image; + } catch (IOException e) { + tile.set_status(AltosMapTile.failed); + return null; + } + } + } + + public void map_cache_changed(int map_cache) { + min_cache_size = map_cache; + + set_cache_size(requested_cache_size); + } + + public void dispose() { + AltosPreferences.unregister_map_cache_listener(this); + + for (int i = 0; i < cache_size; i++) { + MapCacheElement element = elements[i]; + + if (element != null) + element.flush(); + } + } + + public AltosMapCache(AltosMapInterface map_interface) { + this.map_interface = map_interface; + min_cache_size = AltosPreferences.map_cache(); + + set_cache_size(0); + + AltosPreferences.register_map_cache_listener(this); + } +} diff --git a/altosuilib/AltosUIMapCacheListener.java b/altoslib/AltosMapCacheListener.java index 1eec7b0a..dec181bc 100644 --- a/altosuilib/AltosUIMapCacheListener.java +++ b/altoslib/AltosMapCacheListener.java @@ -15,8 +15,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public interface AltosUIMapCacheListener { +public interface AltosMapCacheListener { public void map_cache_changed(int map_cache); } diff --git a/altoslib/AltosMapInterface.java b/altoslib/AltosMapInterface.java new file mode 100644 index 00000000..45398ecd --- /dev/null +++ b/altoslib/AltosMapInterface.java @@ -0,0 +1,47 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.net.*; + +public interface AltosMapInterface { + public abstract AltosMapPath new_path(); + + public abstract AltosMapLine new_line(); + + public abstract AltosImage load_image(File file) throws Exception; + + public abstract AltosMapMark new_mark(double lat, double lon, int state); + + public abstract AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size); + + public abstract int width(); + + public abstract int height(); + + public abstract void repaint(); + + public abstract void repaint(AltosRectangle damage); + + public abstract void set_zoom_label(String label); + + public abstract void debug(String format, Object ... arguments); + + public abstract void select_object(AltosLatLon latlon); +} diff --git a/altoslib/AltosMapLine.java b/altoslib/AltosMapLine.java new file mode 100644 index 00000000..09ecb0c9 --- /dev/null +++ b/altoslib/AltosMapLine.java @@ -0,0 +1,81 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.Math; +import java.util.*; +import java.util.concurrent.*; + +public abstract class AltosMapLine { + public AltosLatLon start, end; + + static public int stroke_width = 6; + + public abstract void paint(AltosMapTransform t); + + private AltosLatLon lat_lon(AltosPointInt pt, AltosMapTransform t) { + return t.screen_lat_lon(pt); + } + + public void dragged(AltosPointInt pt, AltosMapTransform t) { + end = lat_lon(pt, t); + } + + public void pressed(AltosPointInt pt, AltosMapTransform t) { + start = lat_lon(pt, t); + end = null; + } + + public String line_dist() { + String format; + AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon, + end.lat, end.lon); + double distance = g.distance; + + if (AltosConvert.imperial_units) { + distance = AltosConvert.meters_to_feet(distance); + if (distance < 10000) { + format = "%4.0fft"; + } else { + distance /= 5280; + if (distance < 10) + format = "%5.3fmi"; + else if (distance < 100) + format = "%5.2fmi"; + else if (distance < 1000) + format = "%5.1fmi"; + else + format = "%5.0fmi"; + } + } else { + if (distance < 10000) { + format = "%4.0fm"; + } else { + distance /= 1000; + if (distance < 100) + format = "%5.2fkm"; + else if (distance < 1000) + format = "%5.1fkm"; + else + format = "%5.0fkm"; + } + } + return String.format(format, distance); + } +} diff --git a/altoslib/AltosMapLoader.java b/altoslib/AltosMapLoader.java new file mode 100644 index 00000000..8573489a --- /dev/null +++ b/altoslib/AltosMapLoader.java @@ -0,0 +1,205 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.util.*; +import java.text.*; +import java.lang.Math; +import java.net.URL; +import java.net.URLConnection; + +public class AltosMapLoader implements AltosMapTileListener, AltosMapStoreListener { + AltosMapLoaderListener listener; + + double latitude, longitude; + int min_z; + int max_z; + int cur_z; + int all_types; + int cur_type; + double radius; + + int tiles_loaded_layer; + int tiles_loaded_total; + int tiles_this_layer; + int tiles_total; + int layers_total; + int layers_loaded; + + AltosMap map; + + int tile_radius(int zoom) { + double delta_lon = AltosMapTransform.lon_from_distance(latitude, radius); + + AltosMapTransform t = new AltosMapTransform(256, 256, zoom + AltosMap.default_zoom, new AltosLatLon(latitude, longitude)); + + AltosPointDouble center = t.point(new AltosLatLon(latitude, longitude)); + AltosPointDouble edge = t.point(new AltosLatLon(latitude, longitude + delta_lon)); + + int tile_radius = (int) Math.ceil(Math.abs(center.x - edge.x) / AltosMap.px_size); + + return tile_radius; + } + + int tiles_per_layer(int zoom) { + int tile_radius = tile_radius(zoom); + return (tile_radius * 2 + 1) * (tile_radius * 2 + 1); + } + + public void do_load() { + tiles_this_layer = tiles_per_layer(cur_z); + tiles_loaded_layer = 0; + listener.debug("tiles_this_layer %d (zoom %d)\n", tiles_this_layer, cur_z); + + int load_radius = tile_radius(cur_z); + int zoom = cur_z + AltosMap.default_zoom; + int maptype = cur_type; + AltosLatLon load_centre = new AltosLatLon(latitude, longitude); + AltosMapTransform transform = new AltosMapTransform(256, 256, zoom, load_centre); + + map.centre(load_centre); + + AltosPointInt upper_left; + AltosPointInt lower_right; + + AltosPointInt centre = AltosMap.floor(transform.point(load_centre)); + + upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size, + centre.y - load_radius * AltosMap.px_size); + lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size, + centre.y + load_radius * AltosMap.px_size); + + + for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) { + for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) { + listener.debug("Make tile at %d, %d\n", x, y); + AltosPointInt point = new AltosPointInt(x, y); + AltosLatLon ul = transform.lat_lon(point); + AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2)); + AltosMapTile tile = map.map_interface.new_tile(this, ul, center, zoom, maptype, AltosMap.px_size); + tile.add_store_listener(this); + if (tile.store_status() != AltosMapTile.loading) + notify_tile(tile, tile.store_status()); + } + } + } + + public int next_type(int start) { + int next_type; + for (next_type = start; + next_type <= AltosMap.maptype_terrain && (all_types & (1 << next_type)) == 0; + next_type++) + ; + return next_type; + } + + public void next_load() { + int next_type = next_type(cur_type + 1); + + if (next_type > AltosMap.maptype_terrain) { + if (cur_z == max_z) { + return; + } else { + cur_z++; + } + next_type = next_type(0); + } + cur_type = next_type; + do_load(); + } + + private void start_load() { + + cur_z = min_z; + int ntype = 0; + + for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++) + if ((all_types & (1 << t)) != 0) + ntype++; + if (ntype == 0) { + all_types = (1 << AltosMap.maptype_hybrid); + ntype = 1; + } + + cur_type = next_type(0); + + for (int z = min_z; z <= max_z; z++) + tiles_total += tiles_per_layer(z); + + layers_total = (max_z - min_z + 1) * ntype; + layers_loaded = 0; + tiles_loaded_total = 0; + + listener.debug("total tiles %d\n", tiles_total); + + listener.loader_start(tiles_total); + do_load(); + } + + public void load(double latitude, double longitude, int min_z, int max_z, double radius, int all_types) { + listener.debug("lat %f lon %f min_z %d max_z %d radius %f all_types %d\n", + latitude, longitude, min_z, max_z, radius, all_types); + this.latitude = latitude; + this.longitude = longitude; + this.min_z = min_z; + this.max_z = max_z; + this.radius = radius; + this.all_types = all_types; + start_load(); + } + + public synchronized void notify_store(AltosMapStore store, int status) { + boolean do_next = false; + if (status == AltosMapTile.loading) + return; + + if (layers_loaded >= layers_total) + return; + + ++tiles_loaded_total; + ++tiles_loaded_layer; + listener.debug("total %d layer %d\n", tiles_loaded_total, tiles_loaded_layer); + + if (tiles_loaded_layer == tiles_this_layer) { + ++layers_loaded; + listener.debug("%d layers loaded\n", layers_loaded); + if (layers_loaded == layers_total) { + listener.loader_done(tiles_total); + return; + } else { + do_next = true; + } + } + listener.loader_notify(tiles_loaded_total, + tiles_total, store.file.toString()); + if (do_next) + next_load(); + } + + public synchronized void notify_tile(AltosMapTile tile, int status) { + notify_store(tile.store, status); + } + + public AltosMapCache cache() { return map.cache(); } + + public AltosMapLoader(AltosMap map, AltosMapLoaderListener listener) { + this.map = map; + this.listener = listener; + } +} diff --git a/altoslib/AltosMapLoaderListener.java b/altoslib/AltosMapLoaderListener.java new file mode 100644 index 00000000..0b41d9f4 --- /dev/null +++ b/altoslib/AltosMapLoaderListener.java @@ -0,0 +1,28 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +public interface AltosMapLoaderListener { + public abstract void loader_start(int max); + + public abstract void loader_notify(int cur, int max, String name); + + public abstract void loader_done(int max); + + public abstract void debug(String format, Object ... arguments); +} diff --git a/altoslib/AltosMapMark.java b/altoslib/AltosMapMark.java new file mode 100644 index 00000000..cc28f438 --- /dev/null +++ b/altoslib/AltosMapMark.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.Math; +import java.util.*; +import java.util.concurrent.*; + +public abstract class AltosMapMark { + + public AltosLatLon lat_lon; + public int state; + + static public int stroke_width = 6; + + public abstract void paint(AltosMapTransform t); + + public AltosMapMark (double lat, double lon, int state) { + lat_lon = new AltosLatLon(lat, lon); + this.state = state; + } +} diff --git a/altoslib/AltosMapPath.java b/altoslib/AltosMapPath.java new file mode 100644 index 00000000..6967f479 --- /dev/null +++ b/altoslib/AltosMapPath.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.Math; +import java.util.*; +import java.util.concurrent.*; + +public abstract class AltosMapPath { + + public LinkedList<AltosMapPathPoint> points = new LinkedList<AltosMapPathPoint>(); + public AltosMapPathPoint last_point = null; + + static public int stroke_width = 6; + + public abstract void paint(AltosMapTransform t); + + public AltosMapRectangle add(double lat, double lon, int state) { + AltosMapPathPoint point = new AltosMapPathPoint(new AltosLatLon (lat, lon), state); + AltosMapRectangle rect = null; + + if (!point.equals(last_point)) { + if (last_point != null) + rect = new AltosMapRectangle(last_point.lat_lon, point.lat_lon); + points.add (point); + last_point = point; + } + return rect; + } + + public void clear () { + points = new LinkedList<AltosMapPathPoint>(); + } +} diff --git a/altoslib/AltosMapPathPoint.java b/altoslib/AltosMapPathPoint.java new file mode 100644 index 00000000..0af98997 --- /dev/null +++ b/altoslib/AltosMapPathPoint.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.Math; +import java.util.*; +import java.util.concurrent.*; + +public class AltosMapPathPoint { + public AltosLatLon lat_lon; + public int state; + + public int hashCode() { + return lat_lon.hashCode() ^ state; + } + + public boolean equals(Object o) { + if (o == null) + return false; + + if (!(o instanceof AltosMapPathPoint)) + return false; + + AltosMapPathPoint other = (AltosMapPathPoint) o; + + return lat_lon.equals(other.lat_lon) && state == other.state; + } + + public AltosMapPathPoint(AltosLatLon lat_lon, int state) { + this.lat_lon = lat_lon; + this.state = state; + } +} + diff --git a/altosuilib/AltosUIMapRectangle.java b/altoslib/AltosMapRectangle.java index dc0e4cc1..9fdab1a0 100644 --- a/altosuilib/AltosUIMapRectangle.java +++ b/altoslib/AltosMapRectangle.java @@ -15,12 +15,12 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public class AltosUIMapRectangle { - AltosUILatLon ul, lr; +public class AltosMapRectangle { + AltosLatLon ul, lr; - public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) { + public AltosMapRectangle(AltosLatLon a, AltosLatLon b) { double ul_lat, ul_lon; double lr_lat, lr_lon; @@ -39,7 +39,7 @@ public class AltosUIMapRectangle { lr_lon = a.lon; } - ul = new AltosUILatLon(ul_lat, ul_lon); - lr = new AltosUILatLon(lr_lat, lr_lon); + ul = new AltosLatLon(ul_lat, ul_lon); + lr = new AltosLatLon(lr_lat, lr_lon); } } diff --git a/altosuilib/AltosUIMapStore.java b/altoslib/AltosMapStore.java index 70bb6fed..b24fabc8 100644 --- a/altosuilib/AltosUIMapStore.java +++ b/altoslib/AltosMapStore.java @@ -15,22 +15,16 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.net.*; import java.util.*; -public class AltosUIMapStore { +public class AltosMapStore { 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; + public File file; + LinkedList<AltosMapStoreListener> listeners = new LinkedList<AltosMapStoreListener>(); int status; @@ -38,18 +32,18 @@ public class AltosUIMapStore { return status; } - public synchronized void add_listener(AltosUIMapStoreListener listener) { + public synchronized void add_listener(AltosMapStoreListener listener) { if (!listeners.contains(listener)) listeners.add(listener); } - public synchronized void remove_listener(AltosUIMapStoreListener listener) { + public synchronized void remove_listener(AltosMapStoreListener listener) { listeners.remove(listener); } private synchronized void notify_listeners(int status) { this.status = status; - for (AltosUIMapStoreListener listener : listeners) + for (AltosMapStoreListener listener : listeners) listener.notify_store(this, status); } @@ -63,7 +57,7 @@ public class AltosUIMapStore { try { u = new URL(url); } catch (java.net.MalformedURLException e) { - return bad_request; + return AltosMapTile.bad_request; } byte[] data; @@ -81,7 +75,7 @@ public class AltosUIMapStore { synchronized (forbidden_lock) { forbidden_time = System.nanoTime(); forbidden_set = true; - return forbidden; + return AltosMapTile.forbidden; } } } @@ -98,10 +92,10 @@ public class AltosUIMapStore { in.close(); if (offset != contentLength) - return failed; + return AltosMapTile.failed; } catch (IOException e) { - return failed; + return AltosMapTile.failed; } try { @@ -110,13 +104,13 @@ public class AltosUIMapStore { out.flush(); out.close(); } catch (FileNotFoundException e) { - return bad_request; + return AltosMapTile.bad_request; } catch (IOException e) { if (file.exists()) file.delete(); - return bad_request; + return AltosMapTile.bad_request; } - return success; + return AltosMapTile.success; } static Object fetch_lock = new Object(); @@ -124,80 +118,126 @@ public class AltosUIMapStore { 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() { - if (file.exists()) { - notify_listeners(success); - return; - } - - synchronized(forbidden_lock) { - if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { - notify_listeners(forbidden); + try { + if (file.exists()) { + notify_listeners(AltosMapTile.success); return; } - } - int new_status; + synchronized(forbidden_lock) { + if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) { + notify_listeners(AltosMapTile.forbidden); + return; + } + } - 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(); + 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(); } - } else { - new_status = fetch_url(); + notify_listeners(new_status); + } finally { + finish_loader(); } - notify_listeners(new_status); } } + private Thread make_loader_thread() { + return new Thread(new loader()); + } + private void load() { - loader l = new loader(); - Thread lt = new Thread(l); - lt.start(); + add_loader(); } - private AltosUIMapStore (String url, File file) { + private AltosMapStore (String url, File file) { this.url = url; this.file = file; if (file.exists()) - status = success; + status = AltosMapTile.success; else { - status = loading; + status = AltosMapTile.loading; load(); } } - public boolean equals(AltosUIMapStore other) { + 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,AltosUIMapStore> stores = new HashMap<String,AltosUIMapStore>(); + static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>(); - public static AltosUIMapStore get(String url, File file) { - AltosUIMapStore store; + public static AltosMapStore get(String url, File file) { + AltosMapStore store; synchronized(stores) { if (stores.containsKey(url)) { store = stores.get(url); } else { - store = new AltosUIMapStore(url, file); + store = new AltosMapStore(url, file); stores.put(url, store); } } return store; } - } diff --git a/altosuilib/AltosUIMapStoreListener.java b/altoslib/AltosMapStoreListener.java index ccda8983..cef4f926 100644 --- a/altosuilib/AltosUIMapStoreListener.java +++ b/altoslib/AltosMapStoreListener.java @@ -15,8 +15,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public interface AltosUIMapStoreListener { - abstract void notify_store(AltosUIMapStore store, int status); +public interface AltosMapStoreListener { + abstract void notify_store(AltosMapStore store, int status); } diff --git a/altoslib/AltosMapTile.java b/altoslib/AltosMapTile.java new file mode 100644 index 00000000..a6ba5da5 --- /dev/null +++ b/altoslib/AltosMapTile.java @@ -0,0 +1,128 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.util.*; + +public abstract class AltosMapTile implements AltosFontListener { + AltosMapTileListener listener; + public AltosLatLon upper_left, center; + public int px_size; + int zoom; + int maptype; + int scale; + public AltosMapStore store; + public AltosMapCache cache; + public int status; + + static public final int success = 0; + static public final int loading = 1; + static public final int failed = 2; + static public final int bad_request = 3; + static public final int forbidden = 4; + + private File map_file() { + double lat = center.lat; + double lon = center.lon; + char chlat = lat < 0 ? 'S' : 'N'; + char chlon = lon < 0 ? 'W' : 'E'; + + if (lat < 0) lat = -lat; + if (lon < 0) lon = -lon; + String maptype_string = String.format("%s-", AltosMap.maptype_names[maptype]); + String format_string; + if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain) + format_string = "jpg"; + else + format_string = "png"; + return new File(AltosPreferences.mapdir(), + String.format("map-%c%.6f,%c%.6f-%s%d%s.%s", + chlat, lat, chlon, lon, maptype_string, zoom, scale == 1 ? "" : String.format("-%d", scale), format_string)); + } + + private String map_url() { + String format_string; + int z = zoom; + + if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain) + format_string = "jpg"; + else + format_string = "png32"; + + for (int s = 1; s < scale; s <<= 1) + z--; + + if (AltosVersion.has_google_maps_api_key()) + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s&key=%s", + center.lat, center.lon, z, px_size/scale, px_size/scale, scale, AltosMap.maptype_names[maptype], format_string, AltosVersion.google_maps_api_key); + else + return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s", + center.lat, center.lon, z, px_size/scale, px_size/scale, AltosMap.maptype_names[maptype], format_string); + } + + public void font_size_changed(int font_size) { + } + + public void set_status(int status) { + this.status = status; + listener.notify_tile(this, status); + } + + public void notify_image(AltosImage image) { + listener.notify_tile(this, status); + } + + public int store_status() { + return store.status(); + } + + public void add_store_listener(AltosMapStoreListener listener) { + store.add_listener(listener); + } + + public void remove_store_listener(AltosMapStoreListener listener) { + store.remove_listener(listener); + } + + public abstract void paint(AltosMapTransform t); + + public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) { + this.listener = listener; + this.upper_left = upper_left; + this.cache = listener.cache(); + + while (center.lon < -180.0) + center.lon += 360.0; + while (center.lon > 180.0) + center.lon -= 360.0; + + this.center = center; + this.zoom = zoom; + this.maptype = maptype; + this.px_size = px_size; + this.scale = scale; + + status = AltosMapTile.loading; + store = AltosMapStore.get(map_url(), map_file()); + } + + public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + this(listener, upper_left, center, zoom, maptype, px_size, 1); + } +} diff --git a/altosuilib/AltosUIMapTileListener.java b/altoslib/AltosMapTileListener.java index dace5b76..c02483bb 100644 --- a/altosuilib/AltosUIMapTileListener.java +++ b/altoslib/AltosMapTileListener.java @@ -15,10 +15,10 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public interface AltosUIMapTileListener { - abstract public void notify_tile(AltosUIMapTile tile, int status); +public interface AltosMapTileListener { + abstract public void notify_tile(AltosMapTile tile, int status); - abstract public AltosUIMapCache cache(); + abstract public AltosMapCache cache(); } diff --git a/altoslib/AltosMapTransform.java b/altoslib/AltosMapTransform.java new file mode 100644 index 00000000..b8f29107 --- /dev/null +++ b/altoslib/AltosMapTransform.java @@ -0,0 +1,128 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import java.io.*; +import java.lang.Math; +import java.util.*; +import java.util.concurrent.*; + +public class AltosMapTransform { + + double scale_x, scale_y; + + double offset_x, offset_y; + + public AltosLatLon lat_lon (AltosPointDouble point) { + double lat, lon; + double rads; + + lon = point.x/scale_x; + rads = 2 * Math.atan(Math.exp(-point.y/scale_y)); + lat = Math.toDegrees(rads - Math.PI/2); + + return new AltosLatLon(lat,lon); + } + + public AltosLatLon lat_lon (AltosPointInt point) { + return lat_lon(new AltosPointDouble(point.x, point.y)); + } + + public AltosPointDouble screen_point(AltosPointInt screen) { + return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y); + } + + public AltosPointDouble screen_point(AltosPointDouble screen) { + return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y); + } + + public double hypot(AltosLatLon a, AltosLatLon b) { + AltosPointDouble a_pt = point(a); + AltosPointDouble b_pt = point(b); + + return Math.hypot(a_pt.x - b_pt.x, a_pt.y - b_pt.y); + } + + public AltosLatLon screen_lat_lon(AltosPointInt screen) { + return lat_lon(screen_point(screen)); + } + + public AltosLatLon screen_lat_lon(AltosPointDouble screen) { + return lat_lon(screen_point(screen)); + } + + public AltosPointDouble point(AltosLatLon lat_lon) { + double x, y; + double e; + + x = lat_lon.lon * scale_x; + + e = Math.sin(Math.toRadians(lat_lon.lat)); + e = Math.max(e,-(1-1.0E-15)); + e = Math.min(e, 1-1.0E-15 ); + + y = 0.5*Math.log((1+e)/(1-e))*-scale_y; + + return new AltosPointDouble(x, y); + } + + public AltosPointDouble screen(AltosPointDouble point) { + return new AltosPointDouble(point.x - offset_x, point.y - offset_y); + } + + public AltosPointInt screen(AltosPointInt point) { + return new AltosPointInt((int) (point.x - offset_x + 0.5), + (int) (point.y - offset_y + 0.5)); + } + + public AltosRectangle screen(AltosMapRectangle map_rect) { + AltosPointDouble ul = screen(map_rect.ul); + AltosPointDouble lr = screen(map_rect.lr); + + return new AltosRectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y)); + } + + public AltosPointDouble screen(AltosLatLon lat_lon) { + return screen(point(lat_lon)); + } + + private boolean has_location; + + public boolean has_location() { + return has_location; + } + + public AltosMapTransform(int width, int height, int zoom, AltosLatLon centre_lat_lon) { + scale_x = 256/360.0 * Math.pow(2, zoom); + scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); + + AltosPointDouble centre_pt = point(centre_lat_lon); + + has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0); + offset_x = centre_pt.x - width / 2.0; + offset_y = centre_pt.y - height / 2.0; + } + + public static double lon_from_distance(double lat, double distance) { + double c = AltosGreatCircle.earth_radius * Math.cos(lat * Math.PI / 180) * 2 * Math.PI; + + if (c < 10) + return 0; + return distance/c * 360.0; + } +} diff --git a/altosuilib/AltosUIMapZoomListener.java b/altoslib/AltosMapZoomListener.java index 9f74baca..aa067303 100644 --- a/altosuilib/AltosUIMapZoomListener.java +++ b/altoslib/AltosMapZoomListener.java @@ -15,8 +15,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public interface AltosUIMapZoomListener { +public interface AltosMapZoomListener { abstract public void zoom_changed(int zoom); } diff --git a/altoslib/AltosMma655x.java b/altoslib/AltosMma655x.java index c0b94b8c..8b1ac088 100644 --- a/altoslib/AltosMma655x.java +++ b/altoslib/AltosMma655x.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.*; diff --git a/altoslib/AltosMs5607.java b/altoslib/AltosMs5607.java index 2bd4ba8f..e7ca1ba3 100644 --- a/altoslib/AltosMs5607.java +++ b/altoslib/AltosMs5607.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.*; import java.io.*; diff --git a/altoslib/AltosNoSymbol.java b/altoslib/AltosNoSymbol.java index 77410a25..6ef2a142 100644 --- a/altoslib/AltosNoSymbol.java +++ b/altoslib/AltosNoSymbol.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosNoSymbol extends Exception { public AltosNoSymbol(String name) { diff --git a/altoslib/AltosOrient.java b/altoslib/AltosOrient.java index 8cdde750..1c2e2aab 100644 --- a/altoslib/AltosOrient.java +++ b/altoslib/AltosOrient.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosOrient extends AltosUnits { diff --git a/altoslib/AltosParse.java b/altoslib/AltosParse.java index 2fb69c15..8f624b96 100644 --- a/altoslib/AltosParse.java +++ b/altoslib/AltosParse.java @@ -15,8 +15,9 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; +import java.util.*; import java.text.*; public class AltosParse { @@ -40,11 +41,23 @@ public class AltosParse { } } - public static double parse_double(String v) throws ParseException { + static NumberFormat nf_locale = NumberFormat.getInstance(); + + static NumberFormat nf_net = NumberFormat.getInstance(Locale.ROOT); + + public static double parse_double_locale(String str) throws ParseException { try { - return Double.parseDouble(v); - } catch (NumberFormatException e) { - throw new ParseException("error parsing double " + v, 0); + return nf_locale.parse(str.trim()).doubleValue(); + } catch (ParseException pe) { + throw new ParseException("error parsing double " + str, 0); + } + } + + public static double parse_double_net(String str) throws ParseException { + try { + return nf_net.parse(str.trim()).doubleValue(); + } catch (ParseException pe) { + throw new ParseException("error parsing double " + str, 0); } } diff --git a/altoslib/AltosPointDouble.java b/altoslib/AltosPointDouble.java new file mode 100644 index 00000000..ecfcbea7 --- /dev/null +++ b/altoslib/AltosPointDouble.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +public class AltosPointDouble { + public double x, y; + + public int hashCode() { + return new Double(x).hashCode() ^ new Double(y).hashCode(); + } + + public boolean equals(Object o) { + if (o == null) + return false; + + if (!(o instanceof AltosPointDouble)) + return false; + + AltosPointDouble n = (AltosPointDouble) o; + + return n.x == x && n.y == y; + } + + public AltosPointDouble(double x, double y) { + this.x = x; + this.y = y; + } + + public AltosPointDouble(int x, int y) { + this.x = x; + this.y = y; + } + + public AltosPointDouble(AltosPointInt p) { + this.x = p.x; + this.y = p.y; + } +} diff --git a/altoslib/AltosPointInt.java b/altoslib/AltosPointInt.java new file mode 100644 index 00000000..e690aa43 --- /dev/null +++ b/altoslib/AltosPointInt.java @@ -0,0 +1,53 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +public class AltosPointInt { + public int x, y; + + public int hashCode() { + return x ^ y; + } + + public boolean equals(Object o) { + if (o == null) + return false; + + if (!(o instanceof AltosPointInt)) + return false; + + AltosPointInt n = (AltosPointInt) o; + + return n.x == x && n.y == y; + } + + public AltosPointInt(int x, int y) { + this.x = x; + this.y = y; + } + + public AltosPointInt(double x, double y) { + this.x = (int) (x + 0.5); + this.y = (int) (y + 0.5); + } + + public AltosPointInt(AltosPointDouble pt_d) { + this.x = (int) (pt_d.x + 0.5); + this.y = (int) (pt_d.y + 0.5); + } +} diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java index 5aa45d3f..a1e76834 100644 --- a/altoslib/AltosPreferences.java +++ b/altoslib/AltosPreferences.java @@ -15,10 +15,11 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; +import java.text.*; public class AltosPreferences { public static AltosPreferencesBackend backend = null; @@ -42,7 +43,9 @@ public class AltosPreferences { public final static String logfilePreferenceFormat = "LOGFILE-%d"; /* state preference name */ + public final static String statePreferenceHead = "STATE-"; public final static String statePreferenceFormat = "STATE-%d"; + public final static String statePreferenceLatest = "STATE-LATEST"; /* voice preference name */ public final static String voicePreference = "VOICE"; @@ -118,6 +121,13 @@ public class AltosPreferences { public final static String unitsPreference = "IMPERIAL-UNITS"; + /* Maps cache size preference name */ + final static String mapCachePreference = "MAP-CACHE"; + + static LinkedList<AltosMapCacheListener> map_cache_listeners; + + public static int map_cache = 9; + public static AltosFrequency[] load_common_frequencies() { AltosFrequency[] frequencies = null; boolean existing = false; @@ -208,6 +218,9 @@ public class AltosPreferences { common_frequencies = load_common_frequencies(); AltosConvert.imperial_units = backend.getBoolean(unitsPreference, false); + + map_cache = backend.getInt(mapCachePreference, 9); + map_cache_listeners = new LinkedList<AltosMapCacheListener>(); } public static void flush_preferences() { @@ -350,12 +363,43 @@ public class AltosPreferences { synchronized(backend) { backend.putBytes(String.format(statePreferenceFormat, serial), bytes); + backend.putInt(statePreferenceLatest, serial); flush_preferences(); } } catch (IOException ie) { } } + public static ArrayList<Integer> list_states() { + String[] keys = backend.keys(); + ArrayList<Integer> states = new ArrayList<Integer>(); + + for (String key : keys) { + if (key.startsWith(statePreferenceHead)) { + try { + int serial = AltosParse.parse_int(key.substring(statePreferenceHead.length())); + states.add(serial); + } catch (ParseException pe) { + } + } + } + return states; + } + + public static void remove_state(int serial) { + synchronized(backend) { + backend.remove(String.format(statePreferenceFormat, serial)); + } + } + + public static int latest_state() { + int latest = 0; + synchronized (backend) { + latest = backend.getInt(statePreferenceLatest, 0); + } + return latest; + } + public static AltosSavedState state(int serial) { byte[] bytes = null; @@ -548,4 +592,33 @@ public class AltosPreferences { units_listeners.remove(l); } } + + + public static void register_map_cache_listener(AltosMapCacheListener l) { + synchronized(backend) { + map_cache_listeners.add(l); + } + } + + public static void unregister_map_cache_listener(AltosMapCacheListener l) { + synchronized (backend) { + map_cache_listeners.remove(l); + } + } + + public static void set_map_cache(int new_map_cache) { + synchronized(backend) { + map_cache = new_map_cache; + backend.putInt(mapCachePreference, map_cache); + flush_preferences(); + for (AltosMapCacheListener l: map_cache_listeners) + l.map_cache_changed(map_cache); + } + } + + public static int map_cache() { + synchronized(backend) { + return map_cache; + } + } } diff --git a/altoslib/AltosPreferencesBackend.java b/altoslib/AltosPreferencesBackend.java index c83eaa47..3154a519 100644 --- a/altoslib/AltosPreferencesBackend.java +++ b/altoslib/AltosPreferencesBackend.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.File; diff --git a/altoslib/AltosProgrammer.java b/altoslib/AltosProgrammer.java index 7a92c2d0..74ab42b5 100644 --- a/altoslib/AltosProgrammer.java +++ b/altoslib/AltosProgrammer.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosPyro.java b/altoslib/AltosPyro.java index 502e34de..9b7e79f3 100644 --- a/altoslib/AltosPyro.java +++ b/altoslib/AltosPyro.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.*; import java.text.*; diff --git a/altoslib/AltosQuaternion.java b/altoslib/AltosQuaternion.java index 4ad1f3d6..f78b5065 100644 --- a/altoslib/AltosQuaternion.java +++ b/altoslib/AltosQuaternion.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosQuaternion { double r; /* real bit */ diff --git a/altoslib/AltosRectangle.java b/altoslib/AltosRectangle.java new file mode 100644 index 00000000..1b1cc9f4 --- /dev/null +++ b/altoslib/AltosRectangle.java @@ -0,0 +1,29 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +public class AltosRectangle { + public int x, y, width, height; + + public AltosRectangle(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + } +} diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java index 98f0e7d5..9419ec93 100644 --- a/altoslib/AltosReplayReader.java +++ b/altoslib/AltosReplayReader.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; @@ -40,7 +40,7 @@ public class AltosReplayReader extends AltosFlightReader { public void update(AltosState state) throws InterruptedException { /* Make it run in realtime after the rocket leaves the pad */ if (state.state > AltosLib.ao_flight_pad && state.time_change > 0) - Thread.sleep((int) (Math.min(state.time_change,10) * 100)); + Thread.sleep((int) (Math.min(state.time_change,10) * 1000)); state.set_received_time(System.currentTimeMillis()); } diff --git a/altoslib/AltosRomconfig.java b/altoslib/AltosRomconfig.java index c93a01c3..28134aeb 100644 --- a/altoslib/AltosRomconfig.java +++ b/altoslib/AltosRomconfig.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosRotation.java b/altoslib/AltosRotation.java index 49225f77..9771c166 100644 --- a/altoslib/AltosRotation.java +++ b/altoslib/AltosRotation.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosRotation { private AltosQuaternion rotation; diff --git a/altoslib/AltosSavedState.java b/altoslib/AltosSavedState.java index 552e4533..e2ca9f73 100644 --- a/altoslib/AltosSavedState.java +++ b/altoslib/AltosSavedState.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosSelfFlash.java b/altoslib/AltosSelfFlash.java index 83be4be1..e2988b56 100644 --- a/altoslib/AltosSelfFlash.java +++ b/altoslib/AltosSelfFlash.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; diff --git a/altoslib/AltosSensorEMini.java b/altoslib/AltosSensorEMini.java index cb8356a1..e8b7b044 100644 --- a/altoslib/AltosSensorEMini.java +++ b/altoslib/AltosSensorEMini.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorMM.java b/altoslib/AltosSensorMM.java index 9d5649aa..9be29ae0 100644 --- a/altoslib/AltosSensorMM.java +++ b/altoslib/AltosSensorMM.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorMega.java b/altoslib/AltosSensorMega.java index a3c2a033..1f71302e 100644 --- a/altoslib/AltosSensorMega.java +++ b/altoslib/AltosSensorMega.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorMetrum.java b/altoslib/AltosSensorMetrum.java index 39592b50..c00e00d5 100644 --- a/altoslib/AltosSensorMetrum.java +++ b/altoslib/AltosSensorMetrum.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorTGPS.java b/altoslib/AltosSensorTGPS.java index 32607fba..64cce640 100644 --- a/altoslib/AltosSensorTGPS.java +++ b/altoslib/AltosSensorTGPS.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorTM.java b/altoslib/AltosSensorTM.java index c82ba93c..4957b94d 100644 --- a/altoslib/AltosSensorTM.java +++ b/altoslib/AltosSensorTM.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSensorTMini.java b/altoslib/AltosSensorTMini.java index 0fc70e71..9b2db2ff 100644 --- a/altoslib/AltosSensorTMini.java +++ b/altoslib/AltosSensorTMini.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.util.concurrent.TimeoutException; diff --git a/altoslib/AltosSpeed.java b/altoslib/AltosSpeed.java index b714412f..b9fca82b 100644 --- a/altoslib/AltosSpeed.java +++ b/altoslib/AltosSpeed.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosSpeed extends AltosUnits { diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index d363027c..4edae54a 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -19,7 +19,7 @@ * Track flight state from telemetry or eeprom data stream */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; @@ -31,8 +31,9 @@ public class AltosState implements Cloneable, Serializable { public int set; + static final double filter_len = 2.0; static final double ascent_filter_len = 0.5; - static final double descent_filter_len = 0.5; + static final double descent_filter_len = 5.0; /* derived data */ @@ -64,8 +65,10 @@ public class AltosState implements Cloneable, Serializable { } void set_filtered(double new_value, double time) { - if (prev_value != AltosLib.MISSING) - new_value = (prev_value * 15.0 + new_value) / 16.0; + if (prev_value != AltosLib.MISSING) { + double f = 1/Math.exp((time - prev_set_time) / filter_len); + new_value = f * new_value + (1-f) * prev_value; + } set(new_value, time); } @@ -1040,6 +1043,10 @@ public class AltosState implements Cloneable, Serializable { return AltosLib.state_name(state); } + public void set_product(String product) { + this.product = product; + } + public void set_state(int state) { if (state != AltosLib.ao_flight_invalid) { this.state = state; diff --git a/altoslib/AltosStateIterable.java b/altoslib/AltosStateIterable.java index 3b58fc32..5533468b 100644 --- a/altoslib/AltosStateIterable.java +++ b/altoslib/AltosStateIterable.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosStateUpdate.java b/altoslib/AltosStateUpdate.java index 93b9f1c0..ed2e1826 100644 --- a/altoslib/AltosStateUpdate.java +++ b/altoslib/AltosStateUpdate.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosStateUpdate { public void update_state(AltosState state) throws InterruptedException; diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java index 449384d5..4c973cd9 100644 --- a/altoslib/AltosTelemetry.java +++ b/altoslib/AltosTelemetry.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; diff --git a/altoslib/AltosTelemetryConfiguration.java b/altoslib/AltosTelemetryConfiguration.java index 8c922b03..c4cd36b2 100644 --- a/altoslib/AltosTelemetryConfiguration.java +++ b/altoslib/AltosTelemetryConfiguration.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryConfiguration extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryFile.java b/altoslib/AltosTelemetryFile.java index 738e4dd7..d2275f54 100644 --- a/altoslib/AltosTelemetryFile.java +++ b/altoslib/AltosTelemetryFile.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosTelemetryIterable.java b/altoslib/AltosTelemetryIterable.java index 131389d5..e18f7eda 100644 --- a/altoslib/AltosTelemetryIterable.java +++ b/altoslib/AltosTelemetryIterable.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.io.*; import java.util.*; diff --git a/altoslib/AltosTelemetryLegacy.java b/altoslib/AltosTelemetryLegacy.java index 923d139e..f8e72c86 100644 --- a/altoslib/AltosTelemetryLegacy.java +++ b/altoslib/AltosTelemetryLegacy.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; diff --git a/altoslib/AltosTelemetryLocation.java b/altoslib/AltosTelemetryLocation.java index 85da27d5..9341d003 100644 --- a/altoslib/AltosTelemetryLocation.java +++ b/altoslib/AltosTelemetryLocation.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryLocation extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryMap.java b/altoslib/AltosTelemetryMap.java index e8c02e9b..d3242254 100644 --- a/altoslib/AltosTelemetryMap.java +++ b/altoslib/AltosTelemetryMap.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.util.HashMap; diff --git a/altoslib/AltosTelemetryMegaData.java b/altoslib/AltosTelemetryMegaData.java index 2b80df4a..2af8d931 100644 --- a/altoslib/AltosTelemetryMegaData.java +++ b/altoslib/AltosTelemetryMegaData.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryMegaData extends AltosTelemetryStandard { int state; diff --git a/altoslib/AltosTelemetryMegaSensor.java b/altoslib/AltosTelemetryMegaSensor.java index a01c0826..77a76cd7 100644 --- a/altoslib/AltosTelemetryMegaSensor.java +++ b/altoslib/AltosTelemetryMegaSensor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryMegaSensor extends AltosTelemetryStandard { int accel; diff --git a/altoslib/AltosTelemetryMetrumData.java b/altoslib/AltosTelemetryMetrumData.java index e53f1283..17a3410a 100644 --- a/altoslib/AltosTelemetryMetrumData.java +++ b/altoslib/AltosTelemetryMetrumData.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryMetrumData extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryMetrumSensor.java b/altoslib/AltosTelemetryMetrumSensor.java index 415b00a6..71fbd544 100644 --- a/altoslib/AltosTelemetryMetrumSensor.java +++ b/altoslib/AltosTelemetryMetrumSensor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryMetrumSensor extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryMini.java b/altoslib/AltosTelemetryMini.java index 02537c9c..c9fda5b8 100644 --- a/altoslib/AltosTelemetryMini.java +++ b/altoslib/AltosTelemetryMini.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryMini extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryRaw.java b/altoslib/AltosTelemetryRaw.java index 4254fc83..843ca4eb 100644 --- a/altoslib/AltosTelemetryRaw.java +++ b/altoslib/AltosTelemetryRaw.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetryRaw extends AltosTelemetryStandard { public AltosTelemetryRaw(int[] bytes) { diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java index 908fb5c7..cbbdceff 100644 --- a/altoslib/AltosTelemetryReader.java +++ b/altoslib/AltosTelemetryReader.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; import java.text.*; import java.io.*; diff --git a/altoslib/AltosTelemetrySatellite.java b/altoslib/AltosTelemetrySatellite.java index 474789ba..2f4e3354 100644 --- a/altoslib/AltosTelemetrySatellite.java +++ b/altoslib/AltosTelemetrySatellite.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetrySatellite extends AltosTelemetryStandard { int channels; diff --git a/altoslib/AltosTelemetrySensor.java b/altoslib/AltosTelemetrySensor.java index b0c84fd3..27cf22f0 100644 --- a/altoslib/AltosTelemetrySensor.java +++ b/altoslib/AltosTelemetrySensor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTelemetrySensor extends AltosTelemetryStandard { diff --git a/altoslib/AltosTelemetryStandard.java b/altoslib/AltosTelemetryStandard.java index fb8a162e..27561826 100644 --- a/altoslib/AltosTelemetryStandard.java +++ b/altoslib/AltosTelemetryStandard.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public abstract class AltosTelemetryStandard extends AltosTelemetry { int[] bytes; diff --git a/altoslib/AltosTemperature.java b/altoslib/AltosTemperature.java index 494f4e3e..b4d465a0 100644 --- a/altoslib/AltosTemperature.java +++ b/altoslib/AltosTemperature.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosTemperature extends AltosUnits { diff --git a/altoslib/AltosUnits.java b/altoslib/AltosUnits.java index f6e34e77..6b4fff0c 100644 --- a/altoslib/AltosUnits.java +++ b/altoslib/AltosUnits.java @@ -15,7 +15,9 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; + +import java.text.*; public abstract class AltosUnits { @@ -29,13 +31,22 @@ public abstract class AltosUnits { public abstract int show_fraction(int width, boolean imperial_units); - public double parse(String s, boolean imperial_units) throws NumberFormatException { - double v = Double.parseDouble(s); + public double parse_locale(String s, boolean imperial_units) throws ParseException { + double v = AltosParse.parse_double_locale(s); + return inverse(v, imperial_units); + } + + public double parse_net(String s, boolean imperial_units) throws ParseException { + double v = AltosParse.parse_double_net(s); return inverse(v, imperial_units); } - public double parse(String s) throws NumberFormatException { - return parse(s, AltosConvert.imperial_units); + public double parse_locale(String s) throws ParseException { + return parse_locale(s, AltosConvert.imperial_units); + } + + public double parse_net(String s) throws ParseException { + return parse_net(s, AltosConvert.imperial_units); } public double value(double v) { @@ -105,4 +116,4 @@ public abstract class AltosUnits { public String say_units(double v) { return say_units(v, AltosConvert.imperial_units); } -}
\ No newline at end of file +} diff --git a/altoslib/AltosUnitsListener.java b/altoslib/AltosUnitsListener.java index 1b66eb45..0e31492e 100644 --- a/altoslib/AltosUnitsListener.java +++ b/altoslib/AltosUnitsListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosUnitsListener { public void units_changed(boolean imperial_units); diff --git a/altosuilib/AltosUIVersion.java.in b/altoslib/AltosVersion.java.in index beb62cbf..89ed400f 100644 --- a/altosuilib/AltosUIVersion.java.in +++ b/altoslib/AltosVersion.java.in @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altoslib_8; -public class AltosUIVersion { +public class AltosVersion { public final static String version = "@VERSION@"; public final static String google_maps_api_key = @GOOGLEKEY@; - static boolean has_google_maps_api_key() { + public static boolean has_google_maps_api_key() { return google_maps_api_key != null && google_maps_api_key.length() > 1; } } diff --git a/altoslib/AltosVoltage.java b/altoslib/AltosVoltage.java index 986d74b4..fb6f41ee 100644 --- a/altoslib/AltosVoltage.java +++ b/altoslib/AltosVoltage.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public class AltosVoltage extends AltosUnits { diff --git a/altoslib/AltosWriter.java b/altoslib/AltosWriter.java index b125bd87..1dcd3391 100644 --- a/altoslib/AltosWriter.java +++ b/altoslib/AltosWriter.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altoslib_6; +package org.altusmetrum.altoslib_8; public interface AltosWriter { diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index c640c69c..a6b178fa 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -128,7 +128,35 @@ altoslib_JAVA = \ AltosPyro.java \ AltosWriter.java \ AltosQuaternion.java \ - AltosRotation.java + AltosRotation.java \ + AltosImage.java \ + AltosLatLon.java \ + AltosMap.java \ + AltosMapCache.java \ + AltosMapCacheListener.java \ + AltosMapInterface.java \ + AltosMapLine.java \ + AltosMapMark.java \ + AltosMapPath.java \ + AltosMapPathPoint.java \ + AltosMapRectangle.java \ + AltosMapStore.java \ + AltosMapStoreListener.java \ + AltosMapTile.java \ + AltosMapTileListener.java \ + AltosMapTransform.java \ + AltosMapZoomListener.java \ + AltosPointDouble.java \ + AltosPointInt.java \ + AltosRectangle.java \ + AltosFlightDisplay.java \ + AltosFontListener.java \ + AltosLaunchSite.java \ + AltosLaunchSiteListener.java \ + AltosLaunchSites.java \ + AltosMapLoaderListener.java \ + AltosMapLoader.java \ + AltosVersion.java JAR=altoslib_$(ALTOSLIB_VERSION).jar diff --git a/altosui/Altos.java b/altosui/Altos.java index e82931b2..4b3d17fe 100644 --- a/altosui/Altos.java +++ b/altosui/Altos.java @@ -20,8 +20,8 @@ package altosui; import java.awt.*; import libaltosJNI.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class Altos extends AltosUILib { diff --git a/altosui/AltosAscent.java b/altosui/AltosAscent.java index 10080efd..453246bc 100644 --- a/altosui/AltosAscent.java +++ b/altosui/AltosAscent.java @@ -21,8 +21,8 @@ import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosAscent extends AltosUIFlightTab { JLabel cur, max; diff --git a/altosui/AltosCompanionInfo.java b/altosui/AltosCompanionInfo.java index 68dab227..28f26a62 100644 --- a/altosui/AltosCompanionInfo.java +++ b/altosui/AltosCompanionInfo.java @@ -19,8 +19,8 @@ package altosui; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosCompanionInfo extends JTable implements AltosFlightDisplay { private AltosFlightInfoTableModel model; diff --git a/altosui/AltosConfig.java b/altosui/AltosConfig.java index 3c5415d2..39ed5ca4 100644 --- a/altosui/AltosConfig.java +++ b/altosui/AltosConfig.java @@ -22,8 +22,8 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.text.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfig implements ActionListener { diff --git a/altosui/AltosConfigPyroUI.java b/altosui/AltosConfigPyroUI.java index 61208dfe..50a25f00 100644 --- a/altosui/AltosConfigPyroUI.java +++ b/altosui/AltosConfigPyroUI.java @@ -17,12 +17,13 @@ package altosui; +import java.text.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfigPyroUI extends AltosUIDialog @@ -87,9 +88,9 @@ public class AltosConfigPyroUI if (units != null) { try { - double v = units.parse(value.getText(), !imperial_units); + double v = units.parse_locale(value.getText(), !imperial_units); set(enabled(), v); - } catch (NumberFormatException ne) { + } catch (ParseException pe) { set(enabled(), 0.0); } } @@ -129,9 +130,9 @@ public class AltosConfigPyroUI AltosUnits units = AltosPyro.pyro_to_units(flag); try { if (units != null) - return units.parse(value.getText()); - return Double.parseDouble(value.getText()); - } catch (NumberFormatException e) { + return units.parse_locale(value.getText()); + return AltosParse.parse_double_locale(value.getText()); + } catch (ParseException e) { throw new AltosConfigDataException("\"%s\": %s\n", value.getText(), e.getMessage()); } } @@ -298,8 +299,8 @@ public class AltosConfigPyroUI String v = pyro_firing_time_value.getSelectedItem().toString(); try { - return Double.parseDouble(v); - } catch (NumberFormatException e) { + return AltosParse.parse_double_locale(v); + } catch (ParseException e) { throw new AltosConfigDataException("Invalid pyro firing time \"%s\"", v); } } diff --git a/altosui/AltosConfigTD.java b/altosui/AltosConfigTD.java index cd20a174..112ebbc6 100644 --- a/altosui/AltosConfigTD.java +++ b/altosui/AltosConfigTD.java @@ -21,8 +21,8 @@ import java.awt.event.*; import javax.swing.*; import java.io.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfigTD implements ActionListener { diff --git a/altosui/AltosConfigTDUI.java b/altosui/AltosConfigTDUI.java index 52225108..857ab64d 100644 --- a/altosui/AltosConfigTDUI.java +++ b/altosui/AltosConfigTDUI.java @@ -21,8 +21,8 @@ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfigTDUI extends AltosUIDialog diff --git a/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java index 54f06065..76837b31 100644 --- a/altosui/AltosConfigUI.java +++ b/altosui/AltosConfigUI.java @@ -21,8 +21,9 @@ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import java.text.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfigUI extends AltosUIDialog @@ -43,6 +44,7 @@ public class AltosConfigUI JLabel rate_label; JLabel aprs_interval_label; JLabel aprs_ssid_label; + JLabel aprs_format_label; JLabel flight_log_max_label; JLabel ignite_mode_label; JLabel pad_orientation_label; @@ -66,6 +68,7 @@ public class AltosConfigUI AltosUIRateList rate_value; JComboBox<String> aprs_interval_value; JComboBox<Integer> aprs_ssid_value; + JComboBox<String> aprs_format_value; JComboBox<String> flight_log_max_value; JComboBox<String> ignite_mode_value; JComboBox<String> pad_orientation_value; @@ -218,11 +221,20 @@ public class AltosConfigUI void set_aprs_ssid_tool_tip() { if (aprs_ssid_value.isEnabled()) - aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)"); - else if (aprs_interval_value.isEnabled()) - aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID"); + aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)"); + else if (aprs_ssid_value.isEnabled()) + aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID"); else - aprs_interval_value.setToolTipText("Hardware doesn't support APRS"); + aprs_ssid_value.setToolTipText("Hardware doesn't support APRS"); + } + + void set_aprs_format_tool_tip() { + if (aprs_format_value.isEnabled()) + aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)"); + else if (aprs_format_value.isEnabled()) + aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format"); + else + aprs_format_value.setToolTipText("Hardware doesn't support APRS"); } void set_flight_log_max_tool_tip() { @@ -577,6 +589,33 @@ public class AltosConfigUI set_aprs_ssid_tool_tip(); row++; + /* APRS format */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = row; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + aprs_format_label = new JLabel("APRS format:"); + pane.add(aprs_format_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = row; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name); + aprs_format_value.setEditable(false); + aprs_format_value.addItemListener(this); + aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length); + pane.add(aprs_format_value, c); + set_aprs_format_tool_tip(); + row++; + /* Callsign */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = row; @@ -938,8 +977,13 @@ public class AltosConfigUI } - public int main_deploy() { - return (int) (AltosConvert.height.parse(main_deploy_value.getSelectedItem().toString()) + 0.5); + public int main_deploy() throws AltosConfigDataException { + String str = main_deploy_value.getSelectedItem().toString(); + try { + return (int) (AltosConvert.height.parse_locale(str) + 0.5); + } catch (ParseException pe) { + throw new AltosConfigDataException("invalid main deploy height %s", str); + } } String get_main_deploy_label() { @@ -968,14 +1012,21 @@ public class AltosConfigUI String v = main_deploy_value.getSelectedItem().toString(); main_deploy_label.setText(get_main_deploy_label()); set_main_deploy_values(); - int m = (int) (AltosConvert.height.parse(v, !imperial_units) + 0.5); - set_main_deploy(m); + try { + int m = (int) (AltosConvert.height.parse_locale(v, !imperial_units) + 0.5); + set_main_deploy(m); + } catch (ParseException pe) { + } if (tracker_motion_value.isEnabled()) { String motion = tracker_motion_value.getSelectedItem().toString(); tracker_motion_label.setText(get_tracker_motion_label()); set_tracker_motion_values(); - set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5)); + try { + int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5); + set_tracker_motion(m); + } catch (ParseException pe) { + } } if (!was_dirty) @@ -1234,7 +1285,12 @@ public class AltosConfigUI } public int tracker_motion() throws AltosConfigDataException { - return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString()); + String str = tracker_motion_value.getSelectedItem().toString(); + try { + return (int) (AltosConvert.height.parse_locale(str) + 0.5); + } catch (ParseException pe) { + throw new AltosConfigDataException("invalid tracker motion %s", str); + } } public void set_tracker_interval(int tracker_interval) { @@ -1313,4 +1369,16 @@ public class AltosConfigUI Integer i = (Integer) aprs_ssid_value.getSelectedItem(); return i; } + + public void set_aprs_format(int new_aprs_format) { + aprs_format_value.setVisible(new_aprs_format >= 0); + aprs_format_label.setVisible(new_aprs_format >= 0); + + aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format)); + set_aprs_format_tool_tip(); + } + + public int aprs_format() throws AltosConfigDataException { + return aprs_format_value.getSelectedIndex(); + } } diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java index 7bc50570..fa815762 100644 --- a/altosui/AltosConfigureUI.java +++ b/altosui/AltosConfigureUI.java @@ -22,7 +22,7 @@ import java.awt.event.*; import java.beans.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class AltosConfigureUI extends AltosUIConfigure diff --git a/altosui/AltosDescent.java b/altosui/AltosDescent.java index 0db1a4c2..f5b92816 100644 --- a/altosui/AltosDescent.java +++ b/altosui/AltosDescent.java @@ -21,8 +21,8 @@ import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosDescent extends AltosUIFlightTab { diff --git a/altosui/AltosFlightStatus.java b/altosui/AltosFlightStatus.java index c0d3312b..815a6fa4 100644 --- a/altosui/AltosFlightStatus.java +++ b/altosui/AltosFlightStatus.java @@ -19,8 +19,8 @@ package altosui; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosFlightStatus extends JComponent implements AltosFlightDisplay { GridBagLayout layout; diff --git a/altosui/AltosFlightStatusTableModel.java b/altosui/AltosFlightStatusTableModel.java index f2031698..b5587a84 100644 --- a/altosui/AltosFlightStatusTableModel.java +++ b/altosui/AltosFlightStatusTableModel.java @@ -27,7 +27,7 @@ import java.util.*; import java.text.*; import java.util.prefs.*; import java.util.concurrent.LinkedBlockingQueue; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosFlightStatusTableModel extends AbstractTableModel { private String[] columnNames = { diff --git a/altosui/AltosFlightStatusUpdate.java b/altosui/AltosFlightStatusUpdate.java index 3f6494b8..9768c6d1 100644 --- a/altosui/AltosFlightStatusUpdate.java +++ b/altosui/AltosFlightStatusUpdate.java @@ -18,7 +18,7 @@ package altosui; import java.awt.event.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosFlightStatusUpdate implements ActionListener { diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java index 424c57da..db33e0d9 100644 --- a/altosui/AltosFlightUI.java +++ b/altosui/AltosFlightUI.java @@ -22,8 +22,8 @@ import java.awt.event.*; import javax.swing.*; import java.util.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { AltosVoice voice; @@ -40,7 +40,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { AltosDescent descent; AltosLanded landed; AltosCompanionInfo companion; - AltosUIMap sitemap; + AltosUIMapNew sitemap; boolean has_map; boolean has_companion; boolean has_state; @@ -189,12 +189,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { bag = getContentPane(); bag.setLayout(new GridBagLayout()); - GridBagConstraints c = new GridBagConstraints(); - setTitle(String.format("AltOS %s", reader.name)); /* Stick channel selector at top of table for telemetry monitoring */ if (serial >= 0) { + set_inset(3); + // Frequency menu frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial)); frequencies.set_product("Monitor"); @@ -210,14 +210,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { reader.save_frequency(); } }); - c.gridx = 0; - c.gridy = 0; - c.weightx = 0; - c.weighty = 0; - c.insets = new Insets(3, 3, 3, 3); - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - bag.add (frequencies, c); + bag.add (frequencies, constraints(0, 1)); // Telemetry rate list rates = new AltosUIRateList(AltosUIPreferences.telemetry_rate(serial)); @@ -233,14 +226,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { } }); rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400)); - c.gridx = 1; - c.gridy = 0; - c.weightx = 0; - c.weighty = 0; - c.insets = new Insets(3, 3, 3, 3); - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - bag.add (rates, c); + bag.add (rates, constraints(1, 1)); // Telemetry format list if (reader.supports_telemetry(Altos.ao_telemetry_standard)) { @@ -252,14 +238,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { reader.save_telemetry(); } }); - c.gridx = 2; - c.gridy = 0; - c.weightx = 0; - c.weighty = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - bag.add (telemetries, c); - c.insets = new Insets(0, 0, 0, 0); + bag.add (telemetries, constraints(2, 1)); } else { String version; @@ -271,26 +250,17 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { version = "Telemetry: None"; telemetry = new JLabel(version); - c.gridx = 2; - c.gridy = 0; - c.weightx = 0; - c.weighty = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - bag.add (telemetry, c); - c.insets = new Insets(0, 0, 0, 0); + bag.add (telemetry, constraints(2, 1)); } + next_row(); } + set_inset(0); /* Flight status is always visible */ flightStatus = new AltosFlightStatus(); displays.add(flightStatus); - c.gridx = 0; - c.gridy = 1; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridwidth = 3; - bag.add(flightStatus, c); + bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL)); + next_row(); /* The rest of the window uses a tabbed pane to * show one of the alternate data views @@ -319,17 +289,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay { has_companion = false; has_state = false; - sitemap = new AltosUIMap(); + sitemap = new AltosUIMapNew(); displays.add(sitemap); has_map = false; /* Make the tabbed pane use the rest of the window space */ - c.gridx = 0; - c.gridy = 2; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - bag.add(pane, c); + bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH)); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java index f5e339a5..6e45a092 100644 --- a/altosui/AltosGraphUI.java +++ b/altosui/AltosGraphUI.java @@ -23,8 +23,8 @@ import java.util.ArrayList; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; @@ -35,7 +35,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt JTabbedPane pane; AltosGraph graph; AltosUIEnable enable; - AltosUIMap map; + AltosUIMapNew map; AltosState state; AltosGraphDataSet graphDataSet; AltosFlightStats stats; @@ -47,7 +47,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt for (AltosState state : states) { if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) { if (map == null) - map = new AltosUIMap(); + map = new AltosUIMapNew(); map.show(state, null); has_gps = true; } diff --git a/altosui/AltosIdleMonitorUI.java b/altosui/AltosIdleMonitorUI.java index 204d81ed..d54b05c5 100644 --- a/altosui/AltosIdleMonitorUI.java +++ b/altosui/AltosIdleMonitorUI.java @@ -24,8 +24,8 @@ import javax.swing.event.*; import java.io.*; import java.util.concurrent.*; import java.util.Arrays; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDisplay, AltosIdleMonitorListener, DocumentListener { AltosDevice device; @@ -35,9 +35,11 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl AltosFlightStatus flightStatus; AltosIgnitor ignitor; AltosIdleMonitor thread; + AltosUIMapNew sitemap; int serial; boolean remote; boolean has_ignitor; + boolean has_map; void stop_display() { if (thread != null) { @@ -83,11 +85,26 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl has_ignitor = false; } } + if (state.gps != null && state.gps.connected) { + if (!has_map) { + pane.add("Site Map", sitemap); + has_map = true; + } + } else { + if (has_map) { + pane.remove(sitemap); + has_map = false; + } + } + // try { pad.show(state, listener_state); flightStatus.show(state, listener_state); flightInfo.show(state, listener_state); - ignitor.show(state, listener_state); + if (has_ignitor) + ignitor.show(state, listener_state); + if (has_map) + sitemap.show(state, listener_state); // } catch (Exception e) { // System.out.print("Show exception " + e); // } @@ -119,6 +136,7 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl public void changedUpdate(DocumentEvent e) { if (callsign_value != null) { String callsign = callsign_value.getText(); + System.out.printf("callsign set to %s\n", callsign); thread.set_callsign(callsign); AltosUIPreferences.set_callsign(callsign); } @@ -132,30 +150,6 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl changedUpdate(e); } - int row = 0; - - public GridBagConstraints constraints (int x, int width, int fill) { - GridBagConstraints c = new GridBagConstraints(); - Insets insets = new Insets(4, 4, 4, 4); - - c.insets = insets; - c.fill = fill; - if (width == 3) - c.anchor = GridBagConstraints.CENTER; - else if (x == 2) - c.anchor = GridBagConstraints.EAST; - else - c.anchor = GridBagConstraints.WEST; - c.gridx = x; - c.gridwidth = width; - c.gridy = row; - return c; - } - - public GridBagConstraints constraints(int x, int width) { - return constraints(x, width, GridBagConstraints.NONE); - } - void idle_exception(JFrame owner, Exception e) { if (e instanceof FileNotFoundException) { JOptionPane.showMessageDialog(owner, @@ -209,11 +203,14 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl AltosSerial link; try { link = new AltosSerial(device); - link.set_frame(this); } catch (Exception ex) { idle_exception(in_owner, ex); return; } + link.set_frame(this); + + /* We let the user set the freq/callsign, so don't bother with the cancel dialog */ + link.set_cancel_enable(false); bag = getContentPane(); bag.setLayout(new GridBagLayout()); @@ -222,6 +219,8 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl /* Stick frequency selector at top of table for telemetry monitoring */ if (remote && serial >= 0) { + set_inset(3); + // Frequency menu frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial)); frequencies.addActionListener(new ActionListener() { @@ -238,15 +237,17 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl callsign_value = new JTextField(AltosUIPreferences.callsign()); callsign_value.getDocument().addDocumentListener(this); callsign_value.setToolTipText("Callsign sent in packet mode"); - bag.add(callsign_value, constraints(2, 1, GridBagConstraints.BOTH)); - row++; + bag.add(callsign_value, constraints(2, 1, GridBagConstraints.HORIZONTAL)); + next_row(); } + set_inset(0); /* Flight status is always visible */ flightStatus = new AltosFlightStatus(); - bag.add(flightStatus, constraints(0, 3, GridBagConstraints.HORIZONTAL)); - row++; + bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL)); + + next_row(); /* The rest of the window uses a tabbed pane to * show one of the alternate data views @@ -261,8 +262,10 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl ignitor = new AltosIgnitor(); + sitemap = new AltosUIMapNew(); + /* Make the tabbed pane use the rest of the window space */ - bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH)); + bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH)); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java index 944c659b..14a2b606 100644 --- a/altosui/AltosIgniteUI.java +++ b/altosui/AltosIgniteUI.java @@ -24,8 +24,8 @@ import java.io.*; import java.text.*; import java.util.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosIgniteUI extends AltosUIDialog @@ -50,8 +50,6 @@ public class AltosIgniteUI LinkedBlockingQueue<String> command_queue; - LinkedBlockingQueue<String> reply_queue; - class Igniter { JRadioButton button; JLabel status_label; @@ -150,8 +148,7 @@ public class AltosIgniteUI } reply = "status"; } else if (command.equals("get_npyro")) { - put_reply(String.format("%d", ignite.npyro())); - continue; + reply = String.format("npyro %d", ignite.npyro()); } else if (command.equals("quit")) { ignite.close(); break; @@ -211,6 +208,9 @@ public class AltosIgniteUI set_ignite_status(); } else if (reply.equals("fired")) { fired(); + } else if (reply.startsWith("npyro")) { + npyro = Integer.parseInt(reply.substring(6)); + make_ui(); } } @@ -250,24 +250,6 @@ public class AltosIgniteUI } } - void put_reply(String reply) { - try { - reply_queue.put(reply); - } catch (Exception ex) { - ignite_exception(ex); - } - } - - String get_reply() { - String reply = ""; - try { - reply = reply_queue.take(); - } catch (Exception ex) { - ignite_exception(ex); - } - return reply; - } - boolean getting_status = false; boolean visible = false; @@ -287,12 +269,6 @@ public class AltosIgniteUI } } - int get_npyro() { - send_command("get_npyro"); - String reply = get_reply(); - return Integer.parseInt(reply); - } - boolean firing = false; void start_fire(String which) { @@ -310,8 +286,9 @@ public class AltosIgniteUI void close() { if (opened) { send_command("quit"); - timer.stop(); } + if (timer != null) + timer.stop(); setVisible(false); dispose(); } @@ -383,7 +360,6 @@ public class AltosIgniteUI private boolean open() { command_queue = new LinkedBlockingQueue<String>(); - reply_queue = new LinkedBlockingQueue<String>(); opened = false; device = AltosDeviceUIDialog.show(owner, Altos.product_any); @@ -403,13 +379,7 @@ public class AltosIgniteUI return false; } - public AltosIgniteUI(JFrame in_owner) { - - owner = in_owner; - - if (!open()) - return; - + private void make_ui() { group = new ButtonGroup(); Container pane = getContentPane(); @@ -422,8 +392,6 @@ public class AltosIgniteUI timer_running = false; timer.restart(); - owner = in_owner; - pane.setLayout(new GridBagLayout()); c.fill = GridBagConstraints.NONE; @@ -443,8 +411,6 @@ public class AltosIgniteUI y++; - int npyro = get_npyro(); - igniters = new Igniter[2 + npyro]; igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++); @@ -492,4 +458,14 @@ public class AltosIgniteUI addWindowListener(new ConfigListener(this)); } -}
\ No newline at end of file + + public AltosIgniteUI(JFrame in_owner) { + + owner = in_owner; + + if (!open()) + return; + + send_command("get_npyro"); + } +} diff --git a/altosui/AltosIgnitor.java b/altosui/AltosIgnitor.java index 82582a28..682afc5e 100644 --- a/altosui/AltosIgnitor.java +++ b/altosui/AltosIgnitor.java @@ -20,8 +20,8 @@ package altosui; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosIgnitor extends AltosUIFlightTab { diff --git a/altosui/AltosLanded.java b/altosui/AltosLanded.java index 2237df0c..c2997b29 100644 --- a/altosui/AltosLanded.java +++ b/altosui/AltosLanded.java @@ -21,8 +21,8 @@ import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.io.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosLanded extends AltosUIFlightTab implements ActionListener { diff --git a/altosui/AltosLaunch.java b/altosui/AltosLaunch.java index 37077c42..6dc1ca02 100644 --- a/altosui/AltosLaunch.java +++ b/altosui/AltosLaunch.java @@ -20,7 +20,7 @@ package altosui; import java.io.*; import java.util.concurrent.*; import java.awt.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class AltosLaunch { AltosDevice device; diff --git a/altosui/AltosLaunchUI.java b/altosui/AltosLaunchUI.java index 4c205d44..132001bf 100644 --- a/altosui/AltosLaunchUI.java +++ b/altosui/AltosLaunchUI.java @@ -23,7 +23,7 @@ import javax.swing.*; import java.io.*; import java.text.*; import java.util.concurrent.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; class FireButton extends JButton { protected void processMouseEvent(MouseEvent e) { diff --git a/altosui/AltosPad.java b/altosui/AltosPad.java index 6214fa5a..2f87b681 100644 --- a/altosui/AltosPad.java +++ b/altosui/AltosPad.java @@ -18,8 +18,8 @@ package altosui; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosPad extends AltosUIFlightTab { @@ -103,9 +103,12 @@ public class AltosPad extends AltosUIFlightTab { public double voltage(AltosState state) { return AltosLib.MISSING; } - public boolean hide(double v) { return v == AltosLib.MISSING; } public double good() { return AltosLib.ao_battery_good; } + public boolean hide(AltosState state, AltosListenerState listener_state, int i) { + return value(state, listener_state, i) == AltosLib.MISSING; + } + public double value(AltosState state, AltosListenerState listener_state, int i) { if (listener_state == null) return AltosLib.MISSING; @@ -243,12 +246,12 @@ public class AltosPad extends AltosUIFlightTab { public AltosPad() { int y = 0; add(new Battery(this, y++)); + add(new ReceiverBattery(this, y++)); add(new Apogee(this, y++)); add(new Main(this, y++)); add(new LoggingReady(this, y++)); add(new GPSLocked(this, y++)); add(new GPSReady(this, y++)); - add(new ReceiverBattery(this, y++)); add(new PadLat(this, y++)); add(new PadLon(this, y++)); add(new PadAlt(this, y++)); diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java index 0af09651..f6caa4ef 100644 --- a/altosui/AltosUI.java +++ b/altosui/AltosUI.java @@ -22,8 +22,8 @@ import java.awt.event.*; import javax.swing.*; import java.io.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class AltosUI extends AltosUIFrame { public AltosVoice voice = new AltosVoice(); @@ -295,7 +295,7 @@ public class AltosUI extends AltosUIFrame { } void LoadMaps() { - new AltosUIMapPreload(AltosUI.this); + new AltosUIMapPreloadNew(AltosUI.this); } void LaunchController() { @@ -587,16 +587,7 @@ public class AltosUI extends AltosUIFrame { for (int i = 0; i < args.length; i++) { if (args[i].equals("--help")) help(0); - else if (args[i].equals("--fetchmaps")) { - if (args.length < i + 3) { - help(1); - } else { - double lat = Double.parseDouble(args[i+1]); - double lon = Double.parseDouble(args[i+2]); -// AltosSiteMap.prefetchMaps(lat, lon); - i += 2; - } - } else if (args[i].equals("--replay")) + else if (args[i].equals("--replay")) process = process_replay; else if (args[i].equals("--kml")) process = process_kml; diff --git a/altosui/altos-windows.nsi.in b/altosui/altos-windows.nsi.in index 6d4dabfa..2f22a4a3 100644 --- a/altosui/altos-windows.nsi.in +++ b/altosui/altos-windows.nsi.in @@ -103,16 +103,17 @@ Section "${REG_NAME} Application" File "freetts.jar" File "jfreechart.jar" File "jcommon.jar" + File "../icon/${WIN_APP_EXE}" File "*.dll" File "../icon/${WIN_APP_ICON}" - CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "${REG_NAME} Desktop Shortcut" - CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "Firmware" @@ -153,7 +154,6 @@ Section "File Associations" SetOutPath $INSTDIR - File "../icon/${WIN_APP_EXE}" File "../icon/${WIN_TELEM_EXE}" File "../icon/${WIN_EEPROM_EXE}" @@ -165,15 +165,13 @@ Section "File Associations" DeleteRegKey HKCR ".telem\${PROG_ID_EEPROM}" DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}" - SearchPath $1 "javaw.exe" - ; .eeprom elements WriteRegStr HKCR "${PROG_ID_EEPROM}" "" "Altus Metrum Log File" WriteRegStr HKCR "${PROG_ID_EEPROM}" "FriendlyTypeName" "Altus Metrum Log File" WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer" "" "${PROG_ID_EEPROM}" WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" "" '"$INSTDIR\${WIN_EEPROM_EXE}",-101' - WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"' + WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"' WriteRegStr HKCR ".eeprom" "" "${PROG_ID_EEPROM}" WriteRegStr HKCR ".eeprom" "PerceivedType" "Altus Metrum Log File" @@ -188,7 +186,7 @@ Section "File Associations" WriteRegStr HKCR "${PROG_ID_TELEM}" "FriendlyTypeName" "Altus Metrum Telemetry File" WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer" "" "${PROG_ID_TELEM}" WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" "" '"$INSTDIR\${WIN_TELEM_EXE}",-101' - WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"' + WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"' WriteRegStr HKCR ".telem" "" "${PROG_ID_TELEM}" WriteRegStr HKCR ".telem" "PerceivedType" "Altus Metrum Telemetry File" diff --git a/altosuilib/.gitignore b/altosuilib/.gitignore index 4ad8a77a..943408ec 100644 --- a/altosuilib/.gitignore +++ b/altosuilib/.gitignore @@ -1,4 +1,3 @@ -AltosUIVersion.java bin classaltosuilib.stamp altosuilib*.jar diff --git a/altosuilib/AltosBTDevice.java b/altosuilib/AltosBTDevice.java index 0dd1cab2..0d7d07c0 100644 --- a/altosuilib/AltosBTDevice.java +++ b/altosuilib/AltosBTDevice.java @@ -15,10 +15,10 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import libaltosJNI.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosBTDevice extends altos_bt_device implements AltosDevice { @@ -104,17 +104,20 @@ public class AltosBTDevice extends altos_bt_device implements AltosDevice { return false; } + public int hashCode() { + return getName().hashCode() ^ getAddr().hashCode(); + } + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof AltosBTDevice)) return false; AltosBTDevice other = (AltosBTDevice) o; return getName().equals(other.getName()) && getAddr().equals(other.getAddr()); } - public int hashCode() { - return getName().hashCode() ^ getAddr().hashCode(); - } - public AltosBTDevice(String name, String addr) { AltosUILib.load_library(); libaltos.altos_bt_fill_in(name, addr,this); diff --git a/altosuilib/AltosBTDeviceIterator.java b/altosuilib/AltosBTDeviceIterator.java index eebad36b..ca6c436e 100644 --- a/altosuilib/AltosBTDeviceIterator.java +++ b/altosuilib/AltosBTDeviceIterator.java @@ -15,11 +15,11 @@ * 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; diff --git a/altosuilib/AltosBTKnown.java b/altosuilib/AltosBTKnown.java index 73ee473f..0d10fafd 100644 --- a/altosuilib/AltosBTKnown.java +++ b/altosuilib/AltosBTKnown.java @@ -15,10 +15,10 @@ * 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>(); diff --git a/altosuilib/AltosBTManage.java b/altosuilib/AltosBTManage.java index c4ac363f..8a912c94 100644 --- a/altosuilib/AltosBTManage.java +++ b/altosuilib/AltosBTManage.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -23,7 +23,7 @@ import javax.swing.*; import javax.swing.plaf.basic.*; import java.util.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosBTManage extends AltosUIDialog implements ActionListener, Iterable<AltosBTDevice> { LinkedBlockingQueue<AltosBTDevice> found_devices; diff --git a/altosuilib/AltosCSVUI.java b/altosuilib/AltosCSVUI.java index 94d523fe..281a9081 100644 --- a/altosuilib/AltosCSVUI.java +++ b/altosuilib/AltosCSVUI.java @@ -15,13 +15,13 @@ * 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 diff --git a/altosuilib/AltosConfigFreqUI.java b/altosuilib/AltosConfigFreqUI.java index 6253e3e4..04d93f6e 100644 --- a/altosuilib/AltosConfigFreqUI.java +++ b/altosuilib/AltosConfigFreqUI.java @@ -15,13 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; +import java.text.*; import java.awt.event.*; import javax.swing.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; class AltosEditFreqUI extends AltosUIDialog implements ActionListener { Frame frame; @@ -51,10 +52,10 @@ class AltosEditFreqUI extends AltosUIDialog implements ActionListener { String d_s = description.getText(); try { - double f_d = Double.parseDouble(f_s); + double f_d = AltosParse.parse_double_locale(f_s); return new AltosFrequency(f_d, d_s); - } catch (NumberFormatException ne) { + } catch (ParseException ne) { } return null; } diff --git a/altosuilib/AltosDataChooser.java b/altosuilib/AltosDataChooser.java index 3fe76687..d30aec3f 100644 --- a/altosuilib/AltosDataChooser.java +++ b/altosuilib/AltosDataChooser.java @@ -15,12 +15,12 @@ * 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; diff --git a/altosuilib/AltosDevice.java b/altosuilib/AltosDevice.java index 401387a4..64a99596 100644 --- a/altosuilib/AltosDevice.java +++ b/altosuilib/AltosDevice.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import libaltosJNI.*; diff --git a/altosuilib/AltosDeviceDialog.java b/altosuilib/AltosDeviceDialog.java index 5fb1331e..a3c5d283 100644 --- a/altosuilib/AltosDeviceDialog.java +++ b/altosuilib/AltosDeviceDialog.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import javax.swing.*; import java.awt.*; diff --git a/altosuilib/AltosDeviceUIDialog.java b/altosuilib/AltosDeviceUIDialog.java index 9618895e..aa6d323f 100644 --- a/altosuilib/AltosDeviceUIDialog.java +++ b/altosuilib/AltosDeviceUIDialog.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import javax.swing.*; import java.awt.*; diff --git a/altosuilib/AltosDisplayThread.java b/altosuilib/AltosDisplayThread.java index ccf8b3ef..73d85698 100644 --- a/altosuilib/AltosDisplayThread.java +++ b/altosuilib/AltosDisplayThread.java @@ -15,13 +15,13 @@ * 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 { diff --git a/altosuilib/AltosEepromDelete.java b/altosuilib/AltosEepromDelete.java index 9fb21cf4..7648382d 100644 --- a/altosuilib/AltosEepromDelete.java +++ b/altosuilib/AltosEepromDelete.java @@ -15,13 +15,13 @@ * 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; diff --git a/altosuilib/AltosEepromManage.java b/altosuilib/AltosEepromManage.java index 47a62ef7..d97edd12 100644 --- a/altosuilib/AltosEepromManage.java +++ b/altosuilib/AltosEepromManage.java @@ -15,13 +15,13 @@ * 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 { diff --git a/altosuilib/AltosEepromMonitor.java b/altosuilib/AltosEepromMonitor.java index 83c5c1d0..28fa734d 100644 --- a/altosuilib/AltosEepromMonitor.java +++ b/altosuilib/AltosEepromMonitor.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; diff --git a/altosuilib/AltosEepromMonitorUI.java b/altosuilib/AltosEepromMonitorUI.java index ac39fb1d..5f0df7d3 100644 --- a/altosuilib/AltosEepromMonitorUI.java +++ b/altosuilib/AltosEepromMonitorUI.java @@ -15,12 +15,12 @@ * 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; diff --git a/altosuilib/AltosEepromSelect.java b/altosuilib/AltosEepromSelect.java index b291c35a..56b10ef0 100644 --- a/altosuilib/AltosEepromSelect.java +++ b/altosuilib/AltosEepromSelect.java @@ -15,13 +15,13 @@ * 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; diff --git a/altosuilib/AltosFlashUI.java b/altosuilib/AltosFlashUI.java index 6e497c42..0ab085cf 100644 --- a/altosuilib/AltosFlashUI.java +++ b/altosuilib/AltosFlashUI.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -23,7 +23,7 @@ import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.io.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosFlashUI extends AltosUIDialog @@ -54,7 +54,7 @@ public class AltosFlashUI // Flash controller AltosProgrammer programmer; - private static String[] pair_programmed = { + private static final String[] pair_programmed_files = { "teleballoon", "telebt-v1", "teledongle-v0", @@ -67,20 +67,34 @@ public class AltosFlashUI "teleterra" }; + private static final String[] pair_programmed_devices = { + "TeleBalloon", + "TeleBT-v1", + "TeleDongle-v0", + "TeleFire", + "TeleMetrum-v0", + "TeleMetrum-v1", + "TeleMini", + "TeleNano", + "TeleShield", + "TeleTerra" + }; + private boolean is_pair_programmed() { if (file != null) { String name = file.getName(); - for (int i = 0; i < pair_programmed.length; i++) { - if (name.startsWith(pair_programmed[i])) + for (int i = 0; i < pair_programmed_files.length; i++) { + if (name.startsWith(pair_programmed_files[i])) return true; } } if (device != null) { - if (!device.matchProduct(AltosLib.product_altusmetrum) && - (device.matchProduct(AltosLib.product_teledongle) || - device.matchProduct(AltosLib.product_telebt))) - return true; + String name = device.toString(); + for (int i = 0; i < pair_programmed_devices.length; i++) { + if (name.startsWith(pair_programmed_devices[i])) + return true; + } } return false; } diff --git a/altosuilib/AltosFlightInfoTableModel.java b/altosuilib/AltosFlightInfoTableModel.java index f9d7d180..e55004e4 100644 --- a/altosuilib/AltosFlightInfoTableModel.java +++ b/altosuilib/AltosFlightInfoTableModel.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import javax.swing.table.*; diff --git a/altosuilib/AltosFlightStatsTable.java b/altosuilib/AltosFlightStatsTable.java index 8a686646..9c613365 100644 --- a/altosuilib/AltosFlightStatsTable.java +++ b/altosuilib/AltosFlightStatsTable.java @@ -15,12 +15,12 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import javax.swing.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosFlightStatsTable extends JComponent implements AltosFontListener { GridBagLayout layout; diff --git a/altosuilib/AltosGraph.java b/altosuilib/AltosGraph.java index d7739228..be013d57 100644 --- a/altosuilib/AltosGraph.java +++ b/altosuilib/AltosGraph.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosGraphDataPoint.java b/altosuilib/AltosGraphDataPoint.java index 47989d0e..30d436ae 100644 --- a/altosuilib/AltosGraphDataPoint.java +++ b/altosuilib/AltosGraphDataPoint.java @@ -15,9 +15,9 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosGraphDataPoint implements AltosUIDataPoint { diff --git a/altosuilib/AltosGraphDataSet.java b/altosuilib/AltosGraphDataSet.java index b9c9d2a8..7902f407 100644 --- a/altosuilib/AltosGraphDataSet.java +++ b/altosuilib/AltosGraphDataSet.java @@ -15,12 +15,12 @@ * 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; diff --git a/altosuilib/AltosInfoTable.java b/altosuilib/AltosInfoTable.java index 89a656c9..d87c38e2 100644 --- a/altosuilib/AltosInfoTable.java +++ b/altosuilib/AltosInfoTable.java @@ -15,13 +15,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.table.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener { private AltosFlightInfoTableModel model; @@ -137,6 +137,8 @@ public class AltosInfoTable extends JTable implements AltosFlightDisplay, Hierar if (state != null) { if (state.device_type != AltosLib.MISSING) info_add_row(0, "Device", "%s", AltosLib.product_name(state.device_type)); + else if (state.product != null) + info_add_row(0, "Device", "%s", state.product); if (state.altitude() != AltosLib.MISSING) info_add_row(0, "Altitude", "%6.0f m", state.altitude()); if (state.ground_altitude() != AltosLib.MISSING) diff --git a/altosuilib/AltosLed.java b/altosuilib/AltosLed.java index fa33c4b6..b47aa493 100644 --- a/altosuilib/AltosLed.java +++ b/altosuilib/AltosLed.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import javax.swing.*; diff --git a/altosuilib/AltosLights.java b/altosuilib/AltosLights.java index 05d06ac4..8cbd1d10 100644 --- a/altosuilib/AltosLights.java +++ b/altosuilib/AltosLights.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import javax.swing.*; diff --git a/altosuilib/AltosPositionListener.java b/altosuilib/AltosPositionListener.java index 1274a64a..0c900787 100644 --- a/altosuilib/AltosPositionListener.java +++ b/altosuilib/AltosPositionListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public interface AltosPositionListener { public void position_changed(int position); diff --git a/altosuilib/AltosRomconfigUI.java b/altosuilib/AltosRomconfigUI.java index 99e4d004..257674dc 100644 --- a/altosuilib/AltosRomconfigUI.java +++ b/altosuilib/AltosRomconfigUI.java @@ -15,12 +15,12 @@ * 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 diff --git a/altosuilib/AltosScanUI.java b/altosuilib/AltosScanUI.java index 5a7e21b1..e2efd4b3 100644 --- a/altosuilib/AltosScanUI.java +++ b/altosuilib/AltosScanUI.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -25,7 +25,7 @@ import java.io.*; import java.util.*; import java.text.*; import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; class AltosScanResult { String callsign; @@ -62,9 +62,18 @@ class AltosScanResult { rate = in_rate; } - public boolean equals(AltosScanResult other) { + public int hashCode() { + return serial ^ frequency.hashCode() ^ telemetry ^ rate; + } + + public boolean equals(Object o) { + if (o == null) + return false; + if (!(o instanceof AltosScanResult)) + return false; + AltosScanResult other = (AltosScanResult) o; return (serial == other.serial && - frequency.frequency == other.frequency.frequency && + frequency.equals(other.frequency) && telemetry == other.telemetry && rate == other.rate); } diff --git a/altosuilib/AltosSerial.java b/altosuilib/AltosSerial.java index 95815a7b..ff8d900a 100644 --- a/altosuilib/AltosSerial.java +++ b/altosuilib/AltosSerial.java @@ -19,13 +19,13 @@ * 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.*; /* diff --git a/altosuilib/AltosSerialInUseException.java b/altosuilib/AltosSerialInUseException.java index 0487e146..2b198aec 100644 --- a/altosuilib/AltosSerialInUseException.java +++ b/altosuilib/AltosSerialInUseException.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public class AltosSerialInUseException extends Exception { public AltosDevice device; diff --git a/altosuilib/AltosUIAxis.java b/altosuilib/AltosUIAxis.java index 89f5493b..155e7ed6 100644 --- a/altosuilib/AltosUIAxis.java +++ b/altosuilib/AltosUIAxis.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUIConfigure.java b/altosuilib/AltosUIConfigure.java index b022aeec..146acda8 100644 --- a/altosuilib/AltosUIConfigure.java +++ b/altosuilib/AltosUIConfigure.java @@ -15,13 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; import java.beans.*; import javax.swing.*; import javax.swing.event.*; +import org.altusmetrum.altoslib_8.*; class DelegatingRenderer implements ListCellRenderer<Object> { @@ -269,8 +270,8 @@ public class AltosUIConfigure row++; pane.add(new JLabel (String.format("AltOS version %s (%smaps key)", - AltosUIVersion.version, - AltosUIVersion.has_google_maps_api_key() ? "" : "no ")), + AltosVersion.version, + AltosVersion.has_google_maps_api_key() ? "" : "no ")), constraints(0, 3)); row++; diff --git a/altosuilib/AltosUIDataMissing.java b/altosuilib/AltosUIDataMissing.java index a0e41fef..39b8f900 100644 --- a/altosuilib/AltosUIDataMissing.java +++ b/altosuilib/AltosUIDataMissing.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public class AltosUIDataMissing extends Exception { public int id; diff --git a/altosuilib/AltosUIDataPoint.java b/altosuilib/AltosUIDataPoint.java index 82ce862f..793fabfb 100644 --- a/altosuilib/AltosUIDataPoint.java +++ b/altosuilib/AltosUIDataPoint.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public interface AltosUIDataPoint { public abstract double x() throws AltosUIDataMissing; diff --git a/altosuilib/AltosUIDataSet.java b/altosuilib/AltosUIDataSet.java index 6293911d..b7472000 100644 --- a/altosuilib/AltosUIDataSet.java +++ b/altosuilib/AltosUIDataSet.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public interface AltosUIDataSet { public abstract String name(); diff --git a/altosuilib/AltosUIDialog.java b/altosuilib/AltosUIDialog.java index 77e549c4..cfbf2cd3 100644 --- a/altosuilib/AltosUIDialog.java +++ b/altosuilib/AltosUIDialog.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; diff --git a/altosuilib/AltosUIEnable.java b/altosuilib/AltosUIEnable.java index 481e5b87..b4d8d81e 100644 --- a/altosuilib/AltosUIEnable.java +++ b/altosuilib/AltosUIEnable.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -23,7 +23,7 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUIFlightTab.java b/altosuilib/AltosUIFlightTab.java index ea4f0cb0..a2a3e367 100644 --- a/altosuilib/AltosUIFlightTab.java +++ b/altosuilib/AltosUIFlightTab.java @@ -15,13 +15,13 @@ * 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; diff --git a/altosuilib/AltosUIFrame.java b/altosuilib/AltosUIFrame.java index 39b1eb73..79015103 100644 --- a/altosuilib/AltosUIFrame.java +++ b/altosuilib/AltosUIFrame.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -207,6 +207,62 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi } } } + + int row = 0; + + public void next_row() { + row++; + } + + int inset = 0; + + public void set_inset(int i) { + inset = i; + } + + public GridBagConstraints constraints (int x, int width, int fill, int anchor, double weightx, double weighty) { + return new GridBagConstraints(x, /* x */ + row, /* y */ + width, /* width */ + 1, /* height */ + weightx, /* weightx */ + weighty, /* weighty */ + anchor, /* anchor */ + fill, /* fill */ + new Insets(inset,inset,inset,inset), /* insets */ + 0, /* ipadx */ + 0); /* ipady */ + } + + public GridBagConstraints constraints (int x, int width, int fill, int anchor) { + double weightx = 0; + double weighty = 0; + + if (fill == GridBagConstraints.NONE) { + weightx = 0; + weighty = 0; + } else if (fill == GridBagConstraints.HORIZONTAL) { + weightx = 1; + weighty = 0; + } else if (fill == GridBagConstraints.VERTICAL) { + weightx = 0; + weighty = 1; + } else if (fill == GridBagConstraints.BOTH) { + weightx = 1; + weighty = 1; + } + + return constraints (x, width, fill, anchor, weightx, weighty); + } + + public GridBagConstraints constraints (int x, int width, int fill) { + return constraints (x, width, fill, GridBagConstraints.WEST); + } + + public GridBagConstraints constraints(int x, int width) { + return constraints(x, width, GridBagConstraints.NONE); + } + void init() { AltosUIPreferences.register_ui_listener(this); AltosUIPreferences.register_position_listener(this); diff --git a/altosuilib/AltosUIFreqList.java b/altosuilib/AltosUIFreqList.java index 430069f5..9479087e 100644 --- a/altosuilib/AltosUIFreqList.java +++ b/altosuilib/AltosUIFreqList.java @@ -15,10 +15,10 @@ * 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> { diff --git a/altosuilib/AltosUIGraph.java b/altosuilib/AltosUIGraph.java index d20aa54b..a11978d0 100644 --- a/altosuilib/AltosUIGraph.java +++ b/altosuilib/AltosUIGraph.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUIGrapher.java b/altosuilib/AltosUIGrapher.java index fcd3546f..0fe685ef 100644 --- a/altosuilib/AltosUIGrapher.java +++ b/altosuilib/AltosUIGrapher.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUIImage.java b/altosuilib/AltosUIImage.java new file mode 100644 index 00000000..77743741 --- /dev/null +++ b/altosuilib/AltosUIImage.java @@ -0,0 +1,38 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altoslib_8; + +import javax.swing.*; +import javax.imageio.ImageIO; +import java.awt.image.*; +import java.awt.*; +import java.io.*; +import java.net.*; + +public class AltosUIImage implements AltosImage { + public Image image; + + /* Discard storage for image */ + public void flush() { + image.flush(); + } + + public AltosUIImage(Image image) { + this.image = image; + } +} diff --git a/altosuilib/AltosUIIndicator.java b/altosuilib/AltosUIIndicator.java index f2e77218..0a940d97 100644 --- a/altosuilib/AltosUIIndicator.java +++ b/altosuilib/AltosUIIndicator.java @@ -15,11 +15,11 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public abstract class AltosUIIndicator implements AltosFontListener, AltosUnitsListener { JLabel label; diff --git a/altosuilib/AltosUILib.java b/altosuilib/AltosUILib.java index 2fa6cbd6..2fc20317 100644 --- a/altosuilib/AltosUILib.java +++ b/altosuilib/AltosUILib.java @@ -15,12 +15,12 @@ * 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 { diff --git a/altosuilib/AltosUIListener.java b/altosuilib/AltosUIListener.java index 9eec0b24..5b49e7da 100644 --- a/altosuilib/AltosUIListener.java +++ b/altosuilib/AltosUIListener.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; public interface AltosUIListener { public void ui_changed(String look_and_feel); diff --git a/altosuilib/AltosUIMap.java b/altosuilib/AltosUIMap.java deleted file mode 100644 index 5c6d5bdf..00000000 --- a/altosuilib/AltosUIMap.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns <aj@erisian.com.au> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import java.io.*; -import java.lang.Math; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener { - - static final int px_size = 512; - - static final int maptype_hybrid = 0; - static final int maptype_roadmap = 1; - static final int maptype_satellite = 2; - static final int maptype_terrain = 3; - static final int maptype_default = maptype_hybrid; - - static final String[] maptype_names = { - "hybrid", - "roadmap", - "satellite", - "terrain" - }; - - public static final String[] maptype_labels = { - "Hybrid", - "Roadmap", - "Satellite", - "Terrain" - }; - - public static final Color stateColors[] = { - Color.WHITE, // startup - Color.WHITE, // idle - Color.WHITE, // pad - Color.RED, // boost - Color.PINK, // fast - Color.YELLOW, // coast - Color.CYAN, // drogue - Color.BLUE, // main - Color.BLACK, // landed - Color.BLACK, // invalid - Color.CYAN, // stateless - }; - - public void reset() { - // nothing - } - - public void font_size_changed(int font_size) { - view.set_font(); - } - - public void units_changed(boolean imperial_units) { - view.set_units(); - } - - JLabel zoom_label; - - private void set_zoom_label() { - zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom)); - } - - public void zoom_changed(int zoom) { - set_zoom_label(); - } - - public void set_zoom(int zoom) { - view.set_zoom(zoom); - } - - public int get_zoom() { - return view.zoom(); - } - - public void set_maptype(int type) { - view.set_maptype(type); - maptype_combo.setSelectedIndex(type); - } - - public void show(AltosState state, AltosListenerState listener_state) { - view.show(state, listener_state); - } - - public void centre(double lat, double lon) { - view.centre(lat, lon); - } - - public void centre(AltosState state) { - if (!state.gps.locked && state.gps.nsat < 4) - return; - centre(state.gps.lat, state.gps.lon); - } - - public void add_mark(double lat, double lon, int state) { - view.add_mark(lat, lon, state); - } - - public void clear_marks() { - view.clear_marks(); - } - - AltosUIMapView view; - - private GridBagLayout layout = new GridBagLayout(); - - JComboBox<String> maptype_combo; - - public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) { - view.set_load_params(lat, lon, radius, listener); - } - - public boolean all_fetched() { - return view.all_fetched(); - } - - public static void prefetch_maps(double lat, double lon) { - } - - public String getName() { - return "Map"; - } - - public AltosUIMap() { - - view = new AltosUIMapView(); - - view.setPreferredSize(new Dimension(500,500)); - view.setVisible(true); - view.setEnabled(true); - view.add_zoom_listener(this); - - GridBagLayout my_layout = new GridBagLayout(); - - setLayout(my_layout); - - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.BOTH; - c.gridx = 0; - c.gridy = 0; - c.gridwidth = 1; - c.gridheight = 10; - c.weightx = 1; - c.weighty = 1; - add(view, c); - - int y = 0; - - zoom_label = new JLabel("", JLabel.CENTER); - set_zoom_label(); - - c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; - c.gridy = y++; - c.weightx = 0; - c.weighty = 0; - add(zoom_label, c); - - JButton zoom_reset = new JButton("0"); - zoom_reset.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - set_zoom(view.default_zoom); - } - }); - - c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; - c.gridy = y++; - c.weightx = 0; - c.weighty = 0; - add(zoom_reset, c); - - JButton zoom_in = new JButton("+"); - zoom_in.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - set_zoom(get_zoom() + 1); - } - }); - - c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; - c.gridy = y++; - c.weightx = 0; - c.weighty = 0; - add(zoom_in, c); - - JButton zoom_out = new JButton("-"); - zoom_out.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - set_zoom(get_zoom() - 1); - } - }); - c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; - c.gridy = y++; - c.weightx = 0; - c.weighty = 0; - add(zoom_out, c); - - maptype_combo = new JComboBox<String>(maptype_labels); - - maptype_combo.setEditable(false); - maptype_combo.setMaximumRowCount(maptype_combo.getItemCount()); - maptype_combo.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - view.set_maptype(maptype_combo.getSelectedIndex()); - } - }); - - c = new GridBagConstraints(); - c.anchor = GridBagConstraints.CENTER; - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 1; - c.gridy = y++; - c.weightx = 0; - c.weighty = 0; - add(maptype_combo, c); - } -} diff --git a/altosuilib/AltosUIMapCache.java b/altosuilib/AltosUIMapCache.java deleted file mode 100644 index 73401e3c..00000000 --- a/altosuilib/AltosUIMapCache.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns <aj@erisian.com.au> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import javax.swing.*; -import javax.imageio.ImageIO; -import java.awt.image.*; -import java.awt.*; -import java.io.*; -import java.net.*; - -public class AltosUIMapCache implements AltosUIMapCacheListener { - static final int success = 0; - static final int loading = 1; - static final int failed = 2; - static final int bad_request = 3; - static final int forbidden = 4; - - int min_cache_size; /* configured minimum cache size */ - int cache_size; /* current cache size */ - int requested_cache_size; /* cache size computed by application */ - - private Object fetch_lock = new Object(); - private Object cache_lock = new Object(); - - AltosUIMapImage[] images = new AltosUIMapImage[cache_size]; - - long used; - - public void set_cache_size(int new_size) { - - requested_cache_size = new_size; - - if (new_size < min_cache_size) - new_size = min_cache_size; - - if (new_size == cache_size) - return; - - synchronized(cache_lock) { - AltosUIMapImage[] new_images = new AltosUIMapImage[new_size]; - - for (int i = 0; i < cache_size; i++) { - if (i < new_size) - new_images[i] = images[i]; - else if (images[i] != null) - images[i].flush(); - } - images = new_images; - cache_size = new_size; - } - } - - public Image get(AltosUIMapTile tile, AltosUIMapStore store, int width, int height) { - int oldest = -1; - long age = used; - - synchronized(cache_lock) { - AltosUIMapImage image = null; - for (int i = 0; i < cache_size; i++) { - image = images[i]; - - if (image == null) { - oldest = i; - break; - } - if (store.equals(image.store)) { - image.used = used++; - return image.image; - } - if (image.used < age) { - oldest = i; - age = image.used; - } - } - - try { - image = new AltosUIMapImage(tile, store); - image.used = used++; - if (images[oldest] != null) - images[oldest].flush(); - - images[oldest] = image; - - if (image.image == null) - tile.set_status(loading); - else - tile.set_status(success); - - return image.image; - } catch (IOException e) { - tile.set_status(failed); - return null; - } - } - } - - public void map_cache_changed(int map_cache) { - min_cache_size = map_cache; - - set_cache_size(requested_cache_size); - } - - public void dispose() { - AltosUIPreferences.unregister_map_cache_listener(this); - - for (int i = 0; i < cache_size; i++) { - AltosUIMapImage image = images[i]; - - if (image != null) - image.flush(); - } - } - - public AltosUIMapCache() { - min_cache_size = AltosUIPreferences.map_cache(); - - set_cache_size(0); - - AltosUIPreferences.register_map_cache_listener(this); - } -} diff --git a/altosuilib/AltosUIMapImage.java b/altosuilib/AltosUIMapImage.java deleted file mode 100644 index 97220a40..00000000 --- a/altosuilib/AltosUIMapImage.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns <aj@erisian.com.au> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import javax.swing.*; -import javax.imageio.ImageIO; -import java.awt.image.*; -import java.awt.*; -import java.io.*; -import java.net.*; - -public class AltosUIMapImage implements AltosUIMapStoreListener { - static final long google_maps_ratelimit_ms = 1200; - // Google limits static map queries to 50 per minute per IP, so - // each query should take at least 1.2 seconds. - - static final int success = 0; - static final int loading = 1; - static final int failed = 2; - static final int bad_request = 3; - static final int forbidden = 4; - - static long forbidden_time; - static boolean forbidden_set = false; - static final long forbidden_interval = 60l * 1000l * 1000l * 1000l; - - AltosUIMapTile tile; /* Notify when image has been loaded */ - Image image; - AltosUIMapStore store; - long used; - - class loader implements Runnable { - public void run() { - if (image != null) - tile.notify_image(image); - try { - image = ImageIO.read(store.file); - } catch (Exception ex) { - } - if (image == null) - tile.set_status(failed); - else - tile.set_status(success); - tile.notify_image(image); - } - } - - private void load() { - loader l = new loader(); - Thread lt = new Thread(l); - lt.start(); - } - - public void flush() { - if (image != null) { - image.flush(); - image = null; - } - } - - public boolean has_map() { - return store.status() == AltosUIMapStore.success; - } - - public synchronized void notify_store(AltosUIMapStore store, int status) { - switch (status) { - case AltosUIMapStore.loading: - break; - case AltosUIMapStore.success: - load(); - break; - default: - tile.set_status(status); - tile.notify_image(null); - } - } - - public AltosUIMapImage(AltosUIMapTile tile, AltosUIMapStore store) throws IOException { - this.tile = tile; - this.image = null; - this.store = store; - this.used = 0; - - int status = store.status(); - switch (status) { - case AltosUIMapStore.loading: - store.add_listener(this); - break; - case AltosUIMapStore.success: - load(); - break; - default: - tile.set_status(status); - tile.notify_image(null); - break; - } - } -} diff --git a/altosuilib/AltosUIMapLine.java b/altosuilib/AltosUIMapLine.java deleted file mode 100644 index 2634f843..00000000 --- a/altosuilib/AltosUIMapLine.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright © 2014 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import java.io.*; -import java.lang.Math; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMapLine { - AltosUILatLon start, end; - - private Font font = null; - static public int stroke_width = 6; - - public void set_font(Font font) { - this.font = font; - } - - private AltosUILatLon lat_lon(MouseEvent e, AltosUIMapTransform t) { - return t.screen_lat_lon(e.getPoint()); - } - - public void dragged(MouseEvent e, AltosUIMapTransform t) { - end = lat_lon(e, t); - } - - public void pressed(MouseEvent e, AltosUIMapTransform t) { - start = lat_lon(e, t); - end = null; - } - - private String line_dist() { - String format; - AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon, - end.lat, end.lon); - double distance = g.distance; - - if (AltosConvert.imperial_units) { - distance = AltosConvert.meters_to_feet(distance); - if (distance < 10000) { - format = "%4.0fft"; - } else { - distance /= 5280; - if (distance < 10) - format = "%5.3fmi"; - else if (distance < 100) - format = "%5.2fmi"; - else if (distance < 1000) - format = "%5.1fmi"; - else - format = "%5.0fmi"; - } - } else { - if (distance < 10000) { - format = "%4.0fm"; - } else { - distance /= 1000; - if (distance < 100) - format = "%5.2fkm"; - else if (distance < 1000) - format = "%5.1fkm"; - else - format = "%5.0fkm"; - } - } - return String.format(format, distance); - } - - public void paint(Graphics2D g, AltosUIMapTransform t) { - - if (start == null || end == null) - return; - - g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - - Line2D.Double line = new Line2D.Double(t.screen(start), - t.screen(end)); - - g.setColor(Color.WHITE); - g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - g.draw(line); - - g.setColor(Color.BLUE); - g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - g.draw(line); - - String message = line_dist(); - Rectangle2D bounds; - bounds = font.getStringBounds(message, g.getFontRenderContext()); - - float x = (float) line.x1; - float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f; - - if (line.x1 < line.x2) { - x -= (float) bounds.getWidth() + 2.0f; - } else { - x += 2.0f; - } - - g.setFont(font); - g.setColor(Color.WHITE); - for (int dy = -2; dy <= 2; dy += 2) - for (int dx = -2; dx <= 2; dx += 2) - g.drawString(message, x + dx, y + dy); - g.setColor(Color.BLUE); - g.drawString(message, x, y); - } -} diff --git a/altosuilib/AltosUIMapMark.java b/altosuilib/AltosUIMapMark.java deleted file mode 100644 index b4b98efa..00000000 --- a/altosuilib/AltosUIMapMark.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2014 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import java.io.*; -import java.lang.Math; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMapMark { - - AltosUILatLon lat_lon; - int state; - - static public int stroke_width = 6; - - public void paint(Graphics2D g, AltosUIMapTransform t) { - - Point2D.Double pt = t.screen(lat_lon); - - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - - if (0 <= state && state < AltosUIMap.stateColors.length) - g.setColor(AltosUIMap.stateColors[state]); - else - g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]); - - g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); - g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); - g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); - } - - public AltosUIMapMark (double lat, double lon, int state) { - lat_lon = new AltosUILatLon(lat, lon); - this.state = state; - } -} diff --git a/altosuilib/AltosUIMapNew.java b/altosuilib/AltosUIMapNew.java new file mode 100644 index 00000000..016a202b --- /dev/null +++ b/altosuilib/AltosUIMapNew.java @@ -0,0 +1,538 @@ +/* + * Copyright © 2010 Anthony Towns <aj@erisian.com.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.altosuilib_8; + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import javax.swing.*; +import java.io.*; +import java.lang.Math; +import java.awt.geom.*; +import java.util.*; +import java.util.concurrent.*; +import javax.imageio.*; +import org.altusmetrum.altoslib_8.*; + +public class AltosUIMapNew extends JComponent implements AltosFlightDisplay, AltosMapInterface { + + AltosMap map; + Graphics2D g; + Font tile_font; + Font line_font; + + static Point2D.Double point2d(AltosPointDouble pt) { + return new Point2D.Double(pt.x, pt.y); + } + + static final AltosPointDouble point_double(Point pt) { + return new AltosPointDouble(pt.x, pt.y); + } + + class MapMark extends AltosMapMark { + public void paint(AltosMapTransform t) { + AltosPointDouble pt = t.screen(lat_lon); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + + if (0 <= state && state < AltosUIMapNew.stateColors.length) + g.setColor(AltosUIMapNew.stateColors[state]); + else + g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]); + + g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10); + g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40); + g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70); + } + + MapMark(double lat, double lon, int state) { + super(lat, lon, state); + } + } + + class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener { + + private VolatileImage create_back_buffer() { + return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight()); + } + + private void do_paint(Graphics my_g) { + g = (Graphics2D) my_g; + + map.paint(); + } + + public void paint(Graphics my_g) { + VolatileImage back_buffer = create_back_buffer(); + + Graphics2D top_g = (Graphics2D) my_g; + + do { + GraphicsConfiguration gc = getGraphicsConfiguration(); + int code = back_buffer.validate(gc); + if (code == VolatileImage.IMAGE_INCOMPATIBLE) + back_buffer = create_back_buffer(); + + Graphics g_back = back_buffer.getGraphics(); + g_back.setClip(top_g.getClip()); + do_paint(g_back); + g_back.dispose(); + + top_g.drawImage(back_buffer, 0, 0, this); + } while (back_buffer.contentsLost()); + back_buffer.flush(); + } + + public void repaint(AltosRectangle damage) { + repaint(damage.x, damage.y, damage.width, damage.height); + } + + private boolean is_drag_event(MouseEvent e) { + return e.getModifiers() == InputEvent.BUTTON1_MASK; + } + + /* MouseMotionListener methods */ + + public void mouseDragged(MouseEvent e) { + map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e)); + } + + public void mouseMoved(MouseEvent e) { + } + + /* MouseListener methods */ + public void mouseClicked(MouseEvent e) { + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e)); + } + + public void mouseReleased(MouseEvent e) { + } + + /* MouseWheelListener methods */ + + public void mouseWheelMoved(MouseWheelEvent e) { + int zoom_change = e.getWheelRotation(); + + map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y)); + } + + /* ComponentListener methods */ + + public void componentHidden(ComponentEvent e) { + } + + public void componentMoved(ComponentEvent e) { + } + + public void componentResized(ComponentEvent e) { + map.set_transform(); + } + + public void componentShown(ComponentEvent e) { + map.set_transform(); + } + + MapView() { + addComponentListener(this); + addMouseMotionListener(this); + addMouseListener(this); + addMouseWheelListener(this); + } + } + + class MapLine extends AltosMapLine { + + public void paint(AltosMapTransform t) { + + if (start == null || end == null) + return; + + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + Line2D.Double line = new Line2D.Double(point2d(t.screen(start)), + point2d(t.screen(end))); + + g.setColor(Color.WHITE); + g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + g.draw(line); + + g.setColor(Color.BLUE); + g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + g.draw(line); + + String message = line_dist(); + Rectangle2D bounds; + bounds = line_font.getStringBounds(message, g.getFontRenderContext()); + + float x = (float) line.x1; + float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f; + + if (line.x1 < line.x2) { + x -= (float) bounds.getWidth() + 2.0f; + } else { + x += 2.0f; + } + + g.setFont(line_font); + g.setColor(Color.WHITE); + for (int dy = -2; dy <= 2; dy += 2) + for (int dx = -2; dx <= 2; dx += 2) + g.drawString(message, x + dx, y + dy); + g.setColor(Color.BLUE); + g.drawString(message, x, y); + } + + public MapLine() { + } + } + + class MapPath extends AltosMapPath { + public void paint(AltosMapTransform t) { + Point2D.Double prev = null; + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); + + for (AltosMapPathPoint point : points) { + Point2D.Double cur = point2d(t.screen(point.lat_lon)); + if (prev != null) { + Line2D.Double line = new Line2D.Double (prev, cur); + Rectangle bounds = line.getBounds(); + + if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) { + if (0 <= point.state && point.state < AltosUIMapNew.stateColors.length) + g.setColor(AltosUIMapNew.stateColors[point.state]); + else + g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]); + + g.draw(line); + } + } + prev = cur; + } + } + } + + class MapTile extends AltosMapTile { + public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + super(listener, upper_left, center, zoom, maptype, px_size); + } + + public void paint(AltosMapTransform t) { + + AltosPointDouble point_double = t.screen(upper_left); + Point point = new Point((int) (point_double.x + 0.5), + (int) (point_double.y + 0.5)); + + if (!g.hitClip(point.x, point.y, px_size, px_size)) + return; + + AltosImage altos_image = cache.get(this, store, px_size, px_size); + + AltosUIImage ui_image = (AltosUIImage) altos_image; + + Image image = null; + + if (ui_image != null) + image = ui_image.image; + + if (image != null) { + g.drawImage(image, point.x, point.y, null); + } else { + g.setColor(Color.GRAY); + g.fillRect(point.x, point.y, px_size, px_size); + + if (t.has_location()) { + String message = null; + switch (status) { + case AltosMapTile.loading: + message = "Loading..."; + break; + case AltosMapTile.bad_request: + message = "Internal error"; + break; + case AltosMapTile.failed: + message = "Network error, check connection"; + break; + case AltosMapTile.forbidden: + message = "Too many requests, try later"; + break; + } + if (message != null && tile_font != null) { + g.setFont(tile_font); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext()); + + float x = px_size / 2.0f; + float y = px_size / 2.0f; + x = x - (float) bounds.getWidth() / 2.0f; + y = y + (float) bounds.getHeight() / 2.0f; + g.setColor(Color.BLACK); + g.drawString(message, (float) point_double.x + x, (float) point_double.y + y); + } + } + } + } + } + + public static final Color stateColors[] = { + Color.WHITE, // startup + Color.WHITE, // idle + Color.WHITE, // pad + Color.RED, // boost + Color.PINK, // fast + Color.YELLOW, // coast + Color.CYAN, // drogue + Color.BLUE, // main + Color.BLACK, // landed + Color.BLACK, // invalid + Color.CYAN, // stateless + }; + + /* AltosMapInterface functions */ + + public AltosMapPath new_path() { + return new MapPath(); + } + + public AltosMapLine new_line() { + return new MapLine(); + } + + public AltosImage load_image(File file) throws Exception { + return new AltosUIImage(ImageIO.read(file)); + } + + public AltosMapMark new_mark(double lat, double lon, int state) { + return new MapMark(lat, lon, state); + } + + public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) { + return new MapTile(listener, upper_left, center, zoom, maptype, px_size); + } + + public int width() { + return view.getWidth(); + } + + public int height() { + return view.getHeight(); + } + + public void repaint() { + view.repaint(); + } + + public void repaint(AltosRectangle damage) { + view.repaint(damage); + } + + public void set_zoom_label(String label) { + zoom_label.setText(label); + } + + public void select_object(AltosLatLon latlon) { + debug("select at %f,%f\n", latlon.lat, latlon.lon); + } + + public void debug(String format, Object ... arguments) { + System.out.printf(format, arguments); + } + + + /* AltosFlightDisplay interface */ + + public void set_font() { + tile_font = AltosUILib.value_font; + line_font = AltosUILib.status_font; + } + + public void font_size_changed(int font_size) { + set_font(); + repaint(); + } + + public void units_changed(boolean imperial_units) { + repaint(); + } + + JLabel zoom_label; + + public void set_maptype(int type) { + map.set_maptype(type); + maptype_combo.setSelectedIndex(type); + } + + /* AltosUIMapPreload functions */ + + public void set_zoom(int zoom) { + map.set_zoom(zoom); + } + + public void add_mark(double lat, double lon, int status) { + map.add_mark(lat, lon, status); + } + + public void clear_marks() { + map.clear_marks(); + } + + /* AltosFlightDisplay interface */ + public void reset() { + // nothing + } + + public void show(AltosState state, AltosListenerState listener_state) { + map.show(state, listener_state); + } + + public String getName() { + return "Map"; + } + + /* AltosGraphUI interface */ + public void centre(AltosState state) { + map.centre(state); + } + + /* internal layout bits */ + private GridBagLayout layout = new GridBagLayout(); + + JComboBox<String> maptype_combo; + + MapView view; + + public AltosUIMapNew() { + + set_font(); + + view = new MapView(); + + view.setPreferredSize(new Dimension(500,500)); + view.setVisible(true); + view.setEnabled(true); + + GridBagLayout my_layout = new GridBagLayout(); + + setLayout(my_layout); + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 10; + c.weightx = 1; + c.weighty = 1; + add(view, c); + + int y = 0; + + zoom_label = new JLabel("", JLabel.CENTER); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_label, c); + + JButton zoom_reset = new JButton("0"); + zoom_reset.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + map.set_zoom(map.default_zoom); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_reset, c); + + JButton zoom_in = new JButton("+"); + zoom_in.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + map.set_zoom(map.get_zoom() + 1); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_in, c); + + JButton zoom_out = new JButton("-"); + zoom_out.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + map.set_zoom(map.get_zoom() - 1); + } + }); + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(zoom_out, c); + + maptype_combo = new JComboBox<String>(map.maptype_labels); + + maptype_combo.setEditable(false); + maptype_combo.setMaximumRowCount(maptype_combo.getItemCount()); + maptype_combo.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + map.set_maptype(maptype_combo.getSelectedIndex()); + } + }); + + c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.fill = GridBagConstraints.HORIZONTAL; + c.gridx = 1; + c.gridy = y++; + c.weightx = 0; + c.weighty = 0; + add(maptype_combo, c); + + map = new AltosMap(this); + } +} diff --git a/altosuilib/AltosUIMapPath.java b/altosuilib/AltosUIMapPath.java deleted file mode 100644 index e77af580..00000000 --- a/altosuilib/AltosUIMapPath.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2014 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import java.io.*; -import java.lang.Math; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -class PathPoint { - AltosUILatLon lat_lon; - int state; - - public PathPoint(AltosUILatLon lat_lon, int state) { - this.lat_lon = lat_lon; - this.state = state; - } - - public boolean equals(PathPoint other) { - if (other == null) - return false; - - return lat_lon.equals(other.lat_lon) && state == other.state; - } -} - -public class AltosUIMapPath { - - LinkedList<PathPoint> points = new LinkedList<PathPoint>(); - PathPoint last_point = null; - - static public int stroke_width = 6; - - public void paint(Graphics2D g, AltosUIMapTransform t) { - Point2D.Double prev = null; - - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); - - for (PathPoint point : points) { - Point2D.Double cur = t.screen(point.lat_lon); - if (prev != null) { - Line2D.Double line = new Line2D.Double (prev, cur); - Rectangle bounds = line.getBounds(); - - if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) { - if (0 <= point.state && point.state < AltosUIMap.stateColors.length) - g.setColor(AltosUIMap.stateColors[point.state]); - else - g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]); - - g.draw(line); - } - } - prev = cur; - } - } - - public AltosUIMapRectangle add(double lat, double lon, int state) { - PathPoint point = new PathPoint(new AltosUILatLon (lat, lon), state); - AltosUIMapRectangle rect = null; - - if (!point.equals(last_point)) { - if (last_point != null) - rect = new AltosUIMapRectangle(last_point.lat_lon, point.lat_lon); - points.add (point); - last_point = point; - } - return rect; - } - - public void clear () { - points = new LinkedList<PathPoint>(); - } -} diff --git a/altosuilib/AltosUIMapPreload.java b/altosuilib/AltosUIMapPreloadNew.java index e82b6c60..2d4d45b2 100644 --- a/altosuilib/AltosUIMapPreload.java +++ b/altosuilib/AltosUIMapPreloadNew.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; @@ -26,7 +26,7 @@ import java.text.*; import java.lang.Math; import java.net.URL; import java.net.URLConnection; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; class AltosUIMapPos extends Box { AltosUIFrame owner; @@ -53,33 +53,33 @@ class AltosUIMapPos extends Box { hemi.setSelectedIndex(h); } - public double get_value() throws NumberFormatException { + 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 = Double.parseDouble(d_t); - } catch (NumberFormatException ne) { + 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 ne; + throw pe; } try { if (m_t.equals("")) m = 0; else - m = Double.parseDouble(m_t); - } catch (NumberFormatException ne) { + 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 ne; + throw pe; } v = d + m/60.0; if (h == 1) @@ -118,250 +118,87 @@ class AltosUIMapPos extends Box { } } -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 { +public class AltosUIMapPreloadNew extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener { AltosUIFrame owner; - AltosUIMap map; - AltosUIMapCache cache = new AltosUIMapCache(); + AltosUIMapNew map; AltosUIMapPos lat; AltosUIMapPos lon; JProgressBar pbar; - int pbar_max; - int pbar_cur; - AltosUISites sites; + AltosMapLoader loader; + JLabel site_list_label; - JComboBox<AltosUISite> site_list; + JComboBox<AltosLaunchSite> site_list; JToggleButton load_button; boolean loading; JButton close_button; - JCheckBox[] maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1]; + JCheckBox[] maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1]; JComboBox<Integer> min_zoom; JComboBox<Integer> max_zoom; - JComboBox<Integer> radius; + JComboBox<Double> 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" }; + 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; - 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); - } - } + static final String[] lat_hemi_names = { "N", "S" }; + static final String[] lon_hemi_names = { "E", "W" }; 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; + /* 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); + } + }); } - 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(); + 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); + } + }); } - 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(); + public void loader_done(int max) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + pbar.setValue(0); + pbar.setString(""); + load_button.setSelected(false); + loading = false; + } + }); } - /* 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 void debug(String format, Object ... arguments) { + System.out.printf(format, arguments); } - public AltosUIMapCache cache() { return cache; } - public void set_sites() { - int i = 1; - for (AltosUISite site : sites.sites) { - site_list.insertItemAt(site, i); - i++; - } + 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) { @@ -369,8 +206,8 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I if (state == ItemEvent.SELECTED) { Object o = e.getItem(); - if (o instanceof AltosUISite) { - AltosUISite site = (AltosUISite) o; + if (o instanceof AltosLaunchSite) { + AltosLaunchSite site = (AltosLaunchSite) o; lat.set_value(site.latitude); lon.set_value(site.longitude); } @@ -388,21 +225,39 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I try { latitude = lat.get_value(); longitude = lon.get_value(); - min_z = (Integer) min_zoom.getSelectedItem(); - max_z = (Integer) max_zoom.getSelectedItem(); + int min_z = (Integer) min_zoom.getSelectedItem(); + int max_z = (Integer) max_zoom.getSelectedItem(); if (max_z < min_z) max_z = min_z; - r = (Integer) radius.getSelectedItem(); + Double r = (Double) radius.getSelectedItem(); + + if (AltosPreferences.imperial_units()) + r = AltosConvert.distance.inverse(r); + else + r = r * 1000; loading = true; - } catch (NumberFormatException ne) { + + loader.load(latitude, longitude, min_z, max_z, r, all_types()); + } catch (ParseException pe) { load_button.setSelected(false); } - start_load(); } } } - public AltosUIMapPreload(AltosUIFrame in_owner) { + 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(); @@ -413,7 +268,9 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I pane.setLayout(new GridBagLayout()); - map = new AltosUIMap(); + map = new AltosUIMapNew(); + + loader = new AltosMapLoader(map.map, this); c.fill = GridBagConstraints.BOTH; c.anchor = GridBagConstraints.CENTER; @@ -461,10 +318,10 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I pane.add(site_list_label, c); - site_list = new JComboBox<AltosUISite>(new AltosUISite[] { new AltosUISite("Site List", 0, 0) }); + site_list = new JComboBox<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) }); site_list.addItemListener(this); - sites = new AltosUISites(this); + new AltosLaunchSites(this); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.CENTER; @@ -555,9 +412,9 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I 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); + 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; @@ -588,13 +445,21 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I c.gridy = 3; pane.add(max_zoom, c); - JLabel radius_label = new JLabel("Tile Radius"); + 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); - radius = new JComboBox<Integer>(radii); - radius.setSelectedItem(radii[4]); + 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; diff --git a/altosuilib/AltosUIMapTile.java b/altosuilib/AltosUIMapTile.java deleted file mode 100644 index afd1bbc6..00000000 --- a/altosuilib/AltosUIMapTile.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright © 2010 Anthony Towns <aj@erisian.com.au> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.image.*; -import javax.swing.*; -import javax.imageio.*; -import java.awt.geom.*; -import java.io.*; -import java.util.*; -import java.awt.RenderingHints.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMapTile { - AltosUIMapTileListener listener; - AltosUILatLon upper_left, center; - int px_size; - int zoom; - int maptype; - AltosUIMapStore store; - AltosUIMapCache cache; - int status; - - private File map_file() { - double lat = center.lat; - double lon = center.lon; - char chlat = lat < 0 ? 'S' : 'N'; - char chlon = lon < 0 ? 'W' : 'E'; - - if (lat < 0) lat = -lat; - if (lon < 0) lon = -lon; - String maptype_string = String.format("%s-", AltosUIMap.maptype_names[maptype]); - String format_string; - if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain) - format_string = "jpg"; - else - format_string = "png"; - return new File(AltosUIPreferences.mapdir(), - String.format("map-%c%.6f,%c%.6f-%s%d.%s", - chlat, lat, chlon, lon, maptype_string, zoom, format_string)); - } - - private String map_url() { - String format_string; - if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain) - format_string = "jpg"; - else - format_string = "png32"; - - if (AltosUIVersion.has_google_maps_api_key()) - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s&key=%s", - center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string, AltosUIVersion.google_maps_api_key); - else - return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s", - center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string); - } - private Font font = null; - - public void set_font(Font font) { - this.font = font; - } - - int painting_serial; - int painted_serial; - - Image image; - - public void paint_graphics(Graphics2D g2d, AltosUIMapTransform t, int serial) { - if (serial < painted_serial) - return; - - Point2D.Double point_double = t.screen(upper_left); - Point point = new Point((int) (point_double.x + 0.5), - (int) (point_double.y + 0.5)); - - painted_serial = serial; - - if (!g2d.hitClip(point.x, point.y, px_size, px_size)) - return; - - if (image != null) { - g2d.drawImage(image, point.x, point.y, null); - image = null; - } else { - g2d.setColor(Color.GRAY); - g2d.fillRect(point.x, point.y, px_size, px_size); - - if (t.has_location()) { - String message = null; - switch (status) { - case AltosUIMapCache.loading: - message = "Loading..."; - break; - case AltosUIMapCache.bad_request: - message = "Internal error"; - break; - case AltosUIMapCache.failed: - message = "Network error, check connection"; - break; - case AltosUIMapCache.forbidden: - message = "Too many requests, try later"; - break; - } - if (message != null && font != null) { - g2d.setFont(font); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - Rectangle2D bounds = font.getStringBounds(message, g2d.getFontRenderContext()); - - float x = px_size / 2.0f; - float y = px_size / 2.0f; - x = x - (float) bounds.getWidth() / 2.0f; - y = y + (float) bounds.getHeight() / 2.0f; - g2d.setColor(Color.BLACK); - g2d.drawString(message, (float) point_double.x + x, (float) point_double.y + y); - } - } - } - } - - public void set_status(int status) { - this.status = status; - listener.notify_tile(this, status); - } - - public void notify_image(Image image) { - listener.notify_tile(this, status); - } - - public void paint(Graphics g, AltosUIMapTransform t) { - Graphics2D g2d = (Graphics2D) g; - boolean queued = false; - - Point2D.Double point = t.screen(upper_left); - - if (!g.hitClip((int) (point.x + 0.5), (int) (point.y + 0.5), px_size, px_size)) - return; - - ++painting_serial; - - if (image == null && t.has_location()) - image = cache.get(this, store, px_size, px_size); - - paint_graphics(g2d, t, painting_serial); - } - - public int store_status() { - return store.status(); - } - - public void add_store_listener(AltosUIMapStoreListener listener) { - store.add_listener(listener); - } - - public void remove_store_listener(AltosUIMapStoreListener listener) { - store.remove_listener(listener); - } - - public AltosUIMapTile(AltosUIMapTileListener listener, AltosUILatLon upper_left, AltosUILatLon center, int zoom, int maptype, int px_size, Font font) { - this.listener = listener; - this.upper_left = upper_left; - cache = listener.cache(); - - while (center.lon < -180.0) - center.lon += 360.0; - while (center.lon > 180.0) - center.lon -= 360.0; - - this.center = center; - this.zoom = zoom; - this.maptype = maptype; - this.px_size = px_size; - this.font = font; - status = AltosUIMapCache.loading; - store = AltosUIMapStore.get(map_url(), map_file()); - } -} diff --git a/altosuilib/AltosUIMapTransform.java b/altosuilib/AltosUIMapTransform.java deleted file mode 100644 index 25497403..00000000 --- a/altosuilib/AltosUIMapTransform.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright © 2014 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import java.io.*; -import java.lang.Math; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMapTransform { - - double scale_x, scale_y; - - double offset_x, offset_y; - - public AltosUILatLon lat_lon (Point2D.Double point) { - double lat, lon; - double rads; - - lon = point.x/scale_x; - rads = 2 * Math.atan(Math.exp(-point.y/scale_y)); - lat = Math.toDegrees(rads - Math.PI/2); - - return new AltosUILatLon(lat,lon); - } - - public Point2D.Double screen_point(Point screen) { - return new Point2D.Double(screen.x + offset_x, screen.y + offset_y); - } - - public AltosUILatLon screen_lat_lon(Point screen) { - return lat_lon(screen_point(screen)); - } - - public Point2D.Double point(AltosUILatLon lat_lon) { - double x, y; - double e; - - x = lat_lon.lon * scale_x; - - e = Math.sin(Math.toRadians(lat_lon.lat)); - e = Math.max(e,-(1-1.0E-15)); - e = Math.min(e, 1-1.0E-15 ); - - y = 0.5*Math.log((1+e)/(1-e))*-scale_y; - - return new Point2D.Double(x, y); - } - - public Point2D.Double screen(Point2D.Double point) { - return new Point2D.Double(point.x - offset_x, point.y - offset_y); - } - - public Point screen(Point point) { - return new Point((int) (point.x - offset_x + 0.5), - (int) (point.y - offset_y + 0.5)); - } - - public Rectangle screen(AltosUIMapRectangle map_rect) { - Point2D.Double ul = screen(map_rect.ul); - Point2D.Double lr = screen(map_rect.lr); - - return new Rectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y)); - } - - public Point2D.Double screen(AltosUILatLon lat_lon) { - return screen(point(lat_lon)); - } - - private boolean has_location; - - public boolean has_location() { - return has_location; - } - - public AltosUIMapTransform(int width, int height, int zoom, AltosUILatLon centre_lat_lon) { - scale_x = 256/360.0 * Math.pow(2, zoom); - scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom); - - Point2D.Double centre_pt = point(centre_lat_lon); - - has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0); - offset_x = centre_pt.x - width / 2.0; - offset_y = centre_pt.y - height / 2.0; - } -} diff --git a/altosuilib/AltosUIMapView.java b/altosuilib/AltosUIMapView.java deleted file mode 100644 index c8632b99..00000000 --- a/altosuilib/AltosUIMapView.java +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright © 2014 Keith Packard <keithp@keithp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -package org.altusmetrum.altosuilib_6; - -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import javax.swing.*; -import java.io.*; -import java.lang.*; -import java.awt.geom.*; -import java.util.*; -import java.util.concurrent.*; -import org.altusmetrum.altoslib_6.*; - -public class AltosUIMapView extends Component implements MouseMotionListener, MouseListener, MouseWheelListener, ComponentListener, AltosUIMapTileListener, AltosUIMapStoreListener { - - AltosUIMapPath path = new AltosUIMapPath(); - - AltosUIMapLine line = new AltosUIMapLine(); - - AltosUIMapCache cache = new AltosUIMapCache(); - - LinkedList<AltosUIMapMark> marks = new LinkedList<AltosUIMapMark>(); - - LinkedList<AltosUIMapZoomListener> zoom_listeners = new LinkedList<AltosUIMapZoomListener>(); - - boolean have_boost = false; - boolean have_landed = false; - - ConcurrentHashMap<Point,AltosUIMapTile> tiles = new ConcurrentHashMap<Point,AltosUIMapTile>(); - - static final int default_zoom = 15; - static final int min_zoom = 3; - static final int max_zoom = 21; - static final int px_size = 512; - - int load_radius; - AltosUILatLon load_centre = null; - AltosUIMapTileListener load_listener; - - int zoom = default_zoom; - int maptype = AltosUIMap.maptype_default; - - long user_input_time; - - /* Milliseconds to wait after user action before auto-scrolling - */ - static final long auto_scroll_delay = 20 * 1000; - - AltosUIMapTransform transform; - AltosUILatLon centre; - - public void set_font() { - line.set_font(AltosUILib.status_font); - for (AltosUIMapTile tile : tiles.values()) - tile.set_font(AltosUILib.value_font); - repaint(); - } - - public void set_units() { - repaint(); - } - - private boolean is_drag_event(MouseEvent e) { - return e.getModifiers() == InputEvent.BUTTON1_MASK; - } - - Point drag_start; - - private void drag(MouseEvent e) { - if (drag_start == null) - return; - - int dx = e.getPoint().x - drag_start.x; - int dy = e.getPoint().y - drag_start.y; - - AltosUILatLon new_centre = transform.screen_lat_lon(new Point(getWidth() / 2 - dx, getHeight() / 2 - dy)); - centre (new_centre.lat, new_centre.lon); - drag_start = e.getPoint(); - } - - private void drag_start(MouseEvent e) { - drag_start = e.getPoint(); - } - - private void notice_user_input() { - user_input_time = System.currentTimeMillis(); - } - - private boolean recent_user_input() { - return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay; - } - - /* MouseMotionListener methods */ - - public void mouseDragged(MouseEvent e) { - notice_user_input(); - if (is_drag_event(e)) - drag(e); - else { - line.dragged(e, transform); - repaint(); - } - } - - public void mouseMoved(MouseEvent e) { - } - - /* MouseListener methods */ - public void mouseClicked(MouseEvent e) { - } - - public void mouseEntered(MouseEvent e) { - } - - public void mouseExited(MouseEvent e) { - } - - public void mousePressed(MouseEvent e) { - notice_user_input(); - if (is_drag_event(e)) - drag_start(e); - else { - line.pressed(e, transform); - repaint(); - } - } - - public void mouseReleased(MouseEvent e) { - } - - /* MouseWheelListener methods */ - - public void mouseWheelMoved(MouseWheelEvent e) { - int zoom_change = e.getWheelRotation(); - - notice_user_input(); - AltosUILatLon mouse_lat_lon = transform.screen_lat_lon(e.getPoint()); - set_zoom(zoom() - zoom_change); - - Point2D.Double new_mouse = transform.screen(mouse_lat_lon); - - int dx = getWidth()/2 - e.getPoint().x; - int dy = getHeight()/2 - e.getPoint().y; - - AltosUILatLon new_centre = transform.screen_lat_lon(new Point((int) new_mouse.x + dx, (int) new_mouse.y + dy)); - - centre(new_centre.lat, new_centre.lon); - } - - /* ComponentListener methods */ - - public void componentHidden(ComponentEvent e) { - } - - public void componentMoved(ComponentEvent e) { - } - - public void componentResized(ComponentEvent e) { - set_transform(); - } - - public void componentShown(ComponentEvent e) { - set_transform(); - } - - public void repaint(Rectangle r, int pad) { - repaint(r.x - pad, r.y - pad, r.width + pad*2, r.height + pad*2); - } - - public void repaint(AltosUIMapRectangle rect, int pad) { - repaint (transform.screen(rect), pad); - } - - private boolean far_from_centre(AltosUILatLon lat_lon) { - - if (centre == null || transform == null) - return true; - - Point2D.Double screen = transform.screen(lat_lon); - - int width = getWidth(); - int dx = Math.abs ((int) screen.x - width/2); - - if (dx > width / 4) - return true; - - int height = getHeight(); - int dy = Math.abs ((int) screen.y - height/2); - - if (dy > height / 4) - return true; - - return false; - } - - public void show(AltosState state, AltosListenerState listener_state) { - - /* If insufficient gps data, nothing to update - */ - AltosGPS gps = state.gps; - - if (gps == null) - return; - - if (!gps.locked && gps.nsat < 4) - return; - - AltosUIMapRectangle damage = path.add(gps.lat, gps.lon, state.state); - - switch (state.state) { - case AltosLib.ao_flight_boost: - if (!have_boost) { - add_mark(gps.lat, gps.lon, state.state); - have_boost = true; - } - break; - case AltosLib.ao_flight_landed: - if (!have_landed) { - add_mark(gps.lat, gps.lon, state.state); - have_landed = true; - } - break; - } - - if (damage != null) - repaint(damage, AltosUIMapPath.stroke_width); - maybe_centre(gps.lat, gps.lon); - } - - private void set_transform() { - Rectangle bounds = getBounds(); - - transform = new AltosUIMapTransform(bounds.width, bounds.height, zoom, centre); - repaint(); - } - - public boolean set_zoom(int zoom) { - if (min_zoom <= zoom && zoom <= max_zoom && zoom != this.zoom) { - this.zoom = zoom; - tiles.clear(); - set_transform(); - - for (AltosUIMapZoomListener listener : zoom_listeners) - listener.zoom_changed(this.zoom); - - return true; - } - return false; - } - - public void add_zoom_listener(AltosUIMapZoomListener listener) { - if (!zoom_listeners.contains(listener)) - zoom_listeners.add(listener); - } - - public void remove_zoom_listener(AltosUIMapZoomListener listener) { - zoom_listeners.remove(listener); - } - - public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) { - load_centre = new AltosUILatLon(lat, lon); - load_radius = radius; - load_listener = listener; - centre(lat, lon); - make_tiles(); - for (AltosUIMapTile tile : tiles.values()) { - tile.add_store_listener(this); - if (tile.store_status() != AltosUIMapStore.loading) - listener.notify_tile(tile, tile.store_status()); - } - repaint(); - } - - public boolean all_fetched() { - for (AltosUIMapTile tile : tiles.values()) { - if (tile.store_status() == AltosUIMapStore.loading) - return false; - } - return true; - } - - public boolean set_maptype(int maptype) { - if (maptype != this.maptype) { - this.maptype = maptype; - tiles.clear(); - repaint(); - return true; - } - return false; - } - - public int get_maptype() { - return maptype; - } - - public int zoom() { - return zoom; - } - - public void centre(AltosUILatLon lat_lon) { - centre = lat_lon; - set_transform(); - } - - public void centre(double lat, double lon) { - centre(new AltosUILatLon(lat, lon)); - } - - public void maybe_centre(double lat, double lon) { - AltosUILatLon lat_lon = new AltosUILatLon(lat, lon); - if (centre == null || (!recent_user_input() && far_from_centre(lat_lon))) - centre(lat_lon); - } - - private VolatileImage create_back_buffer() { - return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight()); - } - - private Point floor(Point2D.Double point) { - return new Point ((int) Math.floor(point.x / px_size) * px_size, - (int) Math.floor(point.y / px_size) * px_size); - } - - private Point ceil(Point2D.Double point) { - return new Point ((int) Math.ceil(point.x / px_size) * px_size, - (int) Math.ceil(point.y / px_size) * px_size); - } - - private void make_tiles() { - Point upper_left; - Point lower_right; - - if (load_centre != null) { - Point centre = floor(transform.point(load_centre)); - - upper_left = new Point(centre.x - load_radius * px_size, - centre.y - load_radius * px_size); - lower_right = new Point(centre.x + load_radius * px_size, - centre.y + load_radius * px_size); - } else { - upper_left = floor(transform.screen_point(new Point(0, 0))); - lower_right = floor(transform.screen_point(new Point(getWidth(), getHeight()))); - } - LinkedList<Point> to_remove = new LinkedList<Point>(); - - for (Point point : tiles.keySet()) { - if (point.x < upper_left.x || lower_right.x < point.x || - point.y < upper_left.y || lower_right.y < point.y) { - to_remove.add(point); - } - } - - for (Point point : to_remove) - tiles.remove(point); - - cache.set_cache_size((getWidth() / px_size + 2) * (getHeight() / px_size + 2)); - for (int y = upper_left.y; y <= lower_right.y; y += px_size) { - for (int x = upper_left.x; x <= lower_right.x; x += px_size) { - Point point = new Point(x, y); - - if (!tiles.containsKey(point)) { - AltosUILatLon ul = transform.lat_lon(new Point2D.Double(x, y)); - AltosUILatLon center = transform.lat_lon(new Point2D.Double(x + px_size/2, y + px_size/2)); - AltosUIMapTile tile = new AltosUIMapTile(this, ul, center, zoom, maptype, - px_size, AltosUILib.value_font); - tiles.put(point, tile); - } - } - } - } - - /* AltosUIMapTileListener methods */ - public synchronized void notify_tile(AltosUIMapTile tile, int status) { - for (Point point : tiles.keySet()) { - if (tile == tiles.get(point)) { - Point screen = transform.screen(point); - repaint(screen.x, screen.y, px_size, px_size); - } - } - } - - public AltosUIMapCache cache() { return cache; } - - /* AltosUIMapStoreListener methods */ - public synchronized void notify_store(AltosUIMapStore store, int status) { - if (load_listener != null) { - for (AltosUIMapTile tile : tiles.values()) - if (store.equals(tile.store)) - load_listener.notify_tile(tile, status); - } - } - - private void do_paint(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - - make_tiles(); - - for (AltosUIMapTile tile : tiles.values()) - tile.paint(g2d, transform); - - synchronized(marks) { - for (AltosUIMapMark mark : marks) - mark.paint(g2d, transform); - } - - path.paint(g2d, transform); - - line.paint(g2d, transform); - } - - public void paint(Graphics g) { - VolatileImage back_buffer = create_back_buffer(); - do { - GraphicsConfiguration gc = getGraphicsConfiguration(); - int code = back_buffer.validate(gc); - if (code == VolatileImage.IMAGE_INCOMPATIBLE) - back_buffer = create_back_buffer(); - - Graphics g_back = back_buffer.getGraphics(); - g_back.setClip(g.getClip()); - do_paint(g_back); - g_back.dispose(); - - g.drawImage(back_buffer, 0, 0, this); - } while (back_buffer.contentsLost()); - back_buffer.flush(); - } - - public void update(Graphics g) { - paint(g); - } - - public void add_mark(double lat, double lon, int state) { - synchronized(marks) { - marks.add(new AltosUIMapMark(lat, lon, state)); - } - repaint(); - } - - public void clear_marks() { - synchronized(marks) { - marks.clear(); - } - } - - public AltosUIMapView() { - centre(0, 0); - - addComponentListener(this); - addMouseMotionListener(this); - addMouseListener(this); - addMouseWheelListener(this); - set_font(); - } -} diff --git a/altosuilib/AltosUIMarker.java b/altosuilib/AltosUIMarker.java index 843ee939..ec5aae78 100644 --- a/altosuilib/AltosUIMarker.java +++ b/altosuilib/AltosUIMarker.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUIPreferences.java b/altosuilib/AltosUIPreferences.java index 9760494c..5602dc17 100644 --- a/altosuilib/AltosUIPreferences.java +++ b/altosuilib/AltosUIPreferences.java @@ -15,13 +15,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.*; import java.awt.Component; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class AltosUIPreferences extends AltosPreferences { @@ -55,10 +55,6 @@ public class AltosUIPreferences extends AltosPreferences { public static int position = AltosUILib.position_top_left; - static LinkedList<AltosUIMapCacheListener> map_cache_listeners; - - public static int map_cache = 9; - public static void init() { AltosPreferences.init(new AltosUIPreferencesBackend()); @@ -75,9 +71,6 @@ public class AltosUIPreferences extends AltosPreferences { position = backend.getInt(positionPreference, AltosUILib.position_top_left); position_listeners = new LinkedList<AltosPositionListener>(); - - map_cache = backend.getInt(mapCachePreference, 9); - map_cache_listeners = new LinkedList<AltosUIMapCacheListener>(); } static { init(); } @@ -225,32 +218,4 @@ public class AltosUIPreferences extends AltosPreferences { return position; } } - - public static void register_map_cache_listener(AltosUIMapCacheListener l) { - synchronized(backend) { - map_cache_listeners.add(l); - } - } - - public static void unregister_map_cache_listener(AltosUIMapCacheListener l) { - synchronized (backend) { - map_cache_listeners.remove(l); - } - } - - public static void set_map_cache(int new_map_cache) { - synchronized(backend) { - map_cache = new_map_cache; - backend.putInt(mapCachePreference, map_cache); - flush_preferences(); - for (AltosUIMapCacheListener l: map_cache_listeners) - l.map_cache_changed(map_cache); - } - } - - public static int map_cache() { - synchronized(backend) { - return map_cache; - } - } } diff --git a/altosuilib/AltosUIPreferencesBackend.java b/altosuilib/AltosUIPreferencesBackend.java index 91fe42ec..ebdc8b77 100644 --- a/altosuilib/AltosUIPreferencesBackend.java +++ b/altosuilib/AltosUIPreferencesBackend.java @@ -15,11 +15,11 @@ * 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 { diff --git a/altosuilib/AltosUIRateList.java b/altosuilib/AltosUIRateList.java index 0c783a89..a8898808 100644 --- a/altosuilib/AltosUIRateList.java +++ b/altosuilib/AltosUIRateList.java @@ -15,10 +15,10 @@ * 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> { diff --git a/altosuilib/AltosUISeries.java b/altosuilib/AltosUISeries.java index 4cd5ccd1..d4421ac7 100644 --- a/altosuilib/AltosUISeries.java +++ b/altosuilib/AltosUISeries.java @@ -15,14 +15,14 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.io.*; import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/altosuilib/AltosUITelemetryList.java b/altosuilib/AltosUITelemetryList.java index 77eef567..edbc7fd6 100644 --- a/altosuilib/AltosUITelemetryList.java +++ b/altosuilib/AltosUITelemetryList.java @@ -15,11 +15,11 @@ * 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> { diff --git a/altosuilib/AltosUIUnitsIndicator.java b/altosuilib/AltosUIUnitsIndicator.java index f86e274f..b58cd1cc 100644 --- a/altosuilib/AltosUIUnitsIndicator.java +++ b/altosuilib/AltosUIUnitsIndicator.java @@ -15,11 +15,11 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public abstract class AltosUIUnitsIndicator extends AltosUIIndicator { @@ -36,6 +36,10 @@ public abstract class AltosUIUnitsIndicator extends AltosUIIndicator { return hide(value(state, i)); } + public boolean hide(AltosState state, AltosListenerState listener_state, int i) { + return hide(state, i); + } + public double value (AltosState state, AltosListenerState listener_state, int i) { return value(state, i); } @@ -77,7 +81,7 @@ public abstract class AltosUIUnitsIndicator extends AltosUIIndicator { v[i] = value(state, listener_state, i); else v[i] = AltosLib.MISSING; - if (hide(state, i)) + if (hide(state, listener_state, i)) hide = true; } diff --git a/altosuilib/AltosUIVoltageIndicator.java b/altosuilib/AltosUIVoltageIndicator.java index 44ad2ea2..12649730 100644 --- a/altosuilib/AltosUIVoltageIndicator.java +++ b/altosuilib/AltosUIVoltageIndicator.java @@ -15,11 +15,11 @@ * 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 { diff --git a/altosuilib/AltosUSBDevice.java b/altosuilib/AltosUSBDevice.java index e940493f..74306523 100644 --- a/altosuilib/AltosUSBDevice.java +++ b/altosuilib/AltosUSBDevice.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.util.*; import libaltosJNI.*; diff --git a/altosuilib/AltosVoice.java b/altosuilib/AltosVoice.java index 867f6619..71a05a23 100644 --- a/altosuilib/AltosVoice.java +++ b/altosuilib/AltosVoice.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import com.sun.speech.freetts.Voice; import com.sun.speech.freetts.VoiceManager; diff --git a/altosuilib/GrabNDrag.java b/altosuilib/GrabNDrag.java index 5d9ce2d9..919289c7 100644 --- a/altosuilib/GrabNDrag.java +++ b/altosuilib/GrabNDrag.java @@ -15,7 +15,7 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.awt.*; import java.awt.event.*; diff --git a/altosuilib/Makefile.am b/altosuilib/Makefile.am index d51da91d..a00e4a12 100644 --- a/altosuilib/Makefile.am +++ b/altosuilib/Makefile.am @@ -12,8 +12,6 @@ altosuilib_JAVA = \ GrabNDrag.java \ AltosDevice.java \ AltosDeviceDialog.java \ - AltosFlightDisplay.java \ - AltosFontListener.java \ AltosPositionListener.java \ AltosUIConfigure.java \ AltosUIAxis.java \ @@ -31,7 +29,6 @@ altosuilib_JAVA = \ AltosUIPreferencesBackend.java \ AltosUIPreferences.java \ AltosUISeries.java \ - AltosUIVersion.java \ AltosUSBDevice.java \ AltosVoice.java \ AltosDisplayThread.java \ @@ -60,23 +57,8 @@ altosuilib_JAVA = \ AltosBTDeviceIterator.java \ AltosBTManage.java \ AltosBTKnown.java \ - AltosUIMap.java \ - AltosUIMapView.java \ - AltosUIMapLine.java \ - AltosUIMapMark.java \ - AltosUIMapPath.java \ - AltosUIMapTile.java \ - AltosUIMapCache.java \ - AltosUIMapCacheListener.java \ - AltosUIMapImage.java \ - AltosUIMapTransform.java \ - AltosUIMapRectangle.java \ - AltosUIMapZoomListener.java \ - AltosUIMapTileListener.java \ - AltosUIMapPreload.java \ - AltosUIMapStore.java \ - AltosUIMapStoreListener.java \ - AltosUILatLon.java \ + AltosUIMapNew.java \ + AltosUIMapPreloadNew.java \ AltosUIFlightTab.java \ AltosUIIndicator.java \ AltosUIUnitsIndicator.java \ @@ -84,6 +66,7 @@ altosuilib_JAVA = \ AltosUIFreqList.java \ AltosUITelemetryList.java \ AltosUIRateList.java \ + AltosUIImage.java \ OSXAdapter.java JAR=altosuilib_$(ALTOSUILIB_VERSION).jar diff --git a/altosuilib/OSXAdapter.java b/altosuilib/OSXAdapter.java index 28b00ce1..5155b0fe 100755 --- a/altosuilib/OSXAdapter.java +++ b/altosuilib/OSXAdapter.java @@ -55,7 +55,7 @@ Copyright © 2003-2007 Apple, Inc., All Rights Reserved */ -package org.altusmetrum.altosuilib_6; +package org.altusmetrum.altosuilib_8; import java.lang.reflect.*; import java.util.HashMap; diff --git a/ao-bringup/cal-freq b/ao-bringup/cal-freq index 5d876e21..d3d9dc95 100755 --- a/ao-bringup/cal-freq +++ b/ao-bringup/cal-freq @@ -10,42 +10,16 @@ case $# in ;; esac -while true; do - echo 'C 1' > $dev - - echo -n "Generating RF carrier. Please enter measured frequency [enter for done]: " - - read FREQ - - echo 'C 0' > $dev - +../ao-tools/ao-cal-freq/ao-cal-freq --dev=$dev +case $? in + 0) calline=`./get-radio-cal $dev` - CURRENT_CAL=`echo $calline | awk '{print $2}'` + CAL_VALUE=`echo $calline | awk '{print $2}'` CURRENT_FREQ=`echo $calline | awk '{print $4}'` - CAL_VALUE=$CURRENT_CAL - - case "$FREQ" in - "") - echo $SERIAL","$CAL_VALUE >> cal_values - exit 0 - ;; - *) - echo "Current radio calibration "$CURRENT_CAL - echo "Current radio frequency "$CURRENT_FREQ - - CAL_VALUE=`nickle -e "floor($CURRENT_FREQ / $FREQ * $CURRENT_CAL + 0.5)"` - - echo "Programming flash with cal value " $CAL_VALUE - - dd if=$dev iflag=nonblock - - cat << EOF > $dev -c f $CAL_VALUE -c w -EOF - - echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE - ;; - esac -done - + echo $SERIAL","$CAL_VALUE >> cal_values + exit 0 + ;; + *) + exit 1 + ;; +esac diff --git a/ao-bringup/test-easymega b/ao-bringup/test-easymega index 76f3effb..2f0a7822 100755 --- a/ao-bringup/test-easymega +++ b/ao-bringup/test-easymega @@ -12,7 +12,7 @@ echo "\t$PRODUCT v$VERSION powered from USB" echo ret=1 -ao-list | while read product serial dev; do +../ao-tools/ao-list/ao-list | while read product serial dev; do case "$product" in "$PRODUCT-v$VERSION") diff --git a/ao-bringup/test-telemetrum b/ao-bringup/test-telemetrum index 57a4d90d..13407e86 100755 --- a/ao-bringup/test-telemetrum +++ b/ao-bringup/test-telemetrum @@ -61,6 +61,7 @@ ao-list | while read product serial dev; do echo"" echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship + echo "\007" ret=0 ;; esac diff --git a/ao-bringup/turnon_easymega b/ao-bringup/turnon_easymega index b313e162..1e75e72f 100755 --- a/ao-bringup/turnon_easymega +++ b/ao-bringup/turnon_easymega @@ -1,14 +1,14 @@ #!/bin/sh -if [ -x /usr/bin/ao-flash-stm ]; then - STMLOAD=/usr/bin/ao-flash-stm +if [ -x ../ao-tools/ao-flash/ao-flash-stm ]; then + STMLOAD=../ao-tools/ao-flash/ao-flash-stm else echo "Can't find ao-flash-stm! Aborting." exit 1 fi -if [ -x /usr/bin/ao-usbload ]; then - USBLOAD=/usr/bin/ao-usbload +if [ -x ../ao-tools/ao-usbload/ao-usbload ]; then + USBLOAD=../ao-tools/ao-usbload/ao-usbload else echo "Can't find ao-usbload! Aborting." exit 1 @@ -38,7 +38,7 @@ $USBLOAD --serial=$SERIAL $REPO/easymega-v$VERSION*.elf || exit 1 sleep 2 -dev=`ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'` +dev=`../ao-tools/ao-list/ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'` case "$dev" in /dev/tty*) @@ -52,7 +52,7 @@ esac echo 'E 0' > $dev -../ao-tools/ao-cal-accel/ao-cal-accel $dev +../ao-tools/ao-cal-accel/ao-cal-accel $dev || exit 1 echo 'E 1' > $dev diff --git a/ao-bringup/turnon_telebt b/ao-bringup/turnon_telebt index c4902f31..f4100ae2 100755 --- a/ao-bringup/turnon_telebt +++ b/ao-bringup/turnon_telebt @@ -1,8 +1,5 @@ #!/bin/sh -# serial number of the TeleDongle being used as the flash programmer -DONGLE=612 - if [ -x ../ao-tools/ao-load/ao-load ]; then AOLOAD=../ao-tools/ao-load/ao-load elif [ -x /usr/bin/ao-load ]; then @@ -34,6 +31,19 @@ read SERIAL echo $RAWLOAD +case $USER in + bdale) + DONGLE=100 + ;; + keithp) + DONGLE=186 + ;; + *) + echo "Unknow user" + exit 1 + ;; +esac + $RAWLOAD -D $DONGLE -r ao_led_blink.ihx echo "LEDs should be blinking" sleep 5 @@ -45,7 +55,7 @@ read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD -D $DONGLE --cal $CAL_VALUE /usr/share/altos/telebt-v1.0*.ihx $SERIAL +$AOLOAD -D $DONGLE --cal $CAL_VALUE ~/altusmetrumllc/Binaries/telebt-v1.0*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo $SERIAL","$CAL_VALUE >> cal_values diff --git a/ao-bringup/turnon_telegps b/ao-bringup/turnon_telegps index 123f0b54..ba97d503 100755 --- a/ao-bringup/turnon_telegps +++ b/ao-bringup/turnon_telegps @@ -72,10 +72,8 @@ case "$dev" in ;; esac -echo 'E 0' > $dev +SERIAL=$SERIAL ./cal-freq $dev ./test-telegps -SERIAL=$SERIAL ./cal-freq $dev - exit $? diff --git a/ao-bringup/turnon_telemini b/ao-bringup/turnon_telemini index 4450d6f6..6aef7f51 100755 --- a/ao-bringup/turnon_telemini +++ b/ao-bringup/turnon_telemini @@ -18,11 +18,13 @@ else exit 1 fi -echo "TeleMini v1.0 Turn-On and Calibration Program" +VERSION=1.0 + +echo "TeleMini v$VERSION Turn-On and Calibration Program" echo "Copyright 2011 by Bdale Garbee. Released under GPL v2" echo echo "Expectations:" -echo "\tTeleMini v1.0 powered from LiPo" +echo "\tTeleMini v$VERSION powered from LiPo" echo "\t\twith TeleDongle (on /dev/ttyACM0) cabled to debug header" echo "\t\twith frequency counter able to sample RF output" echo @@ -31,18 +33,27 @@ read SERIAL echo $RAWLOAD -$RAWLOAD -D 100 -r ao_led_blink.ihx +case $USER in + bdale) + programmer=100 + ;; + keithp) + programmer=186 + ;; +esac + +$RAWLOAD -D $programmer -r ao_led_blink.ihx echo "LEDs should be blinking" sleep 5 -$RAWLOAD -D 100 -r ao_radio_xmit.ihx +$RAWLOAD -D $programmer -r ao_radio_xmit.ihx echo -n "Generating RF carrier. Please enter measured frequency: " read FREQ CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"` echo "Programming flash with cal value " $CAL_VALUE -$AOLOAD -D 100 --cal $CAL_VALUE /usr/share/altos/stable/telemini-v1.0*.ihx $SERIAL +$AOLOAD -D $programmer --cal $CAL_VALUE ~/altusmetrumllc/Binaries/telemini-v$VERSION-*.ihx $SERIAL echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE echo "Unplug and replug USB, cu to the board, confirm freq and record power" diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 6a170cbd..66d2560e 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -2,7 +2,8 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \ ao-load ao-telem ao-send-telem ao-sky-flash \ ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \ ao-flash ao-usbload ao-test-igniter ao-test-baro \ - ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng + ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \ + ao-cal-freq if LIBSTLINK SUBDIRS += ao-stmload endif diff --git a/ao-tools/ao-cal-freq/.gitignore b/ao-tools/ao-cal-freq/.gitignore new file mode 100644 index 00000000..4fe14865 --- /dev/null +++ b/ao-tools/ao-cal-freq/.gitignore @@ -0,0 +1 @@ +ao-cal-freq diff --git a/ao-tools/ao-cal-freq/Makefile.am b/ao-tools/ao-cal-freq/Makefile.am new file mode 100644 index 00000000..e11c2b0a --- /dev/null +++ b/ao-tools/ao-cal-freq/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS=ao-cal-freq + +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) + +ao_cal_freq_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a + +ao_cal_freq_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS) -lm + +ao_cal_freq_SOURCES=ao-cal-freq.c + +man_MANS = ao-cal-freq.1 diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.1 b/ao-tools/ao-cal-freq/ao-cal-freq.1 new file mode 100644 index 00000000..bfc3101c --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.1 @@ -0,0 +1,58 @@ +.\" +.\" Copyright © 2009 Keith Packard <keithp@keithp.com> +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH AO-LOAD 1 "ao-cal-freq" "" +.SH NAME +ao-cal-freq \- Calibrate AltOS flight computer frequency +.SH SYNOPSIS +.B "ao-cal-freq" +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] +.SH DESCRIPTION +.I ao-cal-freq +drives the frequency calibration process and saves the result. +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device the debugger uses to communicate with +the target device. The special name 'BITBANG' directs ao-dbg to use +the cp2103 connection, otherwise this should be a usb serial port +connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMega:2 +.br +TeleMega +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. +.SH USAGE +.I ao-cal-freq +opens the target device, interactively calibrates the frequency, then +shows the resulting calibration values and saves them to configuration +memory. +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.c b/ao-tools/ao-cal-freq/ao-cal-freq.c new file mode 100644 index 00000000..464faf0f --- /dev/null +++ b/ao-tools/ao-cal-freq/ao-cal-freq.c @@ -0,0 +1,280 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <err.h> +#include <fcntl.h> +#include <gelf.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <stdbool.h> +#include <termios.h> +#include <math.h> +#include "ao-elf.h" +#include "ccdbg.h" +#include "cc-usb.h" +#include "cc.h" +#include "ao-verbose.h" + +static const struct option options[] = { + { .name = "tty", .has_arg = 1, .val = 'T' }, + { .name = "device", .has_arg = 1, .val = 'D' }, + { .name = "raw", .has_arg = 0, .val = 'r' }, + { .name = "verbose", .has_arg = 1, .val = 'v' }, + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>]\n", program); + exit(1); +} + +void +done(struct cc_usb *cc, int code) +{ + cc_usb_close(cc); + exit (code); +} + +static int +ends_with(char *whole, char *suffix) +{ + int whole_len = strlen(whole); + int suffix_len = strlen(suffix); + + if (suffix_len > whole_len) + return 0; + return strcmp(whole + whole_len - suffix_len, suffix) == 0; +} + +static int +starts_with(char *whole, char *prefix) +{ + int whole_len = strlen(whole); + int prefix_len = strlen(prefix); + + if (prefix_len > whole_len) + return 0; + return strncmp(whole, prefix, prefix_len) == 0; +} + +static char ** +tok(char *line) { + char **strs = malloc (sizeof (char *)), *str; + int n = 0; + + while ((str = strtok(line, " \t"))) { + line = NULL; + strs = realloc(strs, (n + 2) * sizeof (char *)); + strs[n] = strdup(str); + n++; + } + strs[n] = '\0'; + return strs; +} + +static void +free_strs(char **strs) { + char *str; + int i; + + for (i = 0; (str = strs[i]) != NULL; i++) + free(str); + free(strs); +} + +struct flash { + struct flash *next; + char line[512]; + char **strs; +}; + +static struct flash * +flash(struct cc_usb *usb) +{ + struct flash *head = NULL, **tail = &head; + cc_usb_printf(usb, "c s\nv\n"); + for (;;) { + char line[512]; + struct flash *b; + + cc_usb_getline(usb, line, sizeof (line)); + b = malloc (sizeof (struct flash)); + strcpy(b->line, line); + b->strs = tok(line); + b->next = NULL; + *tail = b; + tail = &b->next; + if (strstr(line, "software-version")) + break; + } + return head; +} + +static void +free_flash(struct flash *b) { + struct flash *n; + + while (b) { + n = b->next; + free_strs(b->strs); + free(b); + b = n; + } +} + +char ** +find_flash(struct flash *b, char *word0) { + int i; + for (;b; b = b->next) { + if (strstr(b->line, word0)) + return b->strs; + } + return NULL; +} + +void +await_key(void) +{ + struct termios termios, termios_save; + char buf[512]; + + tcgetattr(0, &termios); + termios_save = termios; + cfmakeraw(&termios); + tcsetattr(0, TCSAFLUSH, &termios); + read(0, buf, sizeof (buf)); + tcsetattr(0, TCSAFLUSH, &termios_save); +} + +int +do_cal(struct cc_usb *usb) { + struct flash *b; + char line[1024]; + double measured_freq; + char **cur_freq_words; + char **cur_cal_words; + char *line_end; + int cur_freq; + int cur_cal; + int new_cal; + + cc_usb_printf(usb, "E 0\n"); + + for(;;) { + cc_usb_printf(usb, "C 1\n"); + cc_usb_sync(usb); + + printf("Generating RF carrier. Please enter measured frequency [enter for done]: "); + fflush(stdout); + fgets(line, sizeof (line) - 1, stdin); + cc_usb_printf(usb, "C 0\n"); + cc_usb_sync(usb); + + measured_freq = strtod(line, &line_end); + if (line_end == line) + break; + + b = flash(usb); + + cur_cal_words = find_flash(b, "Radio cal:"); + cur_freq_words = find_flash(b, "Frequency:"); + + if (!cur_cal_words || !cur_freq_words) { + printf("no response\n"); + return 0; + } + + cur_cal = atoi(cur_cal_words[2]); + cur_freq = atoi(cur_freq_words[1]); + + printf ("Current radio calibration %d\n", cur_cal); + printf ("Current radio frequency: %d\n", cur_freq); + + + new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5); + + printf ("Programming flash with cal value %d\n", new_cal); + + cc_usb_printf (usb, "c f %d\nc w\n", new_cal); + cc_usb_sync(usb); + } + return 1; +} + +int +main (int argc, char **argv) +{ + char *device = NULL; + char *filename; + Elf *e; + unsigned int s; + int i; + int c; + int tries; + struct cc_usb *cc = NULL; + char *tty = NULL; + int success; + int verbose = 0; + int ret = 0; + int expected_size; + + while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + case 'D': + device = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(argv[0]); + break; + } + } + + ao_verbose = verbose; + + if (verbose > 1) + ccdbg_add_debug(CC_DEBUG_BITBANG); + + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "AltosFlash"); + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "TeleMega"); + if (!tty) + tty = getenv("ALTOS_TTY"); + if (!tty) + tty="/dev/ttyACM0"; + + cc = cc_usb_open(tty); + + if (!cc) + exit(1); + + if (!do_cal(cc)) + ret = 1; + done(cc, ret); +} diff --git a/ao-tools/ao-dump-up/ao-dump-up.c b/ao-tools/ao-dump-up/ao-dump-up.c index 6268dc8b..b1f85af6 100644 --- a/ao-tools/ao-dump-up/ao-dump-up.c +++ b/ao-tools/ao-dump-up/ao-dump-up.c @@ -29,12 +29,13 @@ static const struct option options[] = { { .name = "tty", .has_arg = 1, .val = 'T' }, { .name = "device", .has_arg = 1, .val = 'D' }, + { .name = "wait", .has_arg = 0, .val = 'w' }, { 0, 0, 0, 0}, }; static void usage(char *program) { - fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>]\n", program); + fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--wait]\n", program); exit(1); } @@ -134,7 +135,7 @@ static int swap16(int i) static int find_header(struct cc_usb *cc) { for (;;) { - if (get_nonwhite(cc, 0) == 'M' && get_nonwhite(cc, 1000) == 'P') + if (get_nonwhite(cc, -1) == 'M' && get_nonwhite(cc, 1000) == 'P') return 1; } } @@ -165,9 +166,13 @@ main (int argc, char **argv) int i; int crc; int current_crc; + int wait = 0; - while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "wT:D:", options, NULL)) != -1) { switch (c) { + case 'w': + wait = 1; + break; case 'T': tty = optarg; break; @@ -179,8 +184,21 @@ main (int argc, char **argv) break; } } - if (!tty) - tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART"); + if (!tty) { + for (;;) { + tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART"); + if (tty) { + if (wait) { + printf("tty is %s\n", tty); + sleep(1); + } + break; + } + if (!wait) + break; + sleep(1); + } + } if (!tty) tty = getenv("ALTOS_TTY"); if (!tty) diff --git a/ao-tools/ao-flash/Makefile.am b/ao-tools/ao-flash/Makefile.am index 6b7ea6bb..4231dc21 100644 --- a/ao-tools/ao-flash/Makefile.am +++ b/ao-tools/ao-flash/Makefile.am @@ -1,3 +1,3 @@ -bin_SCRIPTS=ao-flash-stm ao-flash-lpc +bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x -man_MANS = ao-flash-stm.1 ao-flash-lpc.1
\ No newline at end of file +man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1 diff --git a/ao-tools/ao-flash/ao-flash-stm b/ao-tools/ao-flash/ao-flash-stm index 9eebf5d2..9eebf5d2 100644..100755 --- a/ao-tools/ao-flash/ao-flash-stm +++ b/ao-tools/ao-flash/ao-flash-stm diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x b/ao-tools/ao-flash/ao-flash-stm32f0x new file mode 100755 index 00000000..45643a4f --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-stm32f0x @@ -0,0 +1,16 @@ +#!/bin/sh +case "$#" in +0) + echo "usage: $0 <filename> ..." + exit 1 + ;; +esac +cmds=/tmp/flash$$ +trap "rm $cmds" 0 1 15 +file="$1" +echo "program $file verify reset" > $cmds +openocd \ + -f interface/stlink-v2.cfg \ + -f target/stm32f0x_stlink.cfg \ + -f $cmds \ + -c shutdown diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x.1 b/ao-tools/ao-flash/ao-flash-stm32f0x.1 new file mode 100644 index 00000000..07ff5b59 --- /dev/null +++ b/ao-tools/ao-flash/ao-flash-stm32f0x.1 @@ -0,0 +1,36 @@ +.\" +.\" Copyright © 2013 Keith Packard <keithp@keithp.com> +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH AO-FLASH-LPC 1 "ao-flash-stm32f0x" "" +.SH NAME +ao-flash-stm32f0x \- flash a program to a STM32F0x-based AltOS device using openocd +.SH SYNOPSIS +.B "ao-flash-stm32f0x" +\fIfile.elf\fP +.SH DESCRIPTION +.I ao-flash-stm32f0x +loads the specified .elf file into the target device flash memory. +.SH USAGE +.I ao-flash-stm32f0x +is a simple script that passes the correct arguments to openocd to +load a file into the target device via a connected STlink +debugging dongle. +.SH "SEE ALSO" +openocd(1) +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-list/ao-list.c b/ao-tools/ao-list/ao-list.c index c4b43d8f..4c065e79 100644 --- a/ao-tools/ao-list/ao-list.c +++ b/ao-tools/ao-list/ao-list.c @@ -28,12 +28,12 @@ main (int argc, char **argv) struct cc_usbdev *dev; int i; - devs = cc_usbdevs_scan(); + devs = cc_usbdevs_scan(TRUE); if (devs) { for (i = 0; i < devs->ndev; i++) { dev = devs->dev[i]; printf ("%-20.20s %6d %s\n", - dev->product, dev->serial, dev->tty); + dev->product, dev->serial, dev->tty ? dev->tty : "(none)"); } cc_usbdevs_free(devs); } diff --git a/ao-tools/ao-mega/ao-mega.c b/ao-tools/ao-mega/ao-mega.c index 523229e6..cfb58bb4 100644 --- a/ao-tools/ao-mega/ao-mega.c +++ b/ao-tools/ao-mega/ao-mega.c @@ -85,7 +85,7 @@ main (int argc, char **argv) if (cc_mega_parse(line, &log)) { if (log.is_config) { - printf ("kind %d\n", log.u.config_int.kind); + printf ("config %2d %s", log.u.config_int.kind, line); } else { printf ("tick %5d ", log.tick); switch (log.type) { @@ -126,8 +126,7 @@ main (int argc, char **argv) printf (" s%d %6d", j, log.u.volt.sense[j]); } - printf ("pyro %04x\n", log.u.volt.pyro); - printf ("\n"); + printf (" pyro %04x\n", log.u.volt.pyro); break; default: printf ("type %c\n", log.type, log.tick); diff --git a/ao-tools/ao-usbtrng/ao-usbtrng.1 b/ao-tools/ao-usbtrng/ao-usbtrng.1 index 7a1311b9..eabdd8a1 100644 --- a/ao-tools/ao-usbtrng/ao-usbtrng.1 +++ b/ao-tools/ao-usbtrng/ao-usbtrng.1 @@ -16,28 +16,28 @@ .\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. .\" .\" -.TH AO-LOAD 1 "ao-usbtrng" "" +.TH AO-USBTRNG 1 "ao-usbtrng" "" .SH NAME ao-usbtrng \- dump random numbers from USBtrng .SH SYNOPSIS -.B "ao-usbtrng" -[\-T \fItty-device\fP] -[\--tty \fItty-device\fP] -[\-D \fIaltos-device\fP] -[\--device \fIaltos-device\fP] -\fIkbytes\fP +.B "ao-usbtrng" [OPTION...] [KBYTES] .SH DESCRIPTION .I ao-usbtrng -dumps random numbers from a USBtrng device +dumps random numbers from a USBtrng device. If provided KBYTES specifies the number of 1024 byte blocks to produce on standard output. Without KBYTES +.I ao-usbtrng +produces random bytes continuously until killed. .SH OPTIONS .TP -\-T tty-device | --tty tty-device +\-v, --verbose +increase verbosity +.TP +\-T, -tty=TTYDEVICE This selects which tty device the debugger uses to communicate with the target device. The special name 'BITBANG' directs ao-dbg to use the cp2103 connection, otherwise this should be a usb serial port connected to a suitable cc1111 debug node. .TP -\-D AltOS-device | --device AltOS-device +\-D, --device=ALTOSDEVICE Search for a connected device. This requires an argument of one of the following forms: .IP @@ -52,7 +52,7 @@ product, leaving out the serial number will cause the tool to match one of the available devices. .SH USAGE .I ao-usbtrng -opens the target device and reads the specified number of kbytes of +opens the target device and reads the specified number of KBYTES of random data. .SH AUTHOR Keith Packard diff --git a/ao-tools/ao-usbtrng/ao-usbtrng.c b/ao-tools/ao-usbtrng/ao-usbtrng.c index 232f4e3e..456885d9 100644 --- a/ao-tools/ao-usbtrng/ao-usbtrng.c +++ b/ao-tools/ao-usbtrng/ao-usbtrng.c @@ -35,14 +35,13 @@ static const struct option options[] = { { .name = "tty", .has_arg = 1, .val = 'T' }, { .name = "device", .has_arg = 1, .val = 'D' }, - { .name = "raw", .has_arg = 0, .val = 'r' }, - { .name = "verbose", .has_arg = 1, .val = 'v' }, + { .name = "verbose", .has_arg = 0, .val = 'v' }, { 0, 0, 0, 0}, }; static void usage(char *program) { - fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] <kbytes>\n", program); + fprintf(stderr, "usage: %s [--verbose] [--device=<AltOS-device>] [-tty=<tty>] [<kbytes>]\n", program); exit(1); } @@ -58,21 +57,17 @@ main (int argc, char **argv) { char *device = NULL; char *filename; - Elf *e; - unsigned int s; int i; int c; - int tries; struct cc_usb *cc = NULL; char *tty = NULL; - int success; int verbose = 0; int ret = 0; - int expected_size; - int kbytes; + int kbytes = 0; /* 0 == continuous */ + int written; uint8_t bits[1024]; - while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "vT:D:", options, NULL)) != -1) { switch (c) { case 'T': tty = optarg; @@ -89,12 +84,8 @@ main (int argc, char **argv) } } - if (!argv[optind]) - usage(argv[0]); - - kbytes = atoi(argv[optind]); - if (kbytes < 1) - kbytes = 1; + if (optind < argc) + kbytes = atoi(argv[optind]); ao_verbose = verbose; @@ -113,13 +104,22 @@ main (int argc, char **argv) if (!cc) exit(1); - cc_usb_printf(cc, "f %d\n", kbytes); + if (kbytes) { + cc_usb_printf(cc, "f %d\n", kbytes); - while (kbytes--) { - int i; - for (i = 0; i < 1024; i++) - bits[i] = cc_usb_getchar(cc); - write(1, bits, 1024); + while (kbytes--) { + for (i = 0; i < 1024; i++) + bits[i] = cc_usb_getchar(cc); + write(1, bits, 1024); + } + } else { /* 0 == continuous */ + written = 0; + while (written >= 0) { + cc_usb_printf(cc, "f 1\n"); + for (i = 0; i < 1024; i++) + bits[i] = cc_usb_getchar(cc); + written = write(1, bits, 1024); + } } done(cc, ret); diff --git a/ao-tools/lib/cc-usb.c b/ao-tools/lib/cc-usb.c index 1a4dc7a1..1e023c7e 100644 --- a/ao-tools/lib/cc-usb.c +++ b/ao-tools/lib/cc-usb.c @@ -207,8 +207,10 @@ _cc_usb_sync(struct cc_usb *cc, int wait_for_input, int write_timeout) write(2, cc->in_buf, cc->in_count); cc->in_count = 0; } - } else if (ret < 0) + } else if (ret <= 0) { perror("read"); + return -1; + } } if (fds.revents & POLLOUT) { ret = write(cc->fd, cc->out_buf, diff --git a/ao-tools/lib/cc-usbdev.c b/ao-tools/lib/cc-usbdev.c index 95bfa244..6c3ba591 100644 --- a/ao-tools/lib/cc-usbdev.c +++ b/ao-tools/lib/cc-usbdev.c @@ -219,7 +219,7 @@ is_am(int idVendor, int idProduct) { } struct cc_usbdevs * -cc_usbdevs_scan(void) +cc_usbdevs_scan(int non_tty) { int e; struct dirent **ents; @@ -241,7 +241,7 @@ cc_usbdevs_scan(void) dir = cc_fullname(USB_DEVICES, ents[e]->d_name); dev = usb_scan_device(dir); free(dir); - if (is_am(dev->idVendor, dev->idProduct) && dev->tty) { + if (is_am(dev->idVendor, dev->idProduct) && (non_tty || dev->tty)) { if (devs->dev) devs->dev = realloc(devs->dev, (devs->ndev + 1) * sizeof (struct usbdev *)); @@ -274,7 +274,7 @@ match_dev(char *product, int serial) int i; char *tty = NULL; - devs = cc_usbdevs_scan(); + devs = cc_usbdevs_scan(FALSE); if (!devs) return NULL; for (i = 0; i < devs->ndev; i++) { diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index bff4b2c9..ff95e4fc 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -50,7 +50,7 @@ void cc_usbdevs_free(struct cc_usbdevs *usbdevs); struct cc_usbdevs * -cc_usbdevs_scan(void); +cc_usbdevs_scan(int non_tty); char * cc_usbdevs_find_by_arg(char *arg, char *default_product); diff --git a/configure.ac b/configure.ac index b09daa82..bbf82377 100644 --- a/configure.ac +++ b/configure.ac @@ -18,19 +18,21 @@ dnl dnl Process this file with autoconf to create configure. AC_PREREQ(2.57) -AC_INIT([altos], 1.6) +AC_INIT([altos], 1.6.1) +ANDROID_VERSION=9 AC_CONFIG_SRCDIR([src/kernel/ao.h]) AM_INIT_AUTOMAKE([foreign dist-bzip2]) AM_MAINTAINER_MODE VERSION_DASH=`echo $VERSION | sed 's/\./-/g'` AC_SUBST(VERSION_DASH) +AC_SUBST(ANDROID_VERSION) dnl ========================================================================== dnl Java library versions -ALTOSUILIB_VERSION=6 -ALTOSLIB_VERSION=6 +ALTOSUILIB_VERSION=8 +ALTOSLIB_VERSION=8 AC_SUBST(ALTOSLIB_VERSION) AC_DEFINE(ALTOSLIB_VERSION,$ALTOSLIB_VERSION,[Version of the AltosLib package]) @@ -514,9 +516,9 @@ AC_OUTPUT([ Makefile src/Makedefs altoslib/Makefile +altoslib/AltosVersion.java icon/Makefile altosuilib/Makefile -altosuilib/AltosUIVersion.java altosui/Makefile altosui/Info.plist altosui/altos-windows.nsi @@ -529,6 +531,7 @@ telegps/Info.plist telegps/telegps-windows.nsi altosdroid/Makefile altosdroid/local.properties +altosdroid/AndroidManifest.xml ao-tools/Makefile ao-tools/lib/Makefile ao-tools/ao-rawload/Makefile @@ -551,6 +554,7 @@ ao-tools/ao-test-igniter/Makefile ao-tools/ao-test-baro/Makefile ao-tools/ao-test-flash/Makefile ao-tools/ao-cal-accel/Makefile +ao-tools/ao-cal-freq/Makefile ao-tools/ao-test-gps/Makefile ao-tools/ao-usbtrng/Makefile ao-utils/Makefile diff --git a/debian/control b/debian/control index f3a37ed0..c9b0fa83 100644 --- a/debian/control +++ b/debian/control @@ -6,7 +6,7 @@ Uploaders: Keith Packard <keithp@keithp.com> Build-Depends: debhelper (>= 7), autoconf, automake, gawk, libreadline-dev, libusb-1.0-0-dev, nickle, cc1111, xsltproc, fop, xmlto, docbook-xml, docbook-xsl, swig, default-jdk, freetts, libtool, libjfreechart-java, libbluetooth-dev, pkg-config, libelf-dev, libbluetooth-dev, libssl-dev, gcc-arm-none-eabi, icoutils, librsvg2-bin, icnsutils, graphicsmagick | imagemagick, netpbm, shared-mime-info, libgtk-3-bin Standards-Version: 3.9.5 Homepage: http://altusmetrum.org/AltOS -Vcs-Git: git://git.gag.com/fw/altos +Vcs-Git: git://git.gag.com/fw/altos -b debian Vcs-Browser: http://git.gag.com/?p=fw/altos Package: altos diff --git a/doc/Makefile b/doc/Makefile index 2887a229..9c6189b4 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -18,7 +18,8 @@ RELNOTES=\ release-notes-1.4.html \ release-notes-1.4.1.html \ release-notes-1.5.html \ - release-notes-1.6.html + release-notes-1.6.html \ + release-notes-1.6.1.html PICTURES=\ altosui.png \ @@ -51,6 +52,7 @@ PICTURES=\ micropeak-statistics.png \ MicroPeakUSB-2.0-inuse.jpg \ MicroPeakUSB-2.0.jpg \ + monitor-idle.png \ scan-channels.png \ site-map.png \ table.png \ diff --git a/doc/altusmetrum.xsl b/doc/altusmetrum.xsl index 1631983a..a40481f1 100644 --- a/doc/altusmetrum.xsl +++ b/doc/altusmetrum.xsl @@ -41,6 +41,13 @@ </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> <revremark> @@ -2620,9 +2627,9 @@ NAR #88757, TRA #12200 <listitem> <para> After Motor. The flight software counts each time the - rocket starts accelerating (presumably due to a motor or - motors igniting). Use this value to count ignitions for - multi-staged or multi-airstart launches. + rocket starts accelerating and then decelerating + (presumably due to a motor or motors burning). Use this + value for multi-staged or multi-airstart launches. </para> </listitem> <listitem> @@ -3853,8 +3860,7 @@ NAR #88757, TRA #12200 <para> Before heading out to a new launch site, you can use this to load satellite images in case you don't have internet - connectivity at the site. This loads a fairly large area - around the launch site, which should cover any flight you're likely to make. + connectivity at the site. </para> <para> There's a drop-down menu of launch sites we know about; if @@ -3909,15 +3915,18 @@ NAR #88757, TRA #12200 You can specify the range of zoom levels to download; smaller numbers show more area with less resolution. The default level, 0, shows about 3m/pixel. One zoom level change - doubles or halves that number. + doubles or halves that number. Larger zoom levels show more + detail, smaller zoom levels less. </para> <para> - The Tile Radius value sets how large an area around the center - point to download. Each tile is 512x512 pixels, and the - 'radius' value specifies how many tiles away from the center - will be downloaded. Specify a radius of 0 and you get only the - center tile. A radius of 1 loads a 3x3 grid, centered on the - specified location. + The Map Radius value sets how large an area around the center + point to download. Select a value large enough to cover any + plausible flight from that site. Be aware that loading a large + area with a high maximum zoom level can attempt to download a + lot of data. Loading hybrid maps with a 10km radius at a + minimum zoom of -2 and a maximum zoom of 2 consumes about + 120MB of space. Terrain and road maps consume about 1/10 as + much space as satellite or hybrid maps. </para> <para> Clicking the 'Load Map' button will fetch images from Google @@ -3929,6 +3938,13 @@ NAR #88757, TRA #12200 </section> <section> <title>Monitor Idle</title> + <informalfigure> + <mediaobject> + <imageobject> + <imagedata fileref="monitor-idle.png" width="5.2in" scalefit="1"/> + </imageobject> + </mediaobject> + </informalfigure> <para> This brings up a dialog similar to the Monitor Flight UI, except it works with the altimeter in “idle” mode by sending @@ -3939,22 +3955,28 @@ NAR #88757, TRA #12200 cannot manage to run Monitor Idle, then it's very likely that your callsigns are different in some way. </para> + <para> + You can change the frequency and callsign used to communicate + with the flight computer; they must both match the + configuration in the flight computer exactly. + </para> </section> </chapter> <chapter> <title>AltosDroid</title> <para> AltosDroid provides the same flight monitoring capabilities as - AltosUI, but runs on Android devices and is designed to connect - to a TeleBT receiver over Bluetooth™. AltosDroid monitors + AltosUI, but runs on Android devices. AltosDroid is designed to connect + to a TeleBT receiver over Bluetooth™ and (on Android devices supporting + USB On-the-go) TeleDongle and TeleBT devices over USB. AltosDroid monitors telemetry data, logging it to internal storage in the Android - device, and presents that data in a UI the same way the 'Monitor - Flight' window does in AltosUI. + device, and presents that data in a UI similar to the 'Monitor + Flight' window in AltosUI. </para> <para> - This manual will explain how to configure AltosDroid, connect - to TeleBT, operate the flight monitoring interface and describe - what the displayed data means. + This manual will explain how to configure AltosDroid, connect to + TeleBT or TeleDongle, operate the flight monitoring interface + and describe what the displayed data means. </para> <section> <title>Installing AltosDroid</title> @@ -3968,7 +3990,7 @@ NAR #88757, TRA #12200 </para> </section> <section> - <title>Connecting to TeleBT</title> + <title>Connecting to TeleBT over Bluetooth™</title> <para> Press the Android 'Menu' button or soft-key to see the configuration options available. Select the 'Connect a device' @@ -3983,14 +4005,90 @@ NAR #88757, TRA #12200 </para> </section> <section> + <title>Connecting to TeleDongle or TeleBT over USB</title> + <para> + Get a special USB On-the-go adapter cable. These cables have a USB + micro-B male connector on one end and a standard A female + connector on the other end. Plug in your TeleDongle or TeleBT + device to the adapter cable and the adapter cable into your + phone and AltosDroid should automatically start up. If it + doesn't, the most likely reason is that your Android device + doesn't support USB On-the-go. + </para> + </section> + <section> <title>Configuring AltosDroid</title> <para> - The only configuration option available for AltosDroid is - which frequency to listen on. Press the Android 'Menu' button - or soft-key and pick the 'Select radio frequency' entry. That - brings up a menu of pre-set radio frequencies; pick the one - which matches your altimeter. + There are several configuration and operation parameters + available in the AltosDroid menu. </para> + <section> + <title>Select radio frequency</title> + <para> + This selects which frequency to listen on by bringing up a + menu of pre-set radio frequencies. Pick the one which matches + your altimeter. + </para> + </section> + <section> + <title>Select data rate</title> + <para> + Altus Metrum transmitters can be configured to operate at + lower data rates to improve transmission range. If you have + configured your device to do this, this menu item allows you + to change the receiver to match. + </para> + </section> + <section> + <title>Change units</title> + <para> + This toggles between metric and imperial units. + </para> + </section> + <section> + <title>Load maps</title> + <para> + Brings up a dialog allowing you to download offline map + tiles so that you can have maps available even if you have + no network connectivity at the launch site. + </para> + </section> + <section> + <title>Map type</title> + <para> + Displays a menu of map types and lets you select one. Hybrid + maps include satellite images with a roadmap + overlaid. Satellite maps dispense with the roadmap + overlay. Roadmap shows just the roads. Terrain includes + roads along with shadows indicating changes in elevation, + and other geographical features. + </para> + </section> + <section> + <title>Toggle Online/Offline maps</title> + <para> + Switches between online and offline maps. Online maps will + show a 'move to current position' icon in the upper right + corner, while offline maps will have copyright information + all over the map. Otherwise, they're pretty similar. + </para> + </section> + <section> + <title>Select Tracker</title> + <para> + Switches the information displays to show data for a + different transmitting device. The map will always show all + of the devices in view. Trackers are shown and selected by + serial number, so make sure you note the serial number of + devices in each airframe. + </para> + </section> + <section> + <title>Delete Track</title> + <para> + Deletes all information about a transmitting device. + </para> + </section> </section> <section> <title>AltosDroid Flight Monitoring</title> @@ -4004,91 +4102,353 @@ NAR #88757, TRA #12200 <section> <title>Pad</title> <para> - The 'Launch Pad' tab shows information used to decide when the + The 'Pad' tab shows information used to decide when the rocket is ready for flight. The first elements include red/green indicators, if any of these is red, you'll want to evaluate - whether the rocket is ready to launch: - <variablelist> - <varlistentry> - <term>Battery Voltage</term> - <listitem> - <para> - This indicates whether the Li-Po battery - powering the TeleMetrum has sufficient charge to last for - the duration of the flight. A value of more than - 3.8V is required for a 'GO' status. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>Apogee Igniter Voltage</term> - <listitem> - <para> - This indicates whether the apogee - igniter has continuity. If the igniter has a low - resistance, then the voltage measured here will be close - to the Li-Po battery voltage. A value greater than 3.2V is - required for a 'GO' status. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>Main Igniter Voltage</term> - <listitem> - <para> - This indicates whether the main - igniter has continuity. If the igniter has a low - resistance, then the voltage measured here will be close - to the Li-Po battery voltage. A value greater than 3.2V is - required for a 'GO' status. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>On-board Data Logging</term> - <listitem> - <para> - This indicates whether there is - space remaining on-board to store flight data for the - upcoming flight. If you've downloaded data, but failed - to erase flights, there may not be any space - left. TeleMetrum can store multiple flights, depending - on the configured maximum flight log size. TeleMini - stores only a single flight, so it will need to be - downloaded and erased after each flight to capture - data. This only affects on-board flight logging; the - altimeter will still transmit telemetry and fire - ejection charges at the proper times. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>GPS Locked</term> - <listitem> - <para> - For a TeleMetrum or TeleMega device, this indicates whether the GPS receiver is - currently able to compute position information. GPS requires - at least 4 satellites to compute an accurate position. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term>GPS Ready</term> - <listitem> - <para> - For a TeleMetrum or TeleMega device, this indicates whether GPS has reported at least - 10 consecutive positions without losing lock. This ensures - that the GPS receiver has reliable reception from the - satellites. - </para> - </listitem> - </varlistentry> - </variablelist> + whether the rocket is ready to launch. </para> <para> - The Launchpad tab also shows the computed launch pad position - and altitude, averaging many reported positions to improve the - accuracy of the fix. + When the pad tab is selected, the voice responses will + include status changes to the igniters and GPS reception, + letting you know if the rocket is still ready for launch. + </para> + <variablelist> + <varlistentry> + <term>Battery</term> + <listitem> + <para> + This indicates whether the Li-Po battery + powering the transmitter has sufficient charge to last for + the duration of the flight. A value of more than + 3.8V is required for a 'GO' status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Receiver Battery</term> + <listitem> + <para> + This indicates whether the Li-Po battery + powering the TeleBT has sufficient charge to last for + the duration of the flight. A value of more than + 3.8V is required for a 'GO' status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Data Logging</term> + <listitem> + <para> + This indicates whether there is space remaining + on-board to store flight data for the upcoming + flight. If you've downloaded data, but failed to + erase flights, there may not be any space + left. TeleMetrum and TeleMega can store multiple + flights, depending on the configured maximum flight + log size. TeleGPS logs data continuously. TeleMini + stores only a single flight, so it will need to be + downloaded and erased after each flight to capture + data. This only affects on-board flight logging; the + altimeter will still transmit telemetry and fire + ejection charges at the proper times. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>GPS Locked</term> + <listitem> + <para> + For a TeleMetrum or TeleMega device, this indicates whether the GPS receiver is + currently able to compute position information. GPS requires + at least 4 satellites to compute an accurate position. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>GPS Ready</term> + <listitem> + <para> + For a TeleMetrum or TeleMega device, this indicates whether GPS has reported at least + 10 consecutive positions without losing lock. This ensures + that the GPS receiver has reliable reception from the + satellites. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Apogee Igniter</term> + <listitem> + <para> + This indicates whether the apogee + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the Li-Po battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Main Igniter</term> + <listitem> + <para> + This indicates whether the main + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the Li-Po battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Igniter A-D</term> + <listitem> + <para> + This indicates whether the indicated additional pyro + channel igniter has continuity. If the igniter has a + low resistance, then the voltage measured here will + be close to the Li-Po battery voltage. A value + greater than 3.2V is required for a 'GO' status. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + The Pad tab also shows the location of the Android device. + </para> + </section> + <section> + <title>Flight</title> + <para> + The 'Flight' tab shows information used to evaluate and spot + a rocket while in flight. It displays speed and height data + to monitor the health of the rocket, along with elevation, + range and bearing to help locate the rocket in the sky. + </para> + <para> + While the Flight tab is displayed, the voice announcements + will include current speed, height, elevation and bearing + information. + </para> + <variablelist> + <varlistentry> + <term>Speed</term> + <listitem> + <para> + Shows current vertical speed. During descent, the + speed values are averaged over a fairly long time to + try and make them steadier. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Height</term> + <listitem> + <para> + Shows the current height above the launch pad. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Max Speed</term> + <listitem> + <para> + Shows the maximum vertical speed seen during the flight. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Max Height</term> + <listitem> + <para> + Shows the maximum height above launch pad. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Elevation</term> + <listitem> + <para> + This is the angle above the horizon from the android + devices current position. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Range</term> + <listitem> + <para> + The total distance from the android device to the + rocket, including both ground distance and + difference in altitude. Use this to gauge how large + the rocket is likely to appear in the sky. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Bearing</term> + <listitem> + <para> + This is the aziumuth from true north for the rocket + from the android device. Use this in combination + with the Elevation value to help locate the rocket + in the sky, or at least to help point the antenna in + the general direction. This is provided in both + degrees and a compass point (like West South + West). You'll want to know which direction is true + north before launching your rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Ground Distance</term> + <listitem> + <para> + This shows the distance across the ground to the + lat/lon where the rocket is located. Use this to + estimate what is currently under the rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Latitude/Longitude</term> + <listitem> + <para> + Displays the last known location of the rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Apogee Igniter</term> + <listitem> + <para> + This indicates whether the apogee + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the Li-Po battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Main Igniter</term> + <listitem> + <para> + This indicates whether the main + igniter has continuity. If the igniter has a low + resistance, then the voltage measured here will be close + to the Li-Po battery voltage. A value greater than 3.2V is + required for a 'GO' status. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section> + <title>Recover</title> + <para> + The 'Recover' tab shows information used while recovering the + rocket on the ground after flight. + </para> + <para> + While the Recover tab is displayed, the voice announcements + will include distance along with either bearing or + direction, depending on whether you are moving. + </para> + <variablelist> + <varlistentry> + <term>Bearing</term> + <listitem> + <para> + This is the aziumuth from true north for the rocket + from the android device. Use this in combination + with the Elevation value to help locate the rocket + in the sky, or at least to help point the antenna in + the general direction. This is provided in both + degrees and a compass point (like West South + West). You'll want to know which direction is true + north before launching your rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Direction</term> + <listitem> + <para> + When you are in motion, this provides the angle from + your current direction of motion towards the rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Distance</term> + <listitem> + <para> + Distance over the ground to the rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Tar Lat/Tar Lon</term> + <listitem> + <para> + Displays the last known location of the rocket. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>My Lat/My Lon</term> + <listitem> + <para> + Displays the location of the Android device. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Max Height</term> + <listitem> + <para> + Shows the maximum height above launch pad. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Max Speed</term> + <listitem> + <para> + Shows the maximum vertical speed seen during the flight. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term>Max Accel</term> + <listitem> + <para> + Shows the maximum vertical acceleration seen during the flight. + </para> + </listitem> + </varlistentry> + </variablelist> + </section> + <section> + <title>Map</title> + <para> + The 'Map' tab shows a map of the area around the rocket + being tracked along with information needed to recover it. + </para> + <para> + On the map itself, icons showing the location of the android + device along with the last known location of each tracker. A + blue line is drawn from the android device location to the + currently selected tracker. + </para> + <para> + Below the map, the distance and either bearing or direction + along with the lat/lon of the target and the android device + are shown + </para> + <para> + The Map tab provides the same voice announcements as the + Recover tab. </para> </section> </section> @@ -4097,9 +4457,9 @@ NAR #88757, TRA #12200 <para> AltosDroid always saves every bit of telemetry data it receives. To download that to a computer for use with AltosUI, - simply remove the SD card from your Android device, or connect - your device to your computer's USB port and browse the files - on that device. You will find '.telem' files in the TeleMetrum + remove the SD card from your Android device, or connect your + device to your computer's USB port and browse the files on + that device. You will find '.telem' files in the TeleMetrum directory that will work with AltosUI directly. </para> </section> @@ -5916,6 +6276,13 @@ NAR #88757, TRA #12200 <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 xmlns:xi="http://www.w3.org/2001/XInclude" diff --git a/doc/load-maps.png b/doc/load-maps.png Binary files differindex ae98c9a5..150b8b3c 100644 --- a/doc/load-maps.png +++ b/doc/load-maps.png diff --git a/doc/monitor-idle.png b/doc/monitor-idle.png Binary files differnew file mode 100644 index 00000000..964063f1 --- /dev/null +++ b/doc/monitor-idle.png diff --git a/doc/release-notes-1.6.1.xsl b/doc/release-notes-1.6.1.xsl new file mode 100644 index 00000000..058d43fe --- /dev/null +++ b/doc/release-notes-1.6.1.xsl @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" +"/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd"> + +<article> + <para> + Version 1.6.1 includes support for our updated TeleBT v3.0 + product and bug fixes in in the flight software for all our boards + and ground station interfaces. + </para> + <para> + AltOS New Features + <itemizedlist> + <listitem> + <para> + Add support for TeleBT v3.0 boards. + </para> + </listitem> + <listitem> + <para> + Add support for uncompressed APRS data, providing support + for older APRS receivers. Uncompressed APRS data is less + precise, takes more bandwidth and doesn't have integrated + altitude data. + </para> + </listitem> + </itemizedlist> + </para> + <para> + AltOS Fixes + <itemizedlist> + <listitem> + <para> + Make TeleDongle and TeleBT more tolerant of data rate + variations from transmitting devices. + </para> + </listitem> + </itemizedlist> + </para> + <para> + AltosUI and TeleGPS New Features + <itemizedlist> + <listitem> + <para> + Add map to Monitor Idle display. It's nice to be able to + verify that maps are working, instead of needing to use + Monitor Flight. + </para> + </listitem> + </itemizedlist> + </para> + <para> + AltosUI Fixes + <itemizedlist> + <listitem> + <para> + Fix frequency configuration to round values instead of + truncate them, avoiding a common 1kHz error in the setting. + </para> + </listitem> + <listitem> + <para> + Turn the Windows stub into a more useful program that can + launch the application with parameters so that file manager + icons work more reliably. + </para> + </listitem> + <listitem> + <para> + Force KML export to use a C locale so that numbers are + formatted with '.' instead of ',' for a decimal separator in + non-US locales. + </para> + </listitem> + <listitem> + <para> + Preload map tiles based on distance rather than number of + tiles; this means you get the same resolution covering the + entire area, rather than having high resolution near the + center and low resolution further away. + </para> + </listitem> + <listitem> + <para> + Allow configuration of frequency and callsign in Monitor + Idle mode. + </para> + </listitem> + <listitem> + <para> + Fix layout weirdness when resizing windows on + Windows. Windows shouldn't have giant blank spaces around + the useful content anymore. + </para> + </listitem> + <listitem> + <para> + Fix layout weirdness when resizing windows on + Windows. Windows shouldn't have giant blank spaces around + the useful content anymore. + </para> + </listitem> + <listitem> + <para> + Use a longer filter for descent speed values. This should + provide something more useful on the display, although it + will take longer to respond to changes now. + </para> + </listitem> + <listitem> + <para> + Make Replay Flight run in realtime again. It had been set to + run at 10x speed by mistake. + </para> + </listitem> + </itemizedlist> + </para> + <para> + AltosDroid New Features + <itemizedlist> + <listitem> + <para> + Add offline map support using mapping code from AltosUI. + </para> + </listitem> + <listitem> + <para> + Support TeleDongle (and TeleBT via USB) on devices + supporting USB On-The-Go. + </para> + </listitem> + <listitem> + <para> + Display additional TeleMega pyro channel status in Pad tab. + </para> + </listitem> + <listitem> + <para> + Switch between metric and imperial units. + </para> + </listitem> + <listitem> + <para> + Monitor TeleBT battery voltage. + </para> + </listitem> + <listitem> + <para> + Track multiple devices at the same time, selecting between + them with a menu or using the map. + </para> + </listitem> + <listitem> + <para> + Add hybrid, satellite and terrain map types. + </para> + </listitem> + </itemizedlist> + </para> + <para> + AltosDroid Fixes + <itemizedlist> + <listitem> + <para> + Use standard Android display conventions so that a menu + button is available in the application title bar. + </para> + </listitem> + <listitem> + <para> + Adjust layout to work on large and small screens; shrinking + the go/no-go lights in smaller environments to try and make + everything visible. + </para> + </listitem> + <listitem> + <para> + Make voice announcements depend on current tab. + </para> + </listitem> + <listitem> + <para> + Compute adjustment to current travel direction while in + motion towards rocket. + </para> + </listitem> + </itemizedlist> + </para> +</article> diff --git a/doc/telemetry.xsl b/doc/telemetry.xsl index e4101507..2e0b3ea1 100644 --- a/doc/telemetry.xsl +++ b/doc/telemetry.xsl @@ -110,7 +110,7 @@ </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'/> @@ -124,7 +124,7 @@ <tbody> <row> <entry>0x01</entry> - <entry>TeleMetrum Sensor Data</entry> + <entry>TeleMetrum v1.x Sensor Data</entry> </row> <row> <entry>0x02</entry> @@ -138,7 +138,7 @@ </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. @@ -214,6 +214,316 @@ </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'> <tgroup cols='2' align='center' colsep='1' rowsep='1'> @@ -315,7 +625,7 @@ </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> @@ -604,62 +914,169 @@ </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> @@ -668,10 +1085,12 @@ <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> diff --git a/icon/Makefile.am b/icon/Makefile.am index c08e9236..af238ac4 100644 --- a/icon/Makefile.am +++ b/icon/Makefile.am @@ -150,14 +150,21 @@ SUFFIXES=.svg .build .icns .ico .rc .o .exe icotool -c -o $@ $(shell for i in $(WIN_RES); do echo $*-$$i.png; done) .ico.rc: - echo '101 ICON "$*.ico"' > $@ + ./make-rc "$*" $(VERSION) > $@ MINGCC32=i686-w64-mingw32-gcc MINGWINDRES=i686-w64-mingw32-windres +MINGFLAGS=-Wall -DWINDOWS -mwindows +MINGLIBS=-lshlwapi .rc.o: $(MINGWINDRES) $*.rc $@ .o.exe: - $(MINGCC32) -o $@ windows-stub.c $*.o + $(MINGCC32) -o $@ $(MINGFLAGS) windows-stub.o $*.o $(MINGLIBS) + +$(EXE_FILES): windows-stub.o make-rc + +windows-stub.o: windows-stub.c + $(MINGCC32) -c $(MINGFLAGS) windows-stub.c diff --git a/icon/make-rc b/icon/make-rc new file mode 100755 index 00000000..de647278 --- /dev/null +++ b/icon/make-rc @@ -0,0 +1,53 @@ +#!/bin/sh + +COMPANY="Altus Metrum, LLC" +PRODUCT="Altus Metrum" + +case "$1" in + *altosui*) + PRODUCT="AltosUI" + ;; + *telegps*) + PRODUCT="TeleGPS" + ;; + *micropeak*) + PRODUCT="MicroPeak" + ;; +esac + +VERSION="$2" +VERSION_COMMA=`echo "$VERSION" | sed 's/\./,/g'` +INTERNAL_NAME=`basename $1` +EXE_NAME="$INTERNAL_NAME".exe +YEAR=`date +%Y` + +cat <<EOF +101 ICON "$1.ico" +1 VERSIONINFO +FILEVERSION $VERSION_COMMA +PRODUCTVERSION $VERSION_COMMA +FILEFLAGSMASK 0 +FILEOS 0x40004 +FILETYPE 1 +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "Comments", "$COMPANY $PRODUCT" + VALUE "CompanyName", "$COMPANY" + VALUE "FileDescription", "$PRODUCT" + VALUE "FileVersion", "$VERSION" + VALUE "InternalName", "$INTERNAL_NAME" + VALUE "LegalCopyright", "Copyright $YEAR, $COMPANY" + VALUE "OriginalFilename", "$EXE_NAME" + VALUE "ProductName", "$PRODUCT" + VALUE "ProductVersion", "$VERSION" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x409, 1252 + } +} +EOF diff --git a/icon/windows-stub.c b/icon/windows-stub.c index 8df3e0aa..e075a02d 100644 --- a/icon/windows-stub.c +++ b/icon/windows-stub.c @@ -1,2 +1,201 @@ -__stdcall -WinMain(int a, int b, int c, int d) { return 0; } +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +/* A windows stub program to launch a java program with suitable parameters + * + * Given that the name of this exe is altusmetrum-foo.exe living in directory bar, and + * that it was run with 'args' extra command line parameters, run: + * + * javaw.exe -Djava.library.path="bar" -jar "bar/foo-fat.jar" args + */ + +#define _UNICODE +#define UNICODE +#include <stdlib.h> +#include <windows.h> +#include <setupapi.h> +#include <stdio.h> +#include <stdarg.h> +#include <shlwapi.h> + +/* Concatenate a list of strings together + */ +static LPTSTR +wcsbuild(LPTSTR first, ...) +{ + va_list args; + int len; + LPTSTR buf; + LPTSTR arg; + + buf = wcsdup(first); + va_start(args, first); + while ((arg = va_arg(args, LPTSTR)) != NULL) { + len = wcslen(buf) + wcslen(arg) + 1; + buf = realloc(buf, len * sizeof (wchar_t)); + wcscat(buf, arg); + } + va_end(args); + return buf; +} + +/* Quote a single string, taking care to escape embedded quote and + * backslashes within + */ +static LPTSTR +quote_arg(LPTSTR arg) +{ + LPTSTR result; + LPTSTR in, out; + int out_len = 3; /* quotes and terminating null */ + + /* Find quote and backslashes */ + for (in = arg; *in; in++) { + switch (*in) { + case '"': + case '\\': + out_len += 2; + break; + default: + out_len++; + break; + } + } + + result = malloc ((out_len + 1) * sizeof (wchar_t)); + out = result; + *out++ = '"'; + for (in = arg; *in; in++) { + switch (*in) { + case '"': + case '\\': + *out++ = '\\'; + break; + } + *out++ = *in; + } + *out++ = '"'; + *out++ = '\0'; + return result; +} + +/* Construct a single string from a list of arguments + */ +static LPTSTR +quote_args(LPTSTR *argv, int argc) +{ + LPTSTR result = NULL, arg; + int i; + + result = malloc(1 * sizeof (wchar_t)); + result[0] = '\0'; + for (i = 0; i < argc; i++) { + arg = quote_arg(argv[i]); + result = realloc(result, (wcslen(result) + 1 + wcslen(arg) + 1) * sizeof (wchar_t)); + wcscat(result, L" "); + wcscat(result, arg); + free(arg); + } + return result; +} + +/* Return the directory portion of the provided file + */ +static LPTSTR +get_dir(LPTSTR file) +{ + DWORD len = GetFullPathName(file, 0, NULL, NULL); + LPTSTR full = malloc (len * sizeof (wchar_t)); + GetFullPathName(file, len, full, NULL); + PathRemoveFileSpec(full); + return full; +} + +/* Convert a .exe name into a -fat.jar name, starting + * by computing the complete path name of the source filename + */ +static LPTSTR +make_jar(LPTSTR file) +{ + DWORD len = GetFullPathName(file, 0, NULL, NULL); + LPTSTR full = malloc (len * sizeof (wchar_t)); + LPTSTR base_part; + LPTSTR jar; + LPTSTR dot; + GetFullPathName(file, len, full, &base_part); + static const wchar_t head[] = L"altusmetrum-"; + + if (wcsncmp(base_part, head, wcslen(head)) == 0) + base_part += wcslen(head); + dot = wcsrchr(base_part, '.'); + if (dot) + *dot = '\0'; + jar = wcsdup(base_part); + PathRemoveFileSpec(full); + return wcsbuild(full, L"\\", jar, L"-fat.jar", NULL); +} + +/* Build the complete command line from the pieces + */ +static LPTSTR +make_cmd(LPTSTR dir, LPTSTR jar, LPTSTR quote_args) +{ + LPTSTR quote_dir = quote_arg(dir); + LPTSTR quote_jar = quote_arg(jar); + LPTSTR cmd; + + cmd = wcsbuild(L"javaw.exe -Djava.library.path=", quote_dir, L" -jar ", quote_jar, quote_args, NULL); + free(quote_jar); + free(jar); + free(quote_dir); + return cmd; +} + +int WINAPI +WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line_a, int cmd_show) +{ + STARTUPINFO startup_info; + PROCESS_INFORMATION process_information; + BOOL result; + wchar_t *command_line; + int argc; + LPTSTR *argv = CommandLineToArgvW(GetCommandLine(), &argc); + LPTSTR my_dir; + LPTSTR my_jar; + LPTSTR args = quote_args(argv + 1, argc - 1); + + my_dir = get_dir(argv[0]); + my_jar = make_jar(argv[0]); + command_line = make_cmd(my_dir, my_jar, args); + memset(&startup_info, '\0', sizeof startup_info); + startup_info.cb = sizeof startup_info; + result = CreateProcess(NULL, + command_line, + NULL, + NULL, + FALSE, + CREATE_NO_WINDOW, + NULL, + NULL, + &startup_info, + &process_information); + if (result) { + CloseHandle(process_information.hProcess); + CloseHandle(process_information.hThread); + } + exit(0); +} diff --git a/micropeak/Makefile.am b/micropeak/Makefile.am index 15865606..a8834a66 100644 --- a/micropeak/Makefile.am +++ b/micropeak/Makefile.am @@ -124,20 +124,29 @@ FAT_FILES=$(FATJAR) $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) $(FREETTS_CLASS) $(JFR LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) $(DOC) $(desktop_file).in $(LINUX_ICONS) $(LINUX_MIMETYPE) LINUX_EXTRA=micropeak-fat $(desktop_file).in -MACOSX_DRIVER_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg -MACOSX_DRIVER=FTDIUSBSerialDriver_v2_2_18.dmg +MACOSX_DRIVER_0_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg +MACOSX_DRIVER_0=FTDI_v2_2_18.dmg + +MACOSX_DRIVER_1_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_3.dmg +MACOSX_DRIVER_1=FTDI_v2_3.dmg + +MACOSX_DRIVERS=$(MACOSX_DRIVER_1) $(MACOSX_DRIVER_0) + MACOSX_INFO_PLIST=Info.plist MACOSX_README=ReadMe-Mac.rtf -MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVER) $(MACOSX_README) $(DOC) $(MACOSX_ICONS) +MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVERS) $(MACOSX_README) $(DOC) $(MACOSX_ICONS) + +$(MACOSX_DRIVER_0): + wget -O $@ $(MACOSX_DRIVER_0_URL) -$(MACOSX_DRIVER): - wget $(MACOSX_DRIVER_URL) +$(MACOSX_DRIVER_1): + wget -O $@ $(MACOSX_DRIVER_1_URL) -WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM20824_Setup.exe -WINDOWS_DRIVER=CDM20824_Setup.exe +WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM%20v2.12.00%20WHQL%20Certified.exe +WINDOWS_DRIVER=CDM_v2.12.00_WHQL_Certified.exe $(WINDOWS_DRIVER): - wget $(WINDOWS_DRIVER_URL) + wget -O "$(WINDOWS_DRIVER)" "$(WINDOWS_DRIVER_URL)" WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER) @@ -274,7 +283,7 @@ $(MACOSX_DIST): $(MACOSX_FILES) cp -a $(MACOSX_README) macosx/ReadMe.rtf cp -a $(DOC) macosx cp -p Info.plist macosx/MicroPeak.app/Contents - cp -p $(MACOSX_DRIVER) macosx + cp -p $(MACOSX_DRIVERS) macosx mkdir -p macosx/MicroPeak.app/Contents/Resources/Java cp -p $(MACOSX_ICONS) macosx/MicroPeak.app/Contents/Resources cp -p $(FATJAR) macosx/MicroPeak.app/Contents/Resources/Java/micropeak.jar diff --git a/micropeak/MicroData.java b/micropeak/MicroData.java index 857dbe58..62f03c9d 100644 --- a/micropeak/MicroData.java +++ b/micropeak/MicroData.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak; import java.lang.*; import java.io.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; class MicroIterator implements Iterator<MicroDataPoint> { int i; diff --git a/micropeak/MicroDataPoint.java b/micropeak/MicroDataPoint.java index 45d5ba27..1c36fefe 100644 --- a/micropeak/MicroDataPoint.java +++ b/micropeak/MicroDataPoint.java @@ -17,7 +17,7 @@ package org.altusmetrum.micropeak; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class MicroDataPoint implements AltosUIDataPoint { public double time; diff --git a/micropeak/MicroDeviceDialog.java b/micropeak/MicroDeviceDialog.java index 55328482..a6d0f321 100644 --- a/micropeak/MicroDeviceDialog.java +++ b/micropeak/MicroDeviceDialog.java @@ -21,7 +21,7 @@ import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class MicroDeviceDialog extends AltosDeviceDialog { diff --git a/micropeak/MicroDownload.java b/micropeak/MicroDownload.java index a03d7b3a..7314cc7f 100644 --- a/micropeak/MicroDownload.java +++ b/micropeak/MicroDownload.java @@ -23,8 +23,8 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroDownload extends AltosUIDialog implements Runnable, ActionListener, MicroSerialLog, WindowListener { MicroPeak owner; diff --git a/micropeak/MicroExport.java b/micropeak/MicroExport.java index 93c8cae8..d3cfc3b8 100644 --- a/micropeak/MicroExport.java +++ b/micropeak/MicroExport.java @@ -23,8 +23,8 @@ import java.util.ArrayList; import java.awt.*; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroExport extends JFileChooser { diff --git a/micropeak/MicroFile.java b/micropeak/MicroFile.java index 78f7dac2..d48598cb 100644 --- a/micropeak/MicroFile.java +++ b/micropeak/MicroFile.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak; import java.io.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroFile { diff --git a/micropeak/MicroFileChooser.java b/micropeak/MicroFileChooser.java index 5ac22e30..d72d956a 100644 --- a/micropeak/MicroFileChooser.java +++ b/micropeak/MicroFileChooser.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.io.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroFileChooser extends JFileChooser { JFrame frame; diff --git a/micropeak/MicroFrame.java b/micropeak/MicroFrame.java index 47b03a12..9b0f1e7c 100644 --- a/micropeak/MicroFrame.java +++ b/micropeak/MicroFrame.java @@ -21,7 +21,7 @@ import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class MicroFrame extends AltosUIFrame { static String[] micro_icon_names = { diff --git a/micropeak/MicroGraph.java b/micropeak/MicroGraph.java index a6d511c3..c7a2d345 100644 --- a/micropeak/MicroGraph.java +++ b/micropeak/MicroGraph.java @@ -22,8 +22,8 @@ import java.util.ArrayList; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; import org.jfree.ui.*; import org.jfree.chart.*; diff --git a/micropeak/MicroPeak.java b/micropeak/MicroPeak.java index 9f7095f3..2df81621 100644 --- a/micropeak/MicroPeak.java +++ b/micropeak/MicroPeak.java @@ -23,8 +23,8 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroPeak extends MicroFrame implements ActionListener, ItemListener { diff --git a/micropeak/MicroRaw.java b/micropeak/MicroRaw.java index 587fe927..6aae0bde 100644 --- a/micropeak/MicroRaw.java +++ b/micropeak/MicroRaw.java @@ -20,8 +20,8 @@ package org.altusmetrum.micropeak; import java.awt.*; import java.io.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroRaw extends JTextArea { diff --git a/micropeak/MicroSave.java b/micropeak/MicroSave.java index 65026bda..d8fe59c2 100644 --- a/micropeak/MicroSave.java +++ b/micropeak/MicroSave.java @@ -24,8 +24,8 @@ import javax.swing.filechooser.FileNameExtensionFilter; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroSave extends JFileChooser { diff --git a/micropeak/MicroSerial.java b/micropeak/MicroSerial.java index 4282aba1..8d20718f 100644 --- a/micropeak/MicroSerial.java +++ b/micropeak/MicroSerial.java @@ -20,7 +20,7 @@ package org.altusmetrum.micropeak; import java.util.*; import java.io.*; import libaltosJNI.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class MicroSerial extends InputStream { SWIGTYPE_p_altos_file file; diff --git a/micropeak/MicroSerialLog.java b/micropeak/MicroSerialLog.java index 3ee44649..c7a0525d 100644 --- a/micropeak/MicroSerialLog.java +++ b/micropeak/MicroSerialLog.java @@ -20,7 +20,7 @@ package org.altusmetrum.micropeak; import java.util.*; import java.io.*; import libaltosJNI.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public interface MicroSerialLog { diff --git a/micropeak/MicroStats.java b/micropeak/MicroStats.java index 47ef2a79..8d83fe3e 100644 --- a/micropeak/MicroStats.java +++ b/micropeak/MicroStats.java @@ -18,8 +18,8 @@ package org.altusmetrum.micropeak; import java.io.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroStats { double coast_height; diff --git a/micropeak/MicroStatsTable.java b/micropeak/MicroStatsTable.java index 139c4416..35d10eee 100644 --- a/micropeak/MicroStatsTable.java +++ b/micropeak/MicroStatsTable.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroStatsTable extends JComponent implements AltosFontListener { GridBagLayout layout; diff --git a/micropeak/MicroUSB.java b/micropeak/MicroUSB.java index fa20488d..3a7891cb 100644 --- a/micropeak/MicroUSB.java +++ b/micropeak/MicroUSB.java @@ -19,8 +19,8 @@ package org.altusmetrum.micropeak; import java.util.*; import libaltosJNI.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class MicroUSB extends altos_device implements AltosDevice { diff --git a/micropeak/micropeak-windows.nsi.in b/micropeak/micropeak-windows.nsi.in index a3779630..c3b63f7b 100644 --- a/micropeak/micropeak-windows.nsi.in +++ b/micropeak/micropeak-windows.nsi.in @@ -65,9 +65,9 @@ UninstPage instfiles Section "FTDI USB Driver" SetOutPath $INSTDIR - File "CDM20824_Setup.exe" + File "CDM_v2.12.00_WHQL_Certified.exe" - StrCpy $2 "$INSTDIR\CDM20824_Setup.exe" + StrCpy $2 "$INSTDIR\CDM_v2.12.00_WHQL_Certified.exe" ExecWait $2 SectionEnd @@ -104,16 +104,17 @@ Section "${REG_NAME} Application" File "altosuilib_@ALTOSUILIB_VERSION@.jar" File "jfreechart.jar" File "jcommon.jar" + File "../icon/${WIN_APP_EXE}" File "*.dll" File "../icon/${WIN_APP_ICON}" - CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "${REG_NAME} Desktop Shortcut" - CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "Documentation" @@ -129,11 +130,8 @@ Section "File Associations" SetOutPath $INSTDIR - File "../icon/${WIN_APP_EXE}" File "../icon/${WIN_MPD_EXE}" - SearchPath $1 "javaw.exe" - ; application elements DeleteRegKey HKCR "${PROG_ID}" @@ -143,7 +141,7 @@ Section "File Associations" WriteRegStr HKCR "${PROG_ID_MPD}" "FriendlyTypeName" "MicroPeak Data File" WriteRegStr HKCR "${PROG_ID_MPD}\CurVer" "" "${PROG_ID_MPD}" WriteRegStr HKCR "${PROG_ID_MPD}\DefaultIcon" "" '"$INSTDIR\${WIN_MPD_EXE}",-101' - WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"' + WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"' ; .mpd elements diff --git a/src/Makefile b/src/Makefile index 05e99c7f..dc74bf8c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -34,13 +34,15 @@ ARMM3DIRS=\ telegps-v0.3 telegps-v0.3/flash-loader \ telegps-v1.0 telegps-v1.0/flash-loader \ telelco-v0.2 telelco-v0.2/flash-loader \ + telelco-v0.3 telelco-v0.3/flash-loader \ telescience-v0.2 telescience-v0.2/flash-loader \ teledongle-v3.0 teledongle-v3.0/flash-loader \ teleballoon-v2.0 \ telebt-v3.0 telebt-v3.0/flash-loader ARMM0DIRS=\ - easymini-v1.0 easymini-v1.0/flash-loader + easymini-v1.0 easymini-v1.0/flash-loader \ + chaoskey-v0.1 chaoskey-v0.1/flash-loader AVRDIRS=\ telescience-v0.1 telescience-pwm micropeak nanopeak-v0.1 microkite diff --git a/src/cc1111/ao_arch.h b/src/cc1111/ao_arch.h index b3c6b5dc..6eb1a111 100644 --- a/src/cc1111/ao_arch.h +++ b/src/cc1111/ao_arch.h @@ -228,7 +228,7 @@ void ao_button_init(void); char -ao_button_get(void) __critical; +ao_button_get(uint16_t timeout) __critical; void ao_button_clear(void) __critical; diff --git a/src/cc1111/ao_button.c b/src/cc1111/ao_button.c index 69f3475f..a0f221c2 100644 --- a/src/cc1111/ao_button.c +++ b/src/cc1111/ao_button.c @@ -72,12 +72,12 @@ ao_button_mask(uint8_t reg) } char -ao_button_get(void) __critical +ao_button_get(uint16_t timeout) __critical { char b; while (ao_fifo_empty(ao_button_fifo)) - if (ao_sleep(&ao_button_fifo)) + if (ao_sleep_for(&ao_button_fifo, timeout)) return 0; ao_fifo_remove(ao_button_fifo, b); return b; diff --git a/src/cc1111/ao_radio.c b/src/cc1111/ao_radio.c index b9821a42..cead0364 100644 --- a/src/cc1111/ao_radio.c +++ b/src/cc1111/ao_radio.c @@ -451,13 +451,9 @@ ao_radio_recv(__xdata void *packet, uint8_t size, uint8_t timeout) __reentrant /* Wait for DMA to be done, for the radio receive process to * get aborted or for a receive timeout to fire */ - if (timeout) - ao_alarm(timeout); __critical while (!ao_radio_dma_done && !ao_radio_abort) - if (ao_sleep(&ao_radio_dma_done)) + if (ao_sleep_for(&ao_radio_dma_done, timeout)) break; - if (timeout) - ao_clear_alarm(); /* If recv was aborted, clean up by stopping the DMA engine * and idling the radio diff --git a/src/chaoskey-v0.1/.gitignore b/src/chaoskey-v0.1/.gitignore new file mode 100644 index 00000000..b0adba26 --- /dev/null +++ b/src/chaoskey-v0.1/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +chaoskey-* diff --git a/src/chaoskey-v0.1/Makefile b/src/chaoskey-v0.1/Makefile new file mode 100644 index 00000000..ac4a6788 --- /dev/null +++ b/src/chaoskey-v0.1/Makefile @@ -0,0 +1,70 @@ +# +# AltOS build +# +# + +include ../stmf0/Makefile.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_pins.h \ + ao_product.h \ + ao_task.h \ + ao_adc_fast.h \ + stm32f0.h + +# +# Common AltOS sources +# +ALTOS_SRC = \ + ao_interrupt.c \ + ao_timer.c \ + ao_panic.c \ + ao_mutex.c \ + ao_dma_stm.c \ + ao_adc_fast.c \ + ao_crc_stm.c \ + ao_stdio.c \ + ao_led.c \ + ao_romconfig.c \ + ao_boot_chain.c \ + ao_usb_stm.c \ + ao_trng_send.c \ + ao_task.c \ + ao_product.c + +PRODUCT=ChaosKey-v0.1 +PRODUCT_DEF=-DCHAOSKEY_V_0_1 +IDVENDOR=0x1d50 +IDPRODUCT=0x60c6 + +CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os + +PROGNAME=chaoskey-v0.1 +PROG=$(PROGNAME)-$(VERSION).elf +HEX=$(PROGNAME)-$(VERSION).ihx + +SRC=$(ALTOS_SRC) ao_chaoskey.c +OBJ=$(SRC:.c=.o) + +all: $(PROG) $(HEX) + +$(PROG): Makefile $(OBJ) altos.ld + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +ao_product.h: ao-make-product.5c ../Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -V $(IDVENDOR) -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) -o $@ + +$(OBJ): $(INC) + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/chaoskey-v0.1/ao_chaoskey.c b/src/chaoskey-v0.1/ao_chaoskey.c new file mode 100644 index 00000000..48c8bf04 --- /dev/null +++ b/src/chaoskey-v0.1/ao_chaoskey.c @@ -0,0 +1,41 @@ +/* + * Copyright © 2014 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> +#include <ao_adc_fast.h> +#include <ao_crc.h> +#include <ao_trng_send.h> + +void main(void) +{ + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_RED); + ao_clock_init(); + ao_task_init(); + ao_timer_init(); + ao_dma_init(); + ao_adc_init(); + ao_crc_init(); + + ao_usb_init(); + + ao_trng_send_init(); + + ao_led_off(AO_LED_RED); + + ao_start_scheduler(); +} diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h new file mode 100644 index 00000000..72963dba --- /dev/null +++ b/src/chaoskey-v0.1/ao_pins.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#define LED_PORT_ENABLE STM_RCC_AHBENR_IOPAEN +#define LED_PORT (&stm_gpioa) +#define LED_PIN_RED 2 +#define LED_PIN_GREEN 3 +#define AO_LED_RED (1 << LED_PIN_RED) +#define AO_LED_GREEN (1 << LED_PIN_GREEN) + +#define LEDS_AVAILABLE (AO_LED_RED | AO_LED_GREEN) + +#define HAS_BEEP 0 + +/* 48MHz clock based on USB */ +#define AO_HSI48 1 + +/* HCLK = 48MHz */ +#define AO_AHB_PRESCALER 1 +#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1 + +/* APB = 48MHz */ +#define AO_APB_PRESCALER 1 +#define AO_RCC_CFGR_PPRE_DIV STM_RCC_CFGR_PPRE_DIV_1 + +#define HAS_USB 1 +#define AO_USB_DIRECTIO 1 +#define AO_PA11_PA12_RMP 0 +#define AO_USB_INTERFACE_CLASS 0xff + +#define IS_FLASH_LOADER 0 + +/* ADC */ + +#define AO_ADC_PIN0_PORT (&stm_gpioa) +#define AO_ADC_PIN0_PIN 6 +#define AO_ADC_PIN0_CH 6 + +#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_IOPAEN)) + +#define AO_NUM_ADC 1 + +/* CRC */ +#define AO_CRC_WIDTH 32 +#define AO_CRC_INIT 0xffffffff + +/* TRNG */ +#define AO_LED_TRNG_ACTIVE AO_LED_GREEN + +#endif /* _AO_PINS_H_ */ diff --git a/src/chaoskey-v0.1/flash-loader/.gitignore b/src/chaoskey-v0.1/flash-loader/.gitignore new file mode 100644 index 00000000..a60a4945 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +chaoskey* diff --git a/src/chaoskey-v0.1/flash-loader/Makefile b/src/chaoskey-v0.1/flash-loader/Makefile new file mode 100644 index 00000000..4f61a240 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=chaoskey-v0.1 +include $(TOPDIR)/stmf0/Makefile-flash.defs diff --git a/src/chaoskey-v0.1/flash-loader/ao_pins.h b/src/chaoskey-v0.1/flash-loader/ao_pins.h new file mode 100644 index 00000000..295e0258 --- /dev/null +++ b/src/chaoskey-v0.1/flash-loader/ao_pins.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +#include <ao_flash_stm_pins.h> + +/* Pin 5 on debug connector */ + +#define AO_BOOT_PIN 1 +#define AO_BOOT_APPLICATION_GPIO stm_gpioa +#define AO_BOOT_APPLICATION_PIN 15 +#define AO_BOOT_APPLICATION_VALUE 1 +#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP + +/* USB */ +#define HAS_USB 1 +#define AO_USB_DIRECTIO 0 +#define AO_PA11_PA12_RMP 0 + +#endif /* _AO_PINS_H_ */ diff --git a/src/drivers/ao_aprs.c b/src/drivers/ao_aprs.c index 19beb78f..a8016673 100644 --- a/src/drivers/ao_aprs.c +++ b/src/drivers/ao_aprs.c @@ -707,8 +707,7 @@ static int tncPositionPacket(void) static int32_t latitude; static int32_t longitude; static int32_t altitude; - int32_t lat, lon, alt; - uint8_t *buf; + uint8_t *buf; if (ao_gps_data.flags & AO_GPS_VALID) { latitude = ao_gps_data.latitude; @@ -719,28 +718,99 @@ static int tncPositionPacket(void) } buf = tncBuffer; - *buf++ = '!'; - /* Symbol table ID */ - *buf++ = '/'; +#ifdef AO_APRS_TEST +#define AO_APRS_FORMAT_COMPRESSED 0 +#define AO_APRS_FORMAT_UNCOMPRESSED 1 + switch (AO_APRS_FORMAT_COMPRESSED) { +#else + switch (ao_config.aprs_format) { +#endif + case AO_APRS_FORMAT_COMPRESSED: + default: + { + int32_t lat, lon, alt; + + *buf++ = '!'; + + /* Symbol table ID */ + *buf++ = '/'; + + lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000; + lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000; + + alt = ao_aprs_encode_altitude(altitude); - lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000; - lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000; + tncCompressInt(buf, lat, 4); + buf += 4; + tncCompressInt(buf, lon, 4); + buf += 4; - alt = ao_aprs_encode_altitude(altitude); + /* Symbol code */ + *buf++ = '\''; - tncCompressInt(buf, lat, 4); - buf += 4; - tncCompressInt(buf, lon, 4); - buf += 4; + tncCompressInt(buf, alt, 2); + buf += 2; - /* Symbol code */ - *buf++ = '\''; + *buf++ = 33 + ((1 << 5) | (2 << 3)); - tncCompressInt(buf, alt, 2); - buf += 2; + break; + } + case AO_APRS_FORMAT_UNCOMPRESSED: + { + char lat_sign = 'N', lon_sign = 'E'; + int32_t lat = latitude; + int32_t lon = longitude; + int32_t alt = altitude; + uint16_t lat_deg; + uint16_t lon_deg; + uint16_t lat_min; + uint16_t lat_frac; + uint16_t lon_min; + uint16_t lon_frac; + + if (lat < 0) { + lat_sign = 'S'; + lat = -lat; + } - *buf++ = 33 + ((1 << 5) | (2 << 3)); + if (lon < 0) { + lon_sign = 'W'; + lon = -lon; + } + + /* Round latitude and longitude by 0.005 minutes */ + lat = lat + 833; + if (lat > 900000000) + lat = 900000000; + lon = lon + 833; + if (lon > 1800000000) + lon = 1800000000; + + lat_deg = lat / 10000000; + lat -= lat_deg * 10000000; + lat *= 60; + lat_min = lat / 10000000; + lat -= lat_min * 10000000; + lat_frac = lat / 100000; + + lon_deg = lon / 10000000; + lon -= lon_deg * 10000000; + lon *= 60; + lon_min = lon / 10000000; + lon -= lon_min * 10000000; + lon_frac = lon / 100000; + + /* Convert from meters to feet */ + alt = (alt * 328 + 50) / 100; + + buf += sprintf((char *) tncBuffer, "!%02u%02u.%02u%c/%03u%02u.%02u%c'/A=%06u ", + lat_deg, lat_min, lat_frac, lat_sign, + lon_deg, lon_min, lon_frac, lon_sign, + alt); + break; + } + } buf += tncComment(buf); diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c index e6b28688..8e7052cb 100644 --- a/src/drivers/ao_btm.c +++ b/src/drivers/ao_btm.c @@ -23,7 +23,7 @@ #ifndef ao_serial_btm_getchar #define ao_serial_btm_putchar ao_serial1_putchar #define _ao_serial_btm_pollchar _ao_serial1_pollchar -#define _ao_serial_btm_sleep() ao_sleep((void *) &ao_serial1_rx_fifo) +#define _ao_serial_btm_sleep_for(timeout) ao_sleep_for((void *) &ao_serial1_rx_fifo, timeout) #define ao_serial_btm_set_speed ao_serial1_set_speed #define ao_serial_btm_drain ao_serial1_drain #endif @@ -111,7 +111,7 @@ ao_btm_do_echo(void) while (ao_btm_enable) { ao_arch_block_interrupts(); while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN && ao_btm_enable) - _ao_serial_btm_sleep(); + _ao_serial_btm_sleep_for(0); ao_arch_release_interrupts(); if (c != AO_READ_AGAIN) { putchar(c); @@ -166,9 +166,7 @@ ao_btm_getchar(void) ao_arch_block_interrupts(); while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN) { - ao_alarm(AO_MS_TO_TICKS(10)); - c = _ao_serial_btm_sleep(); - ao_clear_alarm(); + c = _ao_serial_btm_sleep_for(AO_MS_TO_TICKS(10)); if (c) { c = AO_READ_AGAIN; break; @@ -265,6 +263,15 @@ uint8_t ao_btm_cmd(__code char *cmd) { ao_btm_drain(); + +#ifdef AO_BTM_INT_PORT + /* Trust that AltosDroid will eventually disconnect and let us + * get things set up. The BTM module doesn't appear to listen + * for +++, so we have no way to force a disconnect. + */ + while (ao_btm_connected) + ao_sleep(&ao_btm_connected); +#endif ao_btm_string(cmd); return ao_btm_wait_reply(); } @@ -352,6 +359,10 @@ __xdata struct ao_task ao_btm_task; void ao_btm(void) { +#ifdef AO_BTM_INT_PORT + ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN); +#endif + /* * Wait for the bluetooth device to boot */ @@ -382,6 +393,8 @@ ao_btm(void) /* Turn off status reporting */ ao_btm_cmd("ATQ1\r"); + ao_btm_drain(); + ao_btm_stdio = ao_add_stdio(_ao_serial_btm_pollchar, ao_serial_btm_putchar, NULL); @@ -390,10 +403,6 @@ ao_btm(void) /* Check current pin state */ ao_btm_check_link(); -#ifdef AO_BTM_INT_PORT - ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN); -#endif - for (;;) { while (!ao_btm_connected) ao_sleep(&ao_btm_connected); diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c index 90d6cc75..5b814667 100644 --- a/src/drivers/ao_cc1120.c +++ b/src/drivers/ao_cc1120.c @@ -837,15 +837,11 @@ ao_radio_test_cmd(void) static void ao_radio_wait_isr(uint16_t timeout) { - if (timeout) - ao_alarm(timeout); ao_arch_block_interrupts(); while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort) - if (ao_sleep(&ao_radio_wake)) + if (ao_sleep_for(&ao_radio_wake, timeout)) ao_radio_abort = 1; ao_arch_release_interrupts(); - if (timeout) - ao_clear_alarm(); if (ao_radio_mcu_wake) ao_radio_check_marc_status(); } @@ -1060,19 +1056,17 @@ ao_radio_rx_isr(void) static uint16_t ao_radio_rx_wait(void) { - ao_alarm(AO_MS_TO_TICKS(100)); ao_arch_block_interrupts(); rx_waiting = 1; while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK && !ao_radio_abort && !ao_radio_mcu_wake) { - if (ao_sleep(&ao_radio_wake)) + if (ao_sleep_for(&ao_radio_wake, AO_MS_TO_TICKS(100))) ao_radio_abort = 1; } rx_waiting = 0; ao_arch_release_interrupts(); - ao_clear_alarm(); if (ao_radio_abort || ao_radio_mcu_wake) return 0; rx_data_consumed += AO_FEC_DECODE_BLOCK; @@ -1133,19 +1127,15 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout) ao_radio_strobe(CC1120_SRX); - if (timeout) - ao_alarm(timeout); ao_arch_block_interrupts(); while (rx_starting && !ao_radio_abort) { - if (ao_sleep(&ao_radio_wake)) + if (ao_sleep_for(&ao_radio_wake, timeout)) ao_radio_abort = 1; } uint8_t rx_task_id_save = rx_task_id; rx_task_id = 0; rx_starting = 0; ao_arch_release_interrupts(); - if (timeout) - ao_clear_alarm(); if (ao_radio_abort) { if (rx_task_id_save == 0) diff --git a/src/drivers/ao_cc1200.c b/src/drivers/ao_cc1200.c index 8546900e..6547be39 100644 --- a/src/drivers/ao_cc1200.c +++ b/src/drivers/ao_cc1200.c @@ -41,7 +41,11 @@ int8_t ao_radio_rssi; /* Last received RSSI value */ extern const uint32_t ao_radio_cal; +#ifdef AO_CC1200_FOSC +#define FOSC AO_CC1200_FOSC +#else #define FOSC 40000000 +#endif #define ao_radio_select() ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST) #define ao_radio_deselect() ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS) @@ -301,20 +305,28 @@ ao_radio_idle(void) * CHANBW = 5.0 (round to 9.5) */ +#if FOSC == 40000000 #define PACKET_SYMBOL_RATE_M 1013008 - #define PACKET_SYMBOL_RATE_E_384 8 +#define PACKET_SYMBOL_RATE_E_96 6 +#define PACKET_SYMBOL_RATE_E_24 4 +#endif + +#if FOSC == 32000000 +#define PACKET_SYMBOL_RATE_M 239914 +#define PACKET_SYMBOL_RATE_E_384 9 +#define PACKET_SYMBOL_RATE_E_96 7 +#define PACKET_SYMBOL_RATE_E_24 5 +#endif /* 200 / 2 = 100 */ #define PACKET_CHAN_BW_384 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_12 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \ (16 << CC1200_CHAN_BW_BB_CIC_DECFACT)) -#define PACKET_SYMBOL_RATE_E_96 6 /* 200 / 10 = 20 */ #define PACKET_CHAN_BW_96 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \ (16 << CC1200_CHAN_BW_BB_CIC_DECFACT)) -#define PACKET_SYMBOL_RATE_E_24 4 /* 200 / 25 = 8 */ #define PACKET_CHAN_BW_24 ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \ (44 << CC1200_CHAN_BW_BB_CIC_DECFACT)) @@ -715,17 +727,11 @@ ao_radio_show_state(char *where) static void ao_radio_wait_isr(uint16_t timeout) { - if (timeout) - ao_alarm(timeout); - ao_arch_block_interrupts(); while (!ao_radio_wake && !ao_radio_abort) - if (ao_sleep(&ao_radio_wake)) + if (ao_sleep_for(&ao_radio_wake, timeout)) ao_radio_abort = 1; ao_arch_release_interrupts(); - - if (timeout) - ao_clear_alarm(); } static void diff --git a/src/drivers/ao_cc1200.h b/src/drivers/ao_cc1200.h index b04775fd..b2b63cde 100644 --- a/src/drivers/ao_cc1200.h +++ b/src/drivers/ao_cc1200.h @@ -438,6 +438,38 @@ #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) diff --git a/src/drivers/ao_cc1200_CC1200.h b/src/drivers/ao_cc1200_CC1200.h index 35673123..f0214c2a 100644 --- a/src/drivers/ao_cc1200_CC1200.h +++ b/src/drivers/ao_cc1200_CC1200.h @@ -101,6 +101,15 @@ (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] */ diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c index 570b9e40..7e02939b 100644 --- a/src/drivers/ao_companion.c +++ b/src/drivers/ao_companion.c @@ -102,8 +102,7 @@ ao_companion(void) break; } while (ao_companion_running) { - ao_alarm(ao_companion_setup.update_period); - if (ao_sleep(DATA_TO_XDATA(&ao_flight_state))) + if (ao_sleep_for(DATA_TO_XDATA(&ao_flight_state), ao_companion_setup.update_period)) ao_companion_get_data(); else ao_companion_notify(); diff --git a/src/drivers/ao_hmc5883.c b/src/drivers/ao_hmc5883.c index 2d217bcf..f761671a 100644 --- a/src/drivers/ao_hmc5883.c +++ b/src/drivers/ao_hmc5883.c @@ -75,13 +75,11 @@ ao_hmc5883_sample(struct ao_hmc5883_sample *sample) ao_exti_enable(AO_HMC5883_INT_PORT, AO_HMC5883_INT_PIN); ao_hmc5883_reg_write(HMC5883_MODE, HMC5883_MODE_SINGLE); - ao_alarm(AO_MS_TO_TICKS(10)); ao_arch_block_interrupts(); while (!ao_hmc5883_done) - if (ao_sleep(&ao_hmc5883_done)) + if (ao_sleep_for(&ao_hmc5883_done, AO_MS_TO_TICKS(10))) ++ao_hmc5883_missed_irq; ao_arch_release_interrupts(); - ao_clear_alarm(); ao_hmc5883_read(HMC5883_X_MSB, (uint8_t *) sample, sizeof (struct ao_hmc5883_sample)); #if __BYTE_ORDER == __LITTLE_ENDIAN diff --git a/src/telelco-v0.2/ao_lco.c b/src/drivers/ao_lco.c index 4b5f7a9b..b8698a80 100644 --- a/src/telelco-v0.2/ao_lco.c +++ b/src/drivers/ao_lco.c @@ -50,7 +50,7 @@ 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); + ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad); } static void @@ -60,6 +60,30 @@ ao_lco_set_box(uint8_t box) 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) @@ -79,9 +103,12 @@ 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) + /* 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; + return (ao_pad_query.channels >> (pad - 1)) & 1; } static uint8_t @@ -89,7 +116,7 @@ ao_lco_pad_first(void) { uint8_t pad; - for (pad = 0; pad < AO_PAD_MAX_CHANNELS; pad++) + for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++) if (ao_lco_pad_present(pad)) return pad; return 0; @@ -117,14 +144,14 @@ ao_lco_input(void) 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 < 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_pad(ao_lco_pad); + ao_lco_set_display(); } } break; @@ -143,8 +170,9 @@ ao_lco_input(void) } 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_box(ao_lco_box); + ao_lco_set_display(); } } break; @@ -209,9 +237,12 @@ ao_lco_update(void) 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); + 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; @@ -223,6 +254,7 @@ ao_lco_update(void) query.igniter_status[2], query.igniter_status[3]); #endif + PRINTD("ao_lco_update valid %d\n", ao_lco_valid); ao_wakeup(&ao_pad_query); } @@ -253,8 +285,10 @@ ao_lco_search(void) 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); @@ -263,7 +297,9 @@ ao_lco_search(void) 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; } @@ -275,9 +311,8 @@ ao_lco_search(void) 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); + ao_lco_pad = 1; + ao_lco_set_display(); } static void @@ -287,12 +322,12 @@ ao_lco_igniter_status(void) 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; } - 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); @@ -353,15 +388,18 @@ ao_lco_monitor(void) 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); + 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(); - ao_lco_arm(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset); - 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(); } @@ -369,9 +407,7 @@ ao_lco_monitor(void) delay = AO_MS_TO_TICKS(100); else delay = AO_SEC_TO_TICKS(1); - ao_alarm(delay); - ao_sleep(&ao_lco_armed); - ao_clear_alarm(); + ao_sleep_for(&ao_lco_armed, delay); } } diff --git a/src/telelco-v0.2/ao_lco.h b/src/drivers/ao_lco.h index 253f9702..253f9702 100644 --- a/src/telelco-v0.2/ao_lco.h +++ b/src/drivers/ao_lco.h diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c index 8cdf85a9..18330ead 100644 --- a/src/drivers/ao_packet.c +++ b/src/drivers/ao_packet.c @@ -54,14 +54,14 @@ ao_packet_send(void) } uint8_t -ao_packet_recv(void) +ao_packet_recv(uint16_t timeout) { uint8_t dma_done; #ifdef AO_LED_GREEN ao_led_on(AO_LED_GREEN); #endif - dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv), 0); + dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv), timeout); #ifdef AO_LED_GREEN ao_led_off(AO_LED_GREEN); #endif diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c index 42a4f5bf..5e440db0 100644 --- a/src/drivers/ao_packet_master.c +++ b/src/drivers/ao_packet_master.c @@ -97,9 +97,7 @@ ao_packet_master(void) if (ao_tx_packet.len) ao_packet_master_busy(); ao_packet_master_check_busy(); - ao_alarm(AO_PACKET_MASTER_RECV_DELAY); - r = ao_packet_recv(); - ao_clear_alarm(); + r = ao_packet_recv(AO_PACKET_MASTER_RECV_DELAY); if (r) { /* if we can transmit data, do so */ if (ao_packet_tx_used && ao_tx_packet.len == 0) @@ -107,9 +105,7 @@ ao_packet_master(void) if (ao_rx_packet.packet.len) ao_packet_master_busy(); ao_packet_master_sleeping = 1; - ao_alarm(ao_packet_master_delay); - ao_sleep(&ao_packet_master_sleeping); - ao_clear_alarm(); + ao_sleep_for(&ao_packet_master_sleeping, ao_packet_master_delay); ao_packet_master_sleeping = 0; } } diff --git a/src/drivers/ao_packet_slave.c b/src/drivers/ao_packet_slave.c index e75df0d6..0872682f 100644 --- a/src/drivers/ao_packet_slave.c +++ b/src/drivers/ao_packet_slave.c @@ -24,7 +24,7 @@ ao_packet_slave(void) ao_tx_packet.len = AO_PACKET_SYN; ao_packet_restart = 1; while (ao_packet_enable) { - if (ao_packet_recv()) { + if (ao_packet_recv(0)) { ao_xmemcpy(&ao_tx_packet.callsign, &ao_rx_packet.packet.callsign, AO_MAX_CALLSIGN); #if HAS_FLIGHT ao_flight_force_idle = TRUE; diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c index dc2c83fe..ffe46c68 100644 --- a/src/drivers/ao_pad.c +++ b/src/drivers/ao_pad.c @@ -29,6 +29,10 @@ static __pdata uint8_t ao_pad_box; static __xdata uint8_t ao_pad_disabled; static __pdata uint16_t ao_pad_packet_time; +#ifndef AO_PAD_RSSI_MINIMUM +#define AO_PAD_RSSI_MINIMUM -90 +#endif + #define DEBUG 1 #if DEBUG @@ -36,8 +40,8 @@ static __pdata uint8_t ao_pad_debug; #define PRINTD(...) (ao_pad_debug ? (printf(__VA_ARGS__), 0) : 0) #define FLUSHD() (ao_pad_debug ? (flush(), 0) : 0) #else -#define PRINTD(...) -#define FLUSHD() +#define PRINTD(...) +#define FLUSHD() #endif static void @@ -123,6 +127,8 @@ ao_pad_monitor(void) #define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0)) + /* convert ADC value to voltage in tenths, then add .2 for the diode drop */ + query.battery = (packet->adc.batt + 96) / 192 + 2; cur = 0; if (pyro > VOLTS_TO_PYRO(10)) { query.arm_status = AO_PAD_ARM_STATUS_ARMED; @@ -138,7 +144,7 @@ ao_pad_monitor(void) } if ((ao_time() - ao_pad_packet_time) > AO_SEC_TO_TICKS(2)) cur |= AO_LED_RED; - else if (ao_radio_cmac_rssi < -90) + else if (ao_radio_cmac_rssi < AO_PAD_RSSI_MINIMUM) cur |= AO_LED_AMBER; else cur |= AO_LED_GREEN; @@ -255,7 +261,7 @@ ao_pad(void) if (ret != AO_RADIO_CMAC_OK) continue; ao_pad_packet_time = ao_time(); - + ao_pad_box = ao_pad_read_box(); PRINTD ("tick %d box %d (me %d) cmd %d channels %02x\n", diff --git a/src/drivers/ao_pad.h b/src/drivers/ao_pad.h index 23062899..d77d105a 100644 --- a/src/drivers/ao_pad.h +++ b/src/drivers/ao_pad.h @@ -39,6 +39,7 @@ struct ao_pad_query { uint8_t channels; /* which chanels are present */ uint8_t armed; /* which channels are armed */ uint8_t arm_status; /* status of arming switch */ + uint8_t battery; /* battery voltage in decivolts */ uint8_t igniter_status[AO_PAD_MAX_CHANNELS]; /* status for each igniter */ }; diff --git a/src/drivers/ao_trng.c b/src/drivers/ao_trng.c new file mode 100644 index 00000000..e69cd30b --- /dev/null +++ b/src/drivers/ao_trng.c @@ -0,0 +1,79 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> +#include <ao_adc_fast.h> +#include <ao_crc.h> +#include <ao_trng.h> + +static void +ao_trng_fetch(void) +{ + static uint16_t *buffer[2]; + uint32_t kbytes = 1; + uint32_t count; + int usb_buf_id; + uint16_t i; + uint16_t *buf; + uint16_t t; + uint32_t *rnd = (uint32_t *) ao_adc_ring; + + if (!buffer[0]) { + buffer[0] = ao_usb_alloc(); + buffer[1] = ao_usb_alloc(); + if (!buffer[0]) + return; + } + + ao_cmd_decimal(); + if (ao_cmd_status == ao_cmd_success) + kbytes = ao_cmd_lex_u32; + else + ao_cmd_status = ao_cmd_success; + usb_buf_id = 0; + count = kbytes * (1024/AO_USB_IN_SIZE); + + ao_crc_reset(); + + ao_led_on(AO_LED_TRNG_READ); + while (count--) { + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + buf = buffer[usb_buf_id]; + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = ao_crc_in_32_out_16(rnd[t]); + t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + } + ao_adc_ack(AO_USB_IN_SIZE); + ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE); + ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); + ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE); + usb_buf_id = 1-usb_buf_id; + } + ao_led_off(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE); + flush(); +} + +static const struct ao_cmds ao_trng_cmds[] = { + { ao_trng_fetch, "f <kbytes>\0Fetch a block of numbers" }, + { 0, NULL }, +}; + +void +ao_trng_init(void) +{ + ao_cmd_register(ao_trng_cmds); +} diff --git a/src/drivers/ao_trng.h b/src/drivers/ao_trng.h new file mode 100644 index 00000000..78577428 --- /dev/null +++ b/src/drivers/ao_trng.h @@ -0,0 +1,24 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_TRNG_H_ +#define _AO_TRNG_H_ + +void +ao_trng_init(void); + +#endif /* _AO_TRNG_H_ */ diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c new file mode 100644 index 00000000..bac6035c --- /dev/null +++ b/src/drivers/ao_trng_send.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> +#include <ao_adc_fast.h> +#include <ao_crc.h> +#include <ao_trng_send.h> + +static void +ao_trng_send(void) +{ + static uint16_t *buffer[2]; + int usb_buf_id; + uint16_t i; + uint16_t *buf; + uint16_t t; + uint32_t *rnd = (uint32_t *) ao_adc_ring; + + if (!buffer[0]) { + buffer[0] = ao_usb_alloc(); + buffer[1] = ao_usb_alloc(); + if (!buffer[0]) + return; + } + + usb_buf_id = 0; + + ao_crc_reset(); + + for (;;) { + ao_led_on(AO_LED_TRNG_ACTIVE); + t = ao_adc_get(AO_USB_IN_SIZE) >> 1; /* one 16-bit value per output byte */ + buf = buffer[usb_buf_id]; + for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) { + *buf++ = ao_crc_in_32_out_16(rnd[t]); + t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1); + } + ao_adc_ack(AO_USB_IN_SIZE); + ao_led_off(AO_LED_TRNG_ACTIVE); + ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); + usb_buf_id = 1-usb_buf_id; + } +} + +static struct ao_task ao_trng_send_task; + +void +ao_trng_send_init(void) +{ + ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send"); +} diff --git a/src/drivers/ao_trng_send.h b/src/drivers/ao_trng_send.h new file mode 100644 index 00000000..83312d59 --- /dev/null +++ b/src/drivers/ao_trng_send.h @@ -0,0 +1,24 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_TRNG_SEND_H_ +#define _AO_TRNG_SEND_H_ + +void +ao_trng_send_init(void); + +#endif /* _AO_TRNG_SEND_H_ */ diff --git a/src/kernel/ao_config.c b/src/kernel/ao_config.c index 8dab7c42..b0d3e541 100644 --- a/src/kernel/ao_config.c +++ b/src/kernel/ao_config.c @@ -220,6 +220,10 @@ _ao_config_get(void) if (minor < 21) ao_config.send_frequency = 434550; #endif +#if HAS_APRS + if (minor < 22) + ao_config.aprs_format = AO_CONFIG_DEFAULT_APRS_FORMAT; +#endif ao_config.minor = AO_CONFIG_MINOR; ao_config_dirty = 1; } @@ -876,6 +880,23 @@ ao_config_aprs_ssid_set(void) ao_config.aprs_ssid = ao_cmd_lex_i; _ao_config_edit_finish(); } + +void +ao_config_aprs_format_set(void) +{ + ao_cmd_decimal(); + if (ao_cmd_status != ao_cmd_success) + return; + _ao_config_edit_start(); + ao_config.aprs_format = ao_cmd_lex_i != 0; + _ao_config_edit_finish(); +} + +void +ao_config_aprs_format_show(void) +{ + printf ("APRS format: %d\n", ao_config.aprs_format); +} #endif /* HAS_APRS */ struct ao_config_var { @@ -969,6 +990,8 @@ __code struct ao_config_var ao_config_vars[] = { #if HAS_APRS { "S <ssid>\0Set APRS SSID (0-15)", ao_config_aprs_ssid_set, ao_config_aprs_ssid_show }, + { "C <0 compressed, 1 uncompressed>\0APRS format", + ao_config_aprs_format_set, ao_config_aprs_format_show }, #endif { "s\0Show", ao_config_show, 0 }, diff --git a/src/kernel/ao_config.h b/src/kernel/ao_config.h index 164584a5..cfe8555c 100644 --- a/src/kernel/ao_config.h +++ b/src/kernel/ao_config.h @@ -57,7 +57,7 @@ #endif #define AO_CONFIG_MAJOR 1 -#define AO_CONFIG_MINOR 21 +#define AO_CONFIG_MINOR 22 #define AO_AES_LEN 16 @@ -115,8 +115,15 @@ struct ao_config { #if HAS_RADIO_FORWARD uint32_t send_frequency; /* minor version 21 */ #endif +#if HAS_APRS + uint8_t aprs_format; /* minor version 22 */ +#endif }; +#define AO_APRS_FORMAT_COMPRESSED 0 +#define AO_APRS_FORMAT_UNCOMPRESSED 1 +#define AO_CONFIG_DEFAULT_APRS_FORMAT AO_APRS_FORMAT_COMPRESSED + #if HAS_RADIO_FORWARD extern __xdata uint32_t ao_send_radio_setting; #endif diff --git a/src/kernel/ao_packet.h b/src/kernel/ao_packet.h index b8426cf9..136609c3 100644 --- a/src/kernel/ao_packet.h +++ b/src/kernel/ao_packet.h @@ -54,7 +54,7 @@ void ao_packet_send(void); uint8_t -ao_packet_recv(void); +ao_packet_recv(uint16_t timeout); void ao_packet_flush(void); diff --git a/src/kernel/ao_product.c b/src/kernel/ao_product.c index b9327bac..c711a4d2 100644 --- a/src/kernel/ao_product.c +++ b/src/kernel/ao_product.c @@ -33,6 +33,10 @@ const char ao_product[] = AO_iProduct_STRING; #define AO_USB_MAX_POWER 100 #endif +#ifndef AO_USB_INTERFACE_CLASS +#define AO_USB_INTERFACE_CLASS 0x02 +#endif + #include "ao_usb.h" /* USB descriptors in one giant block of bytes */ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = @@ -45,7 +49,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = 0x00, /* bDeviceSubClass */ 0x00, /* bDeviceProtocol */ AO_USB_CONTROL_SIZE, /* bMaxPacketSize */ - LE_WORD(0xFFFE), /* idVendor */ + LE_WORD(AO_idVendor_NUMBER), /* idVendor */ LE_WORD(AO_idProduct_NUMBER), /* idProduct */ LE_WORD(0x0100), /* bcdDevice */ 0x01, /* iManufacturer */ @@ -69,7 +73,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] = 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x01, /* bNumEndPoints */ - 0x02, /* bInterfaceClass */ + AO_USB_INTERFACE_CLASS, /* bInterfaceClass */ 0x02, /* bInterfaceSubClass */ 0x01, /* bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */ 0x00, /* iInterface */ diff --git a/src/kernel/ao_pyro.c b/src/kernel/ao_pyro.c index 3044d565..43e73de4 100644 --- a/src/kernel/ao_pyro.c +++ b/src/kernel/ao_pyro.c @@ -375,9 +375,7 @@ ao_pyro(void) ao_sleep(&ao_flight_state); for (;;) { - ao_alarm(AO_MS_TO_TICKS(100)); - ao_sleep(&ao_pyro_wakeup); - ao_clear_alarm(); + ao_sleep_for(&ao_pyro_wakeup, AO_MS_TO_TICKS(100)); if (ao_flight_state >= ao_flight_landed) break; any_waiting = ao_pyro_check(); diff --git a/src/kernel/ao_radio_cmac.c b/src/kernel/ao_radio_cmac.c index bff848f6..b6835346 100644 --- a/src/kernel/ao_radio_cmac.c +++ b/src/kernel/ao_radio_cmac.c @@ -91,7 +91,6 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant return AO_RADIO_CMAC_TIMEOUT; } - ao_radio_cmac_rssi = ao_radio_rssi; if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & AO_RADIO_STATUS_CRC_OK)) return AO_RADIO_CMAC_CRC_ERROR; @@ -114,13 +113,15 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant /* Check the packet signature against the signature provided * over the link */ - + if (memcmp(&cmac_data[len], &cmac_data[len + AO_CMAC_KEY_LEN + 2], AO_CMAC_KEY_LEN) != 0) { return AO_RADIO_CMAC_MAC_ERROR; } + ao_radio_cmac_rssi = ao_radio_rssi; + return AO_RADIO_CMAC_OK; } @@ -161,4 +162,3 @@ ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentr ao_mutex_put(&ao_radio_cmac_mutex); return i; } - diff --git a/src/kernel/ao_serial.h b/src/kernel/ao_serial.h index dbc9f8e4..e21643ac 100644 --- a/src/kernel/ao_serial.h +++ b/src/kernel/ao_serial.h @@ -35,7 +35,7 @@ int _ao_serial0_pollchar(void); uint8_t -_ao_serial0_sleep(void); +_ao_serial0_sleep_for(uint16_t timeout); void ao_serial0_putchar(char c); @@ -58,7 +58,7 @@ int _ao_serial1_pollchar(void); uint8_t -_ao_serial1_sleep(void); +_ao_serial1_sleep_for(uint16_t timeout); void ao_serial1_putchar(char c); @@ -81,7 +81,7 @@ int _ao_serial2_pollchar(void); uint8_t -_ao_serial2_sleep(void); +_ao_serial2_sleep_for(uint16_t timeout); void ao_serial2_putchar(char c); @@ -104,7 +104,7 @@ int _ao_serial3_pollchar(void); uint8_t -_ao_serial3_sleep(void); +_ao_serial3_sleep_for(uint16_t timeout); void ao_serial3_putchar(char c); diff --git a/src/kernel/ao_task.c b/src/kernel/ao_task.c index bafb4943..55e423bb 100644 --- a/src/kernel/ao_task.c +++ b/src/kernel/ao_task.c @@ -450,37 +450,39 @@ ao_wakeup(__xdata void *wchan) __reentrant ao_check_stack(); } -void -ao_alarm(uint16_t delay) +uint8_t +ao_sleep_for(__xdata void *wchan, uint16_t timeout) { + uint8_t ret; + if (timeout) { #if HAS_TASK_QUEUE - uint32_t flags; - /* Make sure we sleep *at least* delay ticks, which means adding - * one to account for the fact that we may be close to the next tick - */ - flags = ao_arch_irqsave(); + uint32_t flags; + /* Make sure we sleep *at least* delay ticks, which means adding + * one to account for the fact that we may be close to the next tick + */ + flags = ao_arch_irqsave(); #endif - if (!(ao_cur_task->alarm = ao_time() + delay + 1)) - ao_cur_task->alarm = 1; + if (!(ao_cur_task->alarm = ao_time() + timeout + 1)) + ao_cur_task->alarm = 1; #if HAS_TASK_QUEUE - ao_task_to_alarm_queue(ao_cur_task); - ao_arch_irqrestore(flags); + ao_task_to_alarm_queue(ao_cur_task); + ao_arch_irqrestore(flags); #endif -} - -void -ao_clear_alarm(void) -{ + } + ret = ao_sleep(wchan); + if (timeout) { #if HAS_TASK_QUEUE - uint32_t flags; + uint32_t flags; - flags = ao_arch_irqsave(); + flags = ao_arch_irqsave(); #endif - ao_cur_task->alarm = 0; + ao_cur_task->alarm = 0; #if HAS_TASK_QUEUE - ao_task_from_alarm_queue(ao_cur_task); - ao_arch_irqrestore(flags); + ao_task_from_alarm_queue(ao_cur_task); + ao_arch_irqrestore(flags); #endif + } + return ret; } static __xdata uint8_t ao_forever; @@ -488,9 +490,7 @@ static __xdata uint8_t ao_forever; void ao_delay(uint16_t ticks) { - ao_alarm(ticks); - ao_sleep(&ao_forever); - ao_clear_alarm(); + ao_sleep_for(&ao_forever, ticks); } void diff --git a/src/kernel/ao_task.h b/src/kernel/ao_task.h index 9c56b480..c6bec0e3 100644 --- a/src/kernel/ao_task.h +++ b/src/kernel/ao_task.h @@ -68,10 +68,19 @@ extern __data uint8_t ao_task_minimize_latency; /* Reduce IRQ latency */ uint8_t ao_sleep(__xdata void *wchan); +/* Suspend the current task until wchan is awoken or the timeout + * expires. returns: + * 0 on normal wake + * 1 on alarm + */ +uint8_t +ao_sleep_for(__xdata void *wchan, uint16_t timeout); + /* Wake all tasks sleeping on wchan */ void ao_wakeup(__xdata void *wchan) __reentrant; +#if 0 /* set an alarm to go off in 'delay' ticks */ void ao_alarm(uint16_t delay); @@ -79,6 +88,7 @@ ao_alarm(uint16_t delay); /* Clear any pending alarm */ void ao_clear_alarm(void); +#endif /* Yield the processor to another task */ void diff --git a/src/kernel/ao_telemetry.c b/src/kernel/ao_telemetry.c index e2197f7a..854ac898 100644 --- a/src/kernel/ao_telemetry.c +++ b/src/kernel/ao_telemetry.c @@ -486,9 +486,7 @@ ao_telemetry(void) #endif /* HAS_APRS */ delay = time - ao_time(); if (delay > 0) { - ao_alarm(delay); - ao_sleep(&telemetry); - ao_clear_alarm(); + ao_sleep_for(&telemetry, delay); } } } diff --git a/src/kernel/ao_telemetry.h b/src/kernel/ao_telemetry.h index 711e0d36..672d2317 100644 --- a/src/kernel/ao_telemetry.h +++ b/src/kernel/ao_telemetry.h @@ -258,13 +258,14 @@ struct ao_telemetry_metrum_data { uint16_t serial; /* 0 */ uint16_t tick; /* 2 */ uint8_t type; /* 4 */ + uint8_t pad5[3]; /* 5 */ - int32_t ground_pres; /* 8 average pres on pad */ + int32_t ground_pres; /* 8 average pres on pad */ int16_t ground_accel; /* 12 average accel on pad */ int16_t accel_plus_g; /* 14 accel calibration at +1g */ int16_t accel_minus_g; /* 16 accel calibration at -1g */ - uint8_t pad[14]; /* 18 */ + uint8_t pad18[14]; /* 18 */ /* 32 */ }; @@ -332,6 +333,8 @@ union ao_telemetry_all { struct ao_telemetry_baro baro; }; +typedef char ao_check_telemetry_size[sizeof(union ao_telemetry_all) == 32 ? 1 : -1]; + struct ao_telemetry_all_recv { union ao_telemetry_all telemetry; int8_t rssi; diff --git a/src/micropeak/micropeak-load.tmpl b/src/micropeak/micropeak-load.tmpl index 08236a15..c061559d 100644 --- a/src/micropeak/micropeak-load.tmpl +++ b/src/micropeak/micropeak-load.tmpl @@ -8,13 +8,14 @@ LOADSLOW="%LOADSLOW%" LOADFAST="" case "$1" in -fast) - LOADSPEED="$LOADFAST" +slow) + LOADSPEED="$LOADSLOW" ;; *) - LOADSPEED="$LOADSLOW" + LOADSPEED="$LOADFAST" ;; esac echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX} ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX} +/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished" diff --git a/src/microsplash/.gitignore b/src/microsplash/.gitignore index 5f6fe3b2..c2062c34 100644 --- a/src/microsplash/.gitignore +++ b/src/microsplash/.gitignore @@ -1,2 +1,3 @@ ao_product.h -microsplash-* +microsplash-v* +microsplash-load diff --git a/src/microsplash/Makefile b/src/microsplash/Makefile index 10cb825b..9bb636f1 100644 --- a/src/microsplash/Makefile +++ b/src/microsplash/Makefile @@ -8,8 +8,15 @@ vpath make-altitude-pa ../util include ../avr/Makefile.defs +PROGNAME=microsplash-v1.0 +PROG=$(PROGNAME)-$(VERSION).elf +HEX=$(PROGNAME)-$(VERSION).ihx + +SCRIPT=microsplash-load + PUBLISH_DIR=$(HOME)/altusmetrumllc/Binaries -PUBLISH_FILE=$(PUBLISH_DIR)/$(PROG)-$(VERSION).hex +PUBLISH_HEX=$(PUBLISH_DIR)/$(HEX) +PUBLISH_SCRIPT=$(PUBLISH_DIR)/$(SCRIPT) MCU=attiny85 DUDECPUTYPE=t85 @@ -48,15 +55,13 @@ INC=\ altitude-pa.h IDPRODUCT=0 -PRODUCT=MicroSplash-v0.1 +PRODUCT=MicroSplash-v1.0 PRODUCT_DEF=-DMICROPEAK CFLAGS = $(PRODUCT_DEF) -I. -I../attiny -I../kernel -I.. -I../drivers -I../product CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O2 -mcall-prologues -DATTINY NICKLE=nickle -PROG=microsplash-v1.0 - SRC=$(ALTOS_SRC) OBJ=$(SRC:.c=.o) @@ -68,7 +73,7 @@ endif # Otherwise, print the full command line. quiet ?= $($1) -all: $(PROG) $(PROG).hex +all: $(PROG) $(HEX) $(SCRIPT) CHECK=sh ../util/check-avr-mem @@ -76,16 +81,16 @@ $(PROG): Makefile $(OBJ) $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1) -$(PROG).hex: $(PROG) +$(HEX): $(PROG) avr-size $(PROG) $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@ -load: $(PROG).hex - $(LOADCMD) $(LOADARG)$(PROG).hex +load: $(HEX) + $(LOADCMD) $(LOADARG)$(HEX) -load-slow: $(PROG).hex - $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PROG).hex +load-slow: $(HEX) + $(LOADCMD) $(LOADSLOW) $(LOADARG)$(HEX) ao_product.h: ao-make-product.5c ../Version $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ @@ -98,22 +103,30 @@ ao_product.o: ao_product.c ao_product.h distclean: clean clean: - rm -f *.o $(PROG) $(PROG).hex + rm -f *.o $(PROG) $(HEX) $(SCRIPT) rm -f ao_product.h +publish: $(PUBLISH_HEX) $(PUBLISH_SCRIPT) -publish: $(PROG).hex - cp -a $(PROG).hex $(PUBLISH_FILE) +$(PUBLISH_HEX): $(HEX) + cp -a $(HEX) $@ + +$(PUBLISH_SCRIPT): $(SCRIPT) + cp -a $(SCRIPT) $@ load-product: - $(LOADCMD) $(LOADARG)$(PUBLISH_FILE) + ./$(SCRIPT) fast load-product-slow: - $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PUBLISH_FILE) + ./$(SCRIPT) slow ../altitude-pa.h: make-altitude-pa nickle $< > $@ +$(SCRIPT): $(SCRIPT).tmpl Makefile ../Version + sed -e 's/%HEX%/$(HEX)/' -e 's/%LOADCMD%/$(LOADCMD)/' -e 's/%LOADARG%/$(LOADARG)/' -e 's/%LOADSLOW%/$(LOADSLOW)/' $(SCRIPT).tmpl > $@ || (rm $@ && exit 1) + chmod +x $@ + install: uninstall: diff --git a/src/microsplash/microsplash-load.tmpl b/src/microsplash/microsplash-load.tmpl new file mode 100644 index 00000000..c061559d --- /dev/null +++ b/src/microsplash/microsplash-load.tmpl @@ -0,0 +1,21 @@ +#!/bin/sh +dir=`dirname $0` + +HEX="$dir"/"%HEX%" +LOADCMD="%LOADCMD%" +LOADARG="%LOADARG%" +LOADSLOW="%LOADSLOW%" +LOADFAST="" + +case "$1" in +slow) + LOADSPEED="$LOADSLOW" + ;; +*) + LOADSPEED="$LOADFAST" + ;; +esac + +echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX} +${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX} +/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished" diff --git a/src/product/ao_terraui.c b/src/product/ao_terraui.c index 8fd97033..1e7b5dcd 100644 --- a/src/product/ao_terraui.c +++ b/src/product/ao_terraui.c @@ -539,9 +539,7 @@ ao_terraui(void) else ao_terraui_page[ao_current_page](); - ao_alarm(AO_SEC_TO_TICKS(1)); - b = ao_button_get(); - ao_clear_alarm(); + b = ao_button_get(AO_SEC_TO_TICKS(1)); if (b > 0) { ao_beep_for(AO_BEEP_HIGH, AO_MS_TO_TICKS(10)); diff --git a/src/stm/ao_i2c_stm.c b/src/stm/ao_i2c_stm.c index 1c90cdb8..158f5b21 100644 --- a/src/stm/ao_i2c_stm.c +++ b/src/stm/ao_i2c_stm.c @@ -195,15 +195,13 @@ ao_i2c_start(uint8_t index, uint16_t addr) if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_START))) break; } - ao_alarm(AO_MS_TO_TICKS(250)); ao_arch_block_interrupts(); stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN); ao_i2c_ev_isr(index); while (ao_i2c_state[index] == I2C_IDLE) - if (ao_sleep(&ao_i2c_state[index])) + if (ao_sleep_for(&ao_i2c_state[index], AO_MS_TO_TICKS(250))) break; ao_arch_release_interrupts(); - ao_clear_alarm(); return ao_i2c_state[index] == I2C_RUNNING; } @@ -258,16 +256,14 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop) (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR)); ao_dma_start(tx_dma_index); - ao_alarm(1 + len); ao_arch_block_interrupts(); while (!ao_dma_done[tx_dma_index]) - if (ao_sleep(&ao_dma_done[tx_dma_index])) + if (ao_sleep_for(&ao_dma_done[tx_dma_index], 1 + len)) break; - ao_clear_alarm(); ao_dma_done_transfer(tx_dma_index); stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN); while ((stm_i2c->sr1 & (1 << STM_I2C_SR1_BTF)) == 0) - if (ao_sleep(&ao_i2c_state[index])) + if (ao_sleep_for(&ao_i2c_state[index], 1 + len)) break; stm_i2c->cr2 = AO_STM_I2C_CR2; ao_arch_release_interrupts(); @@ -321,14 +317,12 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop) if (stop) stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP); - ao_alarm(1); ao_arch_block_interrupts(); while (ao_i2c_recv_len[index]) - if (ao_sleep(&ao_i2c_recv_len[index])) + if (ao_sleep_for(&ao_i2c_recv_len[index], 1)) break; ao_arch_release_interrupts(); ret = ao_i2c_recv_len[index] == 0; - ao_clear_alarm(); } else { uint8_t rx_dma_index = ao_i2c_stm_info[index].rx_dma_index; ao_dma_set_transfer(rx_dma_index, @@ -351,13 +345,11 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop) ao_i2c_wait_addr(index); ao_dma_start(rx_dma_index); - ao_alarm(len); ao_arch_block_interrupts(); while (!ao_dma_done[rx_dma_index]) - if (ao_sleep(&ao_dma_done[rx_dma_index])) + if (ao_sleep_for(&ao_dma_done[rx_dma_index], len)) break; ao_arch_release_interrupts(); - ao_clear_alarm(); ret = ao_dma_done[rx_dma_index]; ao_dma_done_transfer(rx_dma_index); stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP); diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c index 2568cf43..88f2d029 100644 --- a/src/stm/ao_serial_stm.c +++ b/src/stm/ao_serial_stm.c @@ -86,9 +86,9 @@ ao_usart_getchar(struct ao_stm_usart *usart) } static inline uint8_t -_ao_usart_sleep(struct ao_stm_usart *usart) +_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout) { - return ao_sleep(&usart->rx_fifo); + return ao_sleep_for(&usart->rx_fifo, timeout); } void @@ -217,9 +217,9 @@ _ao_serial1_pollchar(void) } uint8_t -_ao_serial1_sleep(void) +_ao_serial1_sleep_for(uint16_t timeout) { - return _ao_usart_sleep(&ao_stm_usart1); + return _ao_usart_sleep_for(&ao_stm_usart1, timeout); } void @@ -260,9 +260,9 @@ _ao_serial2_pollchar(void) } uint8_t -_ao_serial2_sleep(void) +_ao_serial2_sleep_for(uint16_t timeout) { - return _ao_usart_sleep(&ao_stm_usart2); + return _ao_usart_sleep_for(&ao_stm_usart2, timeout); } void @@ -303,9 +303,9 @@ _ao_serial3_pollchar(void) } uint8_t -_ao_serial3_sleep(void) +_ao_serial3_sleep_for(uint16_t timeout) { - return _ao_usart_sleep(&ao_stm_usart3); + return _ao_usart_sleep_for(&ao_stm_usart3, timeout); } void diff --git a/src/stmf0/ao_adc_fast.c b/src/stmf0/ao_adc_fast.c index be9b5986..26e6691c 100644 --- a/src/stmf0/ao_adc_fast.c +++ b/src/stmf0/ao_adc_fast.c @@ -18,43 +18,53 @@ #include <ao.h> #include <ao_adc_fast.h> -uint16_t ao_adc_ring[AO_ADC_RING_SIZE]; +uint16_t ao_adc_ring[AO_ADC_RING_SIZE] __attribute__((aligned(4))); -uint16_t ao_adc_ring_head, ao_adc_ring_tail; -uint8_t ao_adc_running; +/* Maximum number of samples fetched per _ao_adc_start call */ +#define AO_ADC_RING_CHUNK (AO_ADC_RING_SIZE >> 1) + +uint16_t ao_adc_ring_head, ao_adc_ring_remain; +uint16_t ao_adc_running; /* * Callback from DMA ISR * - * Mark time in ring, shut down DMA engine + * Wakeup any waiting processes, mark the DMA as done, start the ADC + * if there's still lots of space in the ring */ static void ao_adc_dma_done(int index) { (void) index; - ao_adc_ring_head += AO_ADC_RING_CHUNK; + ao_adc_ring_head += ao_adc_running; + ao_adc_ring_remain += ao_adc_running; if (ao_adc_ring_head == AO_ADC_RING_SIZE) ao_adc_ring_head = 0; ao_adc_running = 0; ao_wakeup(&ao_adc_ring_head); ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1)); + _ao_adc_start(); } void _ao_adc_start(void) { uint16_t *buf; + uint16_t count; if (ao_adc_running) return; - if (_ao_adc_space() < AO_ADC_RING_CHUNK) + count = _ao_adc_space(); + if (count == 0) return; - ao_adc_running = 1; + if (count > AO_ADC_RING_CHUNK) + count = AO_ADC_RING_CHUNK; + ao_adc_running = count; buf = ao_adc_ring + ao_adc_ring_head; stm_adc.isr = 0; ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), &stm_adc.dr, buf, - AO_ADC_RING_CHUNK, + count, (0 << STM_DMA_CCR_MEM2MEM) | (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) | (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) | @@ -140,7 +150,6 @@ ao_adc_init(void) #if AO_NUM_ADC > 8 #error Need more ADC defines #endif - stm_adc.chselr = chselr; /* Set the clock */ stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE; @@ -160,14 +169,16 @@ ao_adc_init(void) while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0) ; + stm_adc.chselr = chselr; + stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) | (0 << STM_ADC_CFGR1_AWDEN) | (0 << STM_ADC_CFGR1_AWDSGL) | (0 << STM_ADC_CFGR1_DISCEN) | (0 << STM_ADC_CFGR1_AUTOOFF) | - (1 << STM_ADC_CFGR1_WAIT) | + (0 << STM_ADC_CFGR1_WAIT) | (1 << STM_ADC_CFGR1_CONT) | - (0 << STM_ADC_CFGR1_OVRMOD) | + (1 << STM_ADC_CFGR1_OVRMOD) | (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) | (0 << STM_ADC_CFGR1_ALIGN) | (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) | @@ -186,5 +197,4 @@ ao_adc_init(void) stm_syscfg.cfgr1 &= ~(1 << STM_SYSCFG_CFGR1_ADC_DMA_RMP); ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1)); - ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_dma_done); } diff --git a/src/stmf0/ao_adc_fast.h b/src/stmf0/ao_adc_fast.h index eec45505..c6903e9f 100644 --- a/src/stmf0/ao_adc_fast.h +++ b/src/stmf0/ao_adc_fast.h @@ -26,62 +26,59 @@ ao_adc_init(void); /* Total ring size in samples */ #define AO_ADC_RING_SIZE 256 -/* Number of samples fetched per ao_adc_start call */ -#define AO_ADC_RING_CHUNK (AO_ADC_RING_SIZE >> 1) extern uint16_t ao_adc_ring[AO_ADC_RING_SIZE]; #define ao_adc_ring_step(pos,inc) (((pos) + (inc)) & (AO_ADC_RING_SIZE - 1)) -extern uint16_t ao_adc_ring_head, ao_adc_ring_tail; -extern uint8_t ao_adc_running; - -void -_ao_adc_start(void); +extern uint16_t ao_adc_ring_head, ao_adc_ring_remain; +extern uint16_t ao_adc_running; +/* + * Place to start fetching values from + */ static inline uint16_t -_ao_adc_remain(void) +ao_adc_ring_tail(void) { - if (ao_adc_ring_tail > ao_adc_ring_head) - return AO_ADC_RING_SIZE - ao_adc_ring_tail; - return ao_adc_ring_head - ao_adc_ring_tail; + return (ao_adc_ring_head - ao_adc_ring_remain) & (AO_ADC_RING_SIZE - 1); } +void +_ao_adc_start(void); + +/* + * Space available to write ADC values into + */ static inline uint16_t _ao_adc_space(void) { - if (ao_adc_ring_head == ao_adc_ring_tail) - return AO_ADC_RING_SIZE; - if (ao_adc_ring_head > ao_adc_ring_tail) + /* Free to end of buffer? */ + if (ao_adc_ring_remain <= ao_adc_ring_head) return AO_ADC_RING_SIZE - ao_adc_ring_head; - return ao_adc_ring_tail - ao_adc_ring_head; + + /* no, return just the unused entries beyond head */ + return AO_ADC_RING_SIZE - ao_adc_ring_remain; } -static inline uint16_t * +static inline uint16_t ao_adc_get(uint16_t n) { - if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE) - ao_panic(AO_PANIC_ADC); ao_arch_block_interrupts(); - while (_ao_adc_remain() < n) { + while (ao_adc_ring_remain < n) { if (!ao_adc_running) _ao_adc_start(); ao_sleep(&ao_adc_ring_head); } ao_arch_release_interrupts(); - return &ao_adc_ring[ao_adc_ring_tail]; + return ao_adc_ring_tail(); } static inline void ao_adc_ack(uint16_t n) { - if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE) - ao_panic(AO_PANIC_ADC); ao_arch_block_interrupts(); - ao_adc_ring_tail += n; - if (ao_adc_ring_tail == AO_ADC_RING_SIZE) - ao_adc_ring_tail = 0; - if (!ao_adc_running && _ao_adc_space() >= AO_ADC_RING_CHUNK) + ao_adc_ring_remain -= n; + if (!ao_adc_running) _ao_adc_start(); ao_arch_release_interrupts(); } diff --git a/src/stmf0/ao_crc_stm.c b/src/stmf0/ao_crc_stm.c new file mode 100644 index 00000000..863f5ef5 --- /dev/null +++ b/src/stmf0/ao_crc_stm.c @@ -0,0 +1,90 @@ +/* + * Copyright © 2015 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> +#include <ao_crc.h> + +#ifndef AO_CRC_WIDTH +#error "Must define AO_CRC_WIDTH" +#endif + +/* Only the STM32F07x and ST32F09x series have + * programmable CRC units. Others can only do the ANSI CRC-32 computation + */ + +#if !AO_HAVE_PROGRAMMABLE_CRC_UNIT && AO_CRC_WIDTH != 32 +#error "Target hardware does not have programmable CRC unit" +#endif + +#ifndef AO_CRC_POLY +#if AO_CRC_WIDTH == 16 +#define AO_CRC_POLY AO_CRC_16_DEFAULT +#endif +#if AO_CRC_WIDTH == 32 +#define AO_CRC_POLY AO_CRC_32_DEFAULT +#endif +#endif + +#if !AO_HAVE_PROGRAMMABLE_CRC_UNIT && (AO_CRC_WIDTH != 32 || AO_CRC_POLY != AO_CRC_32_ANSI) +#error "Target hardware does not have programmable CRC unit" +#endif + +#if AO_CRC_WIDTH == 32 +#define AO_CRC_CR_POLYSIZE STM_CRC_CR_POLYSIZE_32 +#endif + +#if AO_CRC_WIDTH == 16 +#define AO_CRC_CR_POLYSIZE STM_CRC_CR_POLYSIZE_16 +#endif + +#if AO_CRC_WIDTH == 8 +#define AO_CRC_CR_POLYSIZE STM_CRC_CR_POLYSIZE_8 +#endif + +#if AO_CRC_WIDTH == 7 +#define AO_CRC_CR_POLYSIZE STM_CRC_CR_POLYSIZE_7 +#endif + +#ifndef AO_CRC_INIT +#define AO_CRC_INIT 0xffffffff +#endif + +void +ao_crc_reset(void) +{ + stm_crc.cr |= (1 << STM_CRC_CR_RESET); + while ((stm_crc.cr & (1 << STM_CRC_CR_RESET)) != 0) + ; +} + +void +ao_crc_init(void) +{ + /* Turn on the CRC clock */ + stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_CRCEN); + + /* Need to initialize CR even on non-programmable hardware, + * the write to the POLYSIZE bits will be ignored in that + * case + */ + stm_crc.cr = (AO_CRC_CR_POLYSIZE << STM_CRC_CR_POLYSIZE); + stm_crc.init = AO_CRC_INIT; +#if AO_HAVE_PROGRAMMABLE_CRC_UNIT + stm_crc.pol = AO_CRC_POLY; +#endif + ao_crc_reset(); +} diff --git a/src/stmf0/ao_exti.h b/src/stmf0/ao_exti.h new file mode 100644 index 00000000..ebea224d --- /dev/null +++ b/src/stmf0/ao_exti.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2012 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_EXTI_H_ +#define _AO_EXTI_H_ + +#define AO_EXTI_MODE_RISING 1 +#define AO_EXTI_MODE_FALLING 2 +#define AO_EXTI_MODE_PULL_UP 4 +#define AO_EXTI_MODE_PULL_DOWN 8 +#define AO_EXTI_PRIORITY_LOW 16 +#define AO_EXTI_PRIORITY_MED 0 +#define AO_EXTI_PRIORITY_HIGH 32 +#define AO_EXTI_PIN_NOCONFIGURE 64 + +void +ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)()); + +void +ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode); + +void +ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)()); + +void +ao_exti_enable(struct stm_gpio *gpio, uint8_t pin); + +void +ao_exti_disable(struct stm_gpio *gpio, uint8_t pin); + +void +ao_exti_init(void); + +#endif /* _AO_EXTI_H_ */ diff --git a/src/stmf0/ao_usb_stm.c b/src/stmf0/ao_usb_stm.c index 3ea7da5e..b8146c21 100644 --- a/src/stmf0/ao_usb_stm.c +++ b/src/stmf0/ao_usb_stm.c @@ -83,9 +83,13 @@ static uint16_t ao_usb_sram_addr; static uint16_t *ao_usb_ep0_tx_buffer; static uint16_t *ao_usb_ep0_rx_buffer; +/* Pointer to interrupt buffer in USB memory */ +static uint16_t ao_usb_int_tx_offset; + /* Pointer to bulk data tx/rx buffers in USB memory */ static uint16_t ao_usb_in_tx_offset; static uint16_t *ao_usb_in_tx_buffer; +static uint16_t ao_usb_out_rx_offset; static uint16_t *ao_usb_out_rx_buffer; /* System ram shadow of USB buffer; writing individual bytes is @@ -146,12 +150,10 @@ static inline uint16_t *ao_usb_packet_buffer_addr(uint16_t sram_addr) return (uint16_t *) (stm_usb_sram + sram_addr); } -#if AO_USB_DIRECTIO static inline uint16_t ao_usb_packet_buffer_offset(uint16_t *addr) { return (uint16_t) ((uint8_t *) addr - stm_usb_sram); } -#endif static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) { return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK; @@ -323,27 +325,42 @@ ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint3 } static void -ao_usb_init_btable(void) +ao_usb_alloc_buffers(void) { ao_usb_sram_addr = 0; ao_usb_bdt = (void *) stm_usb_sram; - ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE; - /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */ - - ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr; - ao_usb_bdt[0].single.count_tx = 0; ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_sram_addr += AO_USB_CONTROL_SIZE; - ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr; - ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) | - (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK)); ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_sram_addr += AO_USB_CONTROL_SIZE; + ao_usb_int_tx_offset = ao_usb_sram_addr; + ao_usb_sram_addr += AO_USB_INT_SIZE; + + ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_out_rx_offset = ao_usb_sram_addr; + ao_usb_sram_addr += AO_USB_OUT_SIZE; + + ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_in_tx_offset = ao_usb_sram_addr; + ao_usb_sram_addr += AO_USB_IN_SIZE; +} + +static void +ao_usb_init_btable(void) +{ + /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */ + + ao_usb_bdt[0].single.addr_tx = ao_usb_packet_buffer_offset(ao_usb_ep0_tx_buffer); + ao_usb_bdt[0].single.count_tx = 0; + + ao_usb_bdt[0].single.addr_rx = ao_usb_packet_buffer_offset(ao_usb_ep0_rx_buffer); + ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) | + (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK)); } static void @@ -370,6 +387,8 @@ ao_usb_set_ep0(void) } ao_usb_set_address(0); + + ao_usb_running = 0; } static void @@ -378,9 +397,8 @@ ao_usb_set_configuration(void) debug ("ao_usb_set_configuration\n"); /* Set up the INT end point */ - ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_int_tx_offset; ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0; - ao_usb_sram_addr += AO_USB_INT_SIZE; ao_usb_init_ep(AO_USB_INT_EPR, AO_USB_INT_EP, @@ -389,11 +407,9 @@ ao_usb_set_configuration(void) STM_USB_EPR_STAT_TX_NAK); /* Set up the OUT end point */ - ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_out_rx_offset; ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) | (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK)); - ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); - ao_usb_sram_addr += AO_USB_OUT_SIZE; ao_usb_init_ep(AO_USB_OUT_EPR, AO_USB_OUT_EP, @@ -402,11 +418,8 @@ ao_usb_set_configuration(void) STM_USB_EPR_STAT_TX_DISABLED); /* Set up the IN end point */ - ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_in_tx_offset; ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0; - ao_usb_in_tx_offset = ao_usb_sram_addr; - ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_in_tx_offset); - ao_usb_sram_addr += AO_USB_IN_SIZE; ao_usb_init_ep(AO_USB_IN_EPR, AO_USB_IN_EP, @@ -415,6 +428,9 @@ ao_usb_set_configuration(void) STM_USB_EPR_STAT_TX_NAK); ao_usb_running = 1; +#if AO_USB_DIRECTIO + ao_wakeup(&ao_usb_running); +#endif } static uint16_t control_count; @@ -916,8 +932,6 @@ ao_usb_alloc(void) { uint16_t *buffer; - if (!ao_usb_running) - return NULL; buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); ao_usb_sram_addr += AO_USB_IN_SIZE; return buffer; @@ -936,12 +950,28 @@ ao_usb_write(uint16_t *buffer, uint16_t len) { ao_arch_block_interrupts(); - /* Flush any pending regular */ - if (ao_usb_tx_count) - _ao_usb_in_send(); + /* Wait for everything to be ready at the same time */ + for (;;) { + /* Make sure USB is connected */ + if (!ao_usb_running) { + ao_sleep(&ao_usb_running); + continue; + } + + /* Flush any pending regular I/O */ + if (ao_usb_tx_count) { + _ao_usb_in_send(); + continue; + } + + /* Wait for an idle IN buffer */ + if (ao_usb_in_pending) { + ao_sleep(&ao_usb_in_pending); + continue; + } + break; + } - while (ao_usb_in_pending) - ao_sleep(&ao_usb_in_pending); ao_usb_in_pending = 1; ao_usb_in_flushed = (len != AO_USB_IN_SIZE); ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer); @@ -1083,6 +1113,9 @@ ao_usb_init(void) debug ("ao_usb_init\n"); ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_alloc_buffers(); + #if USB_ECHO ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo"); #endif diff --git a/src/telebt-v3.0/ao_pins.h b/src/telebt-v3.0/ao_pins.h index 838f0dfc..a6a01662 100644 --- a/src/telebt-v3.0/ao_pins.h +++ b/src/telebt-v3.0/ao_pins.h @@ -130,7 +130,7 @@ struct ao_adc { }; #define AO_ADC_DUMP(p) \ - printf("tick: %5u %5d batt: %5d\n", \ + printf("tick: %5u batt %5d\n", \ (p)->tick, \ (p)->adc.v_batt); @@ -168,7 +168,7 @@ struct ao_adc { #define ao_serial_btm_getchar ao_serial2_getchar #define ao_serial_btm_putchar ao_serial2_putchar #define _ao_serial_btm_pollchar _ao_serial2_pollchar -#define _ao_serial_btm_sleep _ao_serial2_sleep +#define _ao_serial_btm_sleep_for _ao_serial2_sleep_for #define ao_serial_btm_set_speed ao_serial2_set_speed #define ao_serial_btm_drain ao_serial2_drain #define ao_serial_btm_rx_fifo (ao_stm_usart2.rx_fifo) diff --git a/src/telelco-v0.1/ao_lco.c b/src/telelco-v0.1/ao_lco.c index 79f3896b..cb2195ef 100644 --- a/src/telelco-v0.1/ao_lco.c +++ b/src/telelco-v0.1/ao_lco.c @@ -280,9 +280,7 @@ ao_lco_igniter_status(void) uint16_t delay; for (;;) { -// ao_alarm(delay); ao_sleep(&ao_pad_query); -// ao_clear_alarm(); if (!ao_lco_valid) { ao_led_on(AO_LED_RED); ao_led_off(AO_LED_GREEN); @@ -364,9 +362,7 @@ ao_lco_monitor(void) delay = AO_MS_TO_TICKS(100); else delay = AO_SEC_TO_TICKS(1); - ao_alarm(delay); - ao_sleep(&ao_lco_armed); - ao_clear_alarm(); + ao_sleep_for(&ao_lco_armed, delay); } } diff --git a/src/telelco-v0.3/.gitignore b/src/telelco-v0.3/.gitignore new file mode 100644 index 00000000..a32ec26e --- /dev/null +++ b/src/telelco-v0.3/.gitignore @@ -0,0 +1,2 @@ +ao_product.h +telelco*.elf diff --git a/src/telelco-v0.3/Makefile b/src/telelco-v0.3/Makefile new file mode 100644 index 00000000..83d3fc43 --- /dev/null +++ b/src/telelco-v0.3/Makefile @@ -0,0 +1,108 @@ +# +# AltOS build for TeleLCO +# +# + +include ../stm/Makefile.defs + +INC = \ + ao.h \ + ao_arch.h \ + ao_arch_funcs.h \ + ao_boot.h \ + ao_companion.h \ + ao_data.h \ + ao_sample.h \ + ao_pins.h \ + ao_product.h \ + ao_seven_segment.h \ + ao_lco.h \ + ao_lco_cmd.h \ + ao_lco_func.h \ + ao_radio_spi.h \ + ao_radio_cmac.h \ + ao_cc1200_CC1200.h \ + ao_cc1200.h \ + ao_debounce.h \ + stm32l.h + +# +# Common AltOS sources +# + +#PROFILE=ao_profile.c +#PROFILE_DEF=-DAO_PROFILE=1 + +ALTOS_SRC = \ + ao_boot_chain.c \ + ao_interrupt.c \ + ao_product.c \ + ao_romconfig.c \ + ao_cmd.c \ + ao_config.c \ + ao_task.c \ + ao_led.c \ + ao_stdio.c \ + ao_panic.c \ + ao_timer.c \ + ao_mutex.c \ + ao_freq.c \ + ao_dma_stm.c \ + ao_spi_stm.c \ + ao_beep_stm.c \ + ao_eeprom_stm.c \ + ao_fast_timer.c \ + ao_lcd_stm.c \ + ao_usb_stm.c \ + ao_exti_stm.c \ + ao_cc1200.c \ + ao_radio_cmac.c \ + ao_aes.c \ + ao_aes_tables.c \ + ao_fec_tx.c \ + ao_fec_rx.c \ + ao_seven_segment.c \ + ao_debounce.c \ + ao_quadrature.c \ + ao_button.c \ + ao_event.c \ + ao_lco.c \ + ao_lco_cmd.c \ + ao_lco_func.c \ + ao_radio_cmac_cmd.c + +PRODUCT=TeleLCO-v0.3 +PRODUCT_DEF=-DTELELCO +IDPRODUCT=0x0023 + +CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g + +PROGNAME=telelco-v0.3 +PROG=$(PROGNAME)-$(VERSION).elf +HEX=$(PROGNAME)-$(VERSION).ihx + +SRC=$(ALTOS_SRC) ao_telelco.c +OBJ=$(SRC:.c=.o) + +all: $(PROG) $(HEX) + +$(PROG): Makefile $(OBJ) altos.ld + $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS) + +../altitude.h: make-altitude + nickle $< > $@ + +$(OBJ): $(INC) + +ao_product.h: ao-make-product.5c ../Version + $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@ + +distclean: clean + +clean: + rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx + rm -f ao_product.h + +install: + +uninstall: diff --git a/src/telelco-v0.3/ao_pins.h b/src/telelco-v0.3/ao_pins.h new file mode 100644 index 00000000..92095a7c --- /dev/null +++ b/src/telelco-v0.3/ao_pins.h @@ -0,0 +1,269 @@ +/* + * Copyright © 2012 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +/* 8MHz High speed external crystal */ +#define AO_HSE 8000000 + +/* PLLVCO = 96MHz (so that USB will work) */ +#define AO_PLLMUL 12 +#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12) + +#define AO_CC1200_FOSC 40000000 + +/* SYSCLK = 32MHz (no need to go faster than CPU) */ +#define AO_PLLDIV 3 +#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_3) + +/* HCLK = 32MHz (CPU clock) */ +#define AO_AHB_PRESCALER 1 +#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1 + +/* Run APB1 at 16MHz (HCLK/2) */ +#define AO_APB1_PRESCALER 2 +#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_2 + +/* Run APB2 at 16MHz (HCLK/2) */ +#define AO_APB2_PRESCALER 2 +#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_2 + +#define HAS_EEPROM 1 +#define USE_INTERNAL_FLASH 1 +#define USE_EEPROM_CONFIG 1 +#define USE_STORAGE_CONFIG 0 +#define HAS_USB 1 +#define HAS_BEEP 1 +#define HAS_RADIO 1 +#define HAS_RADIO_RATE 1 +#define HAS_TELEMETRY 0 +#define HAS_AES 1 + +#define HAS_SPI_1 0 +#define SPI_1_PA5_PA6_PA7 0 +#define SPI_1_PB3_PB4_PB5 0 +#define SPI_1_PE13_PE14_PE15 0 + +#define HAS_SPI_2 1 /* CC1120 */ +#define SPI_2_PB13_PB14_PB15 0 +#define SPI_2_PD1_PD3_PD4 1 +#define SPI_2_GPIO (&stm_gpiod) +#define SPI_2_SCK 1 +#define SPI_2_MISO 3 +#define SPI_2_MOSI 4 +#define SPI_2_OSPEEDR STM_OSPEEDR_10MHz + +#define HAS_I2C_1 0 + +#define HAS_I2C_2 0 + +#define PACKET_HAS_SLAVE 0 +#define PACKET_HAS_MASTER 0 + +#define FAST_TIMER_FREQ 10000 /* .1ms for debouncing */ + +/* + * Radio is a cc1200 connected via SPI + */ + +#define AO_RADIO_CAL_DEFAULT 5695733 + +#define AO_FEC_DEBUG 0 +#define AO_CC1200_SPI_CS_PORT (&stm_gpiod) +#define AO_CC1200_SPI_CS_PIN 0 +#define AO_CC1200_SPI_BUS AO_SPI_2_PD1_PD3_PD4 +#define AO_CC1200_SPI stm_spi2 + +#define AO_CC1200_INT_PORT (&stm_gpioc) +#define AO_CC1200_INT_PIN (15) + +#define AO_CC1200_INT_GPIO 2 +#define AO_CC1200_INT_GPIO_IOCFG CC1200_IOCFG2 + +#define LOW_LEVEL_DEBUG 0 + +#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOCEN +#define LED_PORT (&stm_gpioc) +#define LED_PIN_RED 7 +#define LED_PIN_AMBER 8 +#define LED_PIN_GREEN 9 +#define LED_PIN_CONTINUITY_3 10 +#define LED_PIN_CONTINUITY_2 11 +#define LED_PIN_CONTINUITY_1 12 +#define LED_PIN_CONTINUITY_0 13 +#define LED_PIN_REMOTE_ARM 14 +#define AO_LED_RED (1 << LED_PIN_RED) +#define AO_LED_AMBER (1 << LED_PIN_AMBER) +#define AO_LED_GREEN (1 << LED_PIN_GREEN) +#define AO_LED_CONTINUITY_3 (1 << LED_PIN_CONTINUITY_3) +#define AO_LED_CONTINUITY_2 (1 << LED_PIN_CONTINUITY_2) +#define AO_LED_CONTINUITY_1 (1 << LED_PIN_CONTINUITY_1) +#define AO_LED_CONTINUITY_0 (1 << LED_PIN_CONTINUITY_0) + +#define AO_LED_CONTINUITY_NUM 4 + +#define AO_LED_REMOTE_ARM (1 << LED_PIN_REMOTE_ARM) + +#define LEDS_AVAILABLE (AO_LED_RED | \ + AO_LED_AMBER | \ + AO_LED_GREEN | \ + AO_LED_CONTINUITY_3 | \ + AO_LED_CONTINUITY_2 | \ + AO_LED_CONTINUITY_1 | \ + AO_LED_CONTINUITY_0 | \ + AO_LED_REMOTE_ARM) + +/* LCD displays */ + +#define LCD_DEBUG 0 +#define SEVEN_SEGMENT_DEBUG 0 + +#define AO_LCD_STM_SEG_ENABLED_0 ( \ + (1 << 0) | /* PA1 */ \ + (1 << 1) | /* PA2 */ \ + (1 << 2) | /* PA3 */ \ + (1 << 3) | /* PA6 */ \ + (1 << 4) | /* PA7 */ \ + (1 << 5) | /* PB0 */ \ + (1 << 6) | /* PB1 */ \ + (1 << 7) | /* PB3 */ \ + (1 << 8) | /* PB4 */ \ + (1 << 9) | /* PB5 */ \ + (1 << 10) | /* PB10 */ \ + (1 << 11) | /* PB11 */ \ + (1 << 12) | /* PB12 */ \ + (1 << 13) | /* PB13 */ \ + (1 << 14) | /* PB14 */ \ + (1 << 15) | /* PB15 */ \ + (1 << 16) | /* PB8 */ \ + (1 << 17) | /* PA15 */ \ + (1 << 18) | /* PC0 */ \ + (1 << 19) | /* PC1 */ \ + (1 << 20) | /* PC2 */ \ + (1 << 21) | /* PC3 */ \ + (1 << 22) | /* PC4 */ \ + (1 << 23) | /* PC5 */ \ + (0 << 24) | /* PC6 */ \ + (0 << 25) | /* PC7 */ \ + (0 << 26) | /* PC8 */ \ + (0 << 27) | /* PC9 */ \ + (0 << 28) | /* PC10 or PD8 */ \ + (0 << 29) | /* PC11 or PD9 */ \ + (0 << 30) | /* PC12 or PD10 */ \ + (0 << 31)) /* PD2 or PD11 */ + +#define AO_LCD_STM_SEG_ENABLED_1 ( \ + (0 << 0) | /* PD12 */ \ + (0 << 1) | /* PD13 */ \ + (0 << 2) | /* PD14 */ \ + (0 << 3) | /* PD15 */ \ + (0 << 4) | /* PE0 */ \ + (0 << 5) | /* PE1 */ \ + (0 << 6) | /* PE2 */ \ + (0 << 7)) /* PE3 */ + +#define AO_LCD_STM_COM_ENABLED ( \ + (1 << 0) | /* PA8 */ \ + (0 << 1) | /* PA9 */ \ + (0 << 2) | /* PA10 */ \ + (0 << 3) | /* PB9 */ \ + (0 << 4) | /* PC10 */ \ + (0 << 5) | /* PC11 */ \ + (0 << 6)) /* PC12 */ + +#define AO_LCD_28_ON_C 0 + +#define AO_LCD_DUTY STM_LCD_CR_DUTY_STATIC + +#define AO_LCD_PER_DIGIT 1 + +#define AO_LCD_DIGITS 3 +#define AO_LCD_SEGMENTS 8 + +#define AO_SEGMENT_MAP { \ + /* pad segments */ \ + { 0, 14 }, \ + { 0, 13 }, \ + { 0, 15 }, \ + { 0, 17 }, \ + { 0, 16 }, \ + { 0, 8 }, \ + { 0, 9 }, \ + { 0, 7 }, \ + /* box1 segments */ \ + { 0, 10 }, \ + { 0, 6 }, \ + { 0, 11 }, \ + { 0, 12 }, \ + { 0, 21 }, \ + { 0, 19 }, \ + { 0, 20 }, \ + { 0, 18 }, \ + /* box0 segments */ \ + { 0, 22 }, \ + { 0, 4 }, \ + { 0, 23 }, \ + { 0, 5 }, \ + { 0, 3 }, \ + { 0, 1 }, \ + { 0, 2 }, \ + { 0, 0 }, \ +} + +/* + * Use event queue for input devices + */ + +#define AO_EVENT 1 + +/* + * Knobs + */ + +#define AO_QUADRATURE_COUNT 2 + +#define AO_QUADRATURE_0_PORT &stm_gpioe +#define AO_QUADRATURE_0_A 3 +#define AO_QUADRATURE_0_B 2 + +#define AO_QUADRATURE_PAD 0 + +#define AO_QUADRATURE_1_PORT &stm_gpioe +#define AO_QUADRATURE_1_A 1 +#define AO_QUADRATURE_1_B 0 + +#define AO_QUADRATURE_BOX 1 + +/* + * Buttons + */ + +#define AO_BUTTON_COUNT 2 +#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_UP + +#define AO_BUTTON_0_PORT &stm_gpioe +#define AO_BUTTON_0 4 + +#define AO_BUTTON_ARM 0 + +#define AO_BUTTON_1_PORT &stm_gpioe +#define AO_BUTTON_1 5 + +#define AO_BUTTON_FIRE 1 + +#endif /* _AO_PINS_H_ */ diff --git a/src/telelco-v0.3/ao_telelco.c b/src/telelco-v0.3/ao_telelco.c new file mode 100644 index 00000000..d9f7c693 --- /dev/null +++ b/src/telelco-v0.3/ao_telelco.c @@ -0,0 +1,70 @@ +/* + * Copyright © 2011 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include <ao.h> +#include <ao_exti.h> +#include <ao_packet.h> +#include <ao_companion.h> +#include <ao_profile.h> +#include <ao_pyro.h> +#include <ao_aes.h> +#include <ao_seven_segment.h> +#include <ao_quadrature.h> +#include <ao_button.h> +#include <ao_lco.h> +#include <ao_lco_cmd.h> +#include <ao_radio_cmac_cmd.h> +#include <ao_eeprom.h> + +int +main(void) +{ + ao_clock_init(); + + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_GREEN); + ao_task_init(); + + ao_timer_init(); + + ao_spi_init(); + ao_dma_init(); + ao_exti_init(); + + ao_beep_init(); + ao_cmd_init(); + + ao_lcd_stm_init(); + ao_seven_segment_init(); + ao_quadrature_init(); + ao_button_init(); + + ao_eeprom_init(); + + ao_radio_init(); + + ao_usb_init(); + + ao_config_init(); + + ao_lco_init(); + ao_lco_cmd_init(); +// ao_radio_cmac_cmd_init(); + + ao_start_scheduler(); + return 0; +} diff --git a/src/telelco-v0.3/flash-loader/Makefile b/src/telelco-v0.3/flash-loader/Makefile new file mode 100644 index 00000000..679e61ba --- /dev/null +++ b/src/telelco-v0.3/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=telelco-v0.2 +include $(TOPDIR)/stm/Makefile-flash.defs diff --git a/src/telelco-v0.3/flash-loader/ao_pins.h b/src/telelco-v0.3/flash-loader/ao_pins.h new file mode 100644 index 00000000..6c8ff7e2 --- /dev/null +++ b/src/telelco-v0.3/flash-loader/ao_pins.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2013 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _AO_PINS_H_ +#define _AO_PINS_H_ + +/* External crystal at 8MHz */ +#define AO_HSE 8000000 + +#include <ao_flash_stm_pins.h> + +/* Arm switch. Press at power on to get boot loader */ + +#define AO_BOOT_PIN 1 +#define AO_BOOT_APPLICATION_GPIO stm_gpioe +#define AO_BOOT_APPLICATION_PIN 4 +#define AO_BOOT_APPLICATION_VALUE 1 +#define AO_BOOT_APPLICATION_MODE AO_EXTI_MODE_PULL_UP + +#endif /* _AO_PINS_H_ */ diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c index fbbc4bd9..f71c3052 100644 --- a/src/test/ao_flight_test.c +++ b/src/test/ao_flight_test.c @@ -554,9 +554,9 @@ ao_insert(void) ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation); +#if 0 int out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI); -#if 0 printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n", time, ao_state_names[ao_flight_state], @@ -717,7 +717,7 @@ ao_sleep(void *wchan) break; } #if TELEMEGA - if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) { + if ((log_format == AO_LOG_FORMAT_TELEMEGA_OLD || log_format == AO_LOG_FORMAT_TELEMEGA) && nword == 30 && strlen(words[0]) == 1) { int i; struct ao_ms5607_value value; @@ -895,7 +895,6 @@ ao_sleep(void *wchan) ao_config.accel_zero_along = atoi(words[3]); ao_config.accel_zero_across = atoi(words[5]); ao_config.accel_zero_through = atoi(words[7]); - printf ("%d %d %d\n", ao_config.accel_zero_along, ao_config.accel_zero_across, ao_config.accel_zero_through); #endif } else if (nword >= 4 && strcmp(words[0], "Main") == 0) { ao_config.main_deploy = atoi(words[2]); diff --git a/src/usbtrng-v2.0/Makefile b/src/usbtrng-v2.0/Makefile index abbdbbcc..49798f1c 100644 --- a/src/usbtrng-v2.0/Makefile +++ b/src/usbtrng-v2.0/Makefile @@ -12,6 +12,7 @@ INC = \ ao_pins.h \ ao_product.h \ ao_task.h \ + ao_adc_fast.h \ stm32f0.h # @@ -31,6 +32,7 @@ ALTOS_SRC = \ ao_boot_chain.c \ ao_cmd.c \ ao_usb_stm.c \ + ao_trng.c \ ao_task.c \ ao_product.c diff --git a/src/usbtrng-v2.0/ao_pins.h b/src/usbtrng-v2.0/ao_pins.h index 23759444..1997d205 100644 --- a/src/usbtrng-v2.0/ao_pins.h +++ b/src/usbtrng-v2.0/ao_pins.h @@ -60,4 +60,8 @@ #define AO_CRC_WIDTH 32 #define AO_CRC_INIT 0xffffffff +/* TRNG */ +#define AO_LED_TRNG_READ AO_LED_RED +#define AO_LED_TRNG_WRITE AO_LED_GREEN + #endif /* _AO_PINS_H_ */ diff --git a/src/usbtrng-v2.0/ao_usbtrng.c b/src/usbtrng-v2.0/ao_usbtrng.c index e1f43cdd..42713b6e 100644 --- a/src/usbtrng-v2.0/ao_usbtrng.c +++ b/src/usbtrng-v2.0/ao_usbtrng.c @@ -18,58 +18,7 @@ #include <ao.h> #include <ao_adc_fast.h> #include <ao_crc.h> - -static void -ao_trng_fetch(void) -{ - static uint16_t *buffer[2]; - uint32_t kbytes = 1; - uint32_t count; - int usb_buf_id; - int i; - uint16_t *buf; - uint32_t *rnd; - - if (!buffer[0]) { - buffer[0] = ao_usb_alloc(); - buffer[1] = ao_usb_alloc(); - if (!buffer[0]) - return; - } - - ao_cmd_decimal(); - if (ao_cmd_status == ao_cmd_success) - kbytes = ao_cmd_lex_u32; - else - ao_cmd_status = ao_cmd_success; - usb_buf_id = 0; - count = kbytes * (1024/AO_USB_IN_SIZE); - - ao_crc_reset(); - - ao_led_on(AO_LED_GREEN); - while (count--) { - buf = buffer[usb_buf_id]; -// printf ("before get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush(); - rnd = (uint32_t *) ao_adc_get(AO_USB_IN_SIZE); /* one 16-bit value per output byte */ -// printf ("after get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush(); - for (i = 0; i < 32; i++) - *buf++ = ao_crc_in_32_out_16(*rnd++); - ao_adc_ack(AO_USB_IN_SIZE); -// printf ("after ack: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush(); - ao_led_toggle(AO_LED_GREEN|AO_LED_RED); - ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE); - ao_led_toggle(AO_LED_GREEN|AO_LED_RED); - usb_buf_id = 1-usb_buf_id; - } - ao_led_off(AO_LED_GREEN|AO_LED_RED); - flush(); -} - -static const struct ao_cmds usbtrng_cmds[] = { - { ao_trng_fetch, "f <kbytes>\0Fetch a block of numbers" }, - { 0, NULL }, -}; +#include <ao_trng.h> void main(void) { @@ -86,7 +35,8 @@ void main(void) ao_usb_init(); - ao_cmd_register(usbtrng_cmds); + ao_trng_init(); + ao_led_off(AO_LED_RED); ao_start_scheduler(); diff --git a/src/util/ao-make-product.5c b/src/util/ao-make-product.5c index 5f2eb8e8..3ab8d16e 100644 --- a/src/util/ao-make-product.5c +++ b/src/util/ao-make-product.5c @@ -1,54 +1,58 @@ -#!/bin/sh +#!/usr/bin/nickle autoimport ParseArgs; +file out = stdout; + void write_ucs2(string a, string description) { int len = String::length(a); - printf("/* %s */\n", description); - printf("#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2); - printf("#define AO_%s_STRING \"%s\"\n", description, a); - printf("#define AO_%s_UCS2", description); + File::fprintf(out, "/* %s */\n", description); + File::fprintf(out, "#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2); + File::fprintf(out, "#define AO_%s_STRING \"%s\"\n", description, a); + File::fprintf(out, "#define AO_%s_UCS2", description); for (int i = 0; i < len; i++) { int c = a[i]; if (i > 0) - printf(","); + File::fprintf(out, ","); if (0x20 <= c && c < 128) - printf(" '%c', 0", c); + File::fprintf(out, " '%c', 0", c); else - printf(" LE_WORD(0x%04x),", c); + File::fprintf(out, " LE_WORD(0x%04x),", c); } - printf("\n\n"); + File::fprintf(out, "\n\n"); } void write_string(string a, string description) { - printf ("/* %s */\n", description); - printf ("#define AO_%s_STRING \"%s\"\n", description, a); + File::fprintf(out, "/* %s */\n", description); + File::fprintf(out, "#define AO_%s_STRING \"%s\"\n", description, a); } void write_int(int a, string description) { - printf ("/* %s */\n", description); - printf ("#define AO_%s_NUMBER %d\n\n", description, a); + File::fprintf(out, "/* %s */\n", description); + File::fprintf(out, "#define AO_%s_NUMBER %d\n\n", description, a); } void write_hex(int a, string description) { - printf ("/* %s */\n", description); - printf ("#define AO_%s_NUMBER 0x%04x\n\n", description, a); + File::fprintf(out, "/* %s */\n", description); + File::fprintf(out, "#define AO_%s_NUMBER 0x%04x\n\n", description, a); } string manufacturer = "altusmetrum.org"; string product = "TeleMetrum"; string version = "0.0"; +string output = ""; int serial = 1; int user_argind = 0; +int id_vendor = 0xfffe; int id_product = 0x000a; argdesc argd = { @@ -66,6 +70,12 @@ argdesc argd = { .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 = "id_product", @@ -83,6 +93,12 @@ argdesc argd = { .name = "version", .expr_name = "string", .desc = "Program version." }, + { + .var = { .arg_string = &output }, + .abbr = 'o', + .name = "output", + .expr_name = "out", + .desc = "Output file." }, }, .prog_name = "usb descriptors", }; @@ -92,11 +108,14 @@ main() { string[dim(argv)-1] nargv = {[n] = argv[n+1]}; parseargs(&argd, &nargv); + if (output != "") + out = File::open(output, "w"); write_ucs2(manufacturer, "iManufacturer"); write_ucs2(product, "iProduct"); write_ucs2(sprintf("%06d", serial), "iSerial"); write_int(serial, "iSerial"); write_hex(id_product, "idProduct"); + write_hex(id_vendor, "idVendor"); write_string(version, "iVersion"); } diff --git a/telegps/TeleGPS.java b/telegps/TeleGPS.java index fe335176..0208e81d 100644 --- a/telegps/TeleGPS.java +++ b/telegps/TeleGPS.java @@ -23,8 +23,9 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import java.text.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPS extends AltosUIFrame @@ -70,7 +71,7 @@ public class TeleGPS JTabbedPane pane; - AltosUIMap map; + AltosUIMapNew map; TeleGPSInfo gps_info; TeleGPSState gps_state; AltosInfoTable info_table; @@ -172,7 +173,7 @@ public class TeleGPS } void load_maps() { - new AltosUIMapPreload(this); + new AltosUIMapPreloadNew(this); } void disconnect() { @@ -372,7 +373,6 @@ public class TeleGPS public void actionPerformed(ActionEvent e) { int rate = rates.rate(); try { - System.out.printf("set rate %d\n", rate); reader.set_telemetry_rate(rate); } catch (TimeoutException te) { } catch (InterruptedException ie) { @@ -480,8 +480,6 @@ public class TeleGPS bag = getContentPane(); bag.setLayout(new GridBagLayout()); - GridBagConstraints c = new GridBagConstraints(); - setTitle("TeleGPS"); menu_bar = new JMenuBar(); @@ -491,25 +489,16 @@ public class TeleGPS monitor_menu = make_menu("Monitor", monitor_menu_entries); device_menu = make_menu("Device", device_menu_entries); + set_inset(3); frequencies = new AltosUIFreqList(); frequencies.setEnabled(false); - c.gridx = 0; - c.gridy = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - c.weightx = 0; - c.gridwidth = 1; - bag.add(frequencies, c); + bag.add(frequencies, constraints (0, 1)); rates = new AltosUIRateList(); rates.setEnabled(false); - c.gridx = 1; - c.gridy = 0; - c.fill = GridBagConstraints.NONE; - c.anchor = GridBagConstraints.WEST; - c.weightx = 0; - c.gridwidth = 1; - bag.add(rates, c); + bag.add(rates, constraints(1, 1)); + next_row(); + set_inset(0); displays = new LinkedList<AltosFlightDisplay>(); @@ -517,13 +506,9 @@ public class TeleGPS /* TeleGPS status is always visible */ telegps_status = new TeleGPSStatus(); - c.gridx = 0; - c.gridy = 1; - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = 1; - c.gridwidth = 2; - bag.add(telegps_status, c); - c.gridwidth = 1; + bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL)); + next_row(); + displays.add(telegps_status); @@ -533,15 +518,9 @@ public class TeleGPS pane = new JTabbedPane(); /* Make the tabbed pane use the rest of the window space */ - c.gridx = 0; - c.gridy = 2; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - c.gridwidth = 2; - bag.add(pane, c); - - map = new AltosUIMap(); + bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH)); + + map = new AltosUIMapNew(); pane.add(map.getName(), map); displays.add(map); @@ -650,7 +629,6 @@ public class TeleGPS public static void help(int code) { System.out.printf("Usage: altosui [OPTION]... [FILE]...\n"); System.out.printf(" Options:\n"); - System.out.printf(" --fetchmaps <lat> <lon>\tpre-fetch maps for site map view\n"); System.out.printf(" --replay <filename>\t\trelive the glory of past flights \n"); System.out.printf(" --graph <filename>\t\tgraph a flight\n"); System.out.printf(" --csv\tgenerate comma separated output for spreadsheets, etc\n"); @@ -675,16 +653,7 @@ public class TeleGPS for (int i = 0; i < args.length; i++) { if (args[i].equals("--help")) help(0); - else if (args[i].equals("--fetchmaps")) { - if (args.length < i + 3) { - help(1); - } else { - double lat = Double.parseDouble(args[i+1]); - double lon = Double.parseDouble(args[i+2]); - AltosUIMap.prefetch_maps(lat, lon); - i += 2; - } - } else if (args[i].equals("--replay")) + else if (args[i].equals("--replay")) process = process_replay; else if (args[i].equals("--kml")) process = process_kml; diff --git a/telegps/TeleGPSConfig.java b/telegps/TeleGPSConfig.java index 55763e22..e2cf1258 100644 --- a/telegps/TeleGPSConfig.java +++ b/telegps/TeleGPSConfig.java @@ -22,8 +22,8 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.text.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSConfig implements ActionListener { diff --git a/telegps/TeleGPSConfigUI.java b/telegps/TeleGPSConfigUI.java index 5e700b72..8d4916aa 100644 --- a/telegps/TeleGPSConfigUI.java +++ b/telegps/TeleGPSConfigUI.java @@ -17,12 +17,13 @@ package org.altusmetrum.telegps; +import java.text.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSConfigUI extends AltosUIDialog @@ -40,6 +41,7 @@ public class TeleGPSConfigUI JLabel rate_label; JLabel aprs_interval_label; JLabel aprs_ssid_label; + JLabel aprs_format_label; JLabel flight_log_max_label; JLabel callsign_label; JLabel tracker_motion_label; @@ -57,6 +59,7 @@ public class TeleGPSConfigUI AltosUIRateList rate_value; JComboBox<String> aprs_interval_value; JComboBox<Integer> aprs_ssid_value; + JComboBox<String> aprs_format_value; JComboBox<String> flight_log_max_value; JTextField callsign_value; JComboBox<String> tracker_motion_value; @@ -165,11 +168,20 @@ public class TeleGPSConfigUI void set_aprs_ssid_tool_tip() { if (aprs_ssid_value.isEnabled()) - aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)"); - else if (aprs_interval_value.isEnabled()) - aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID"); + aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)"); + else if (aprs_ssid_value.isEnabled()) + aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID"); else - aprs_interval_value.setToolTipText("Hardware doesn't support APRS"); + aprs_ssid_value.setToolTipText("Hardware doesn't support APRS"); + } + + void set_aprs_format_tool_tip() { + if (aprs_format_value.isEnabled()) + aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)"); + else if (aprs_format_value.isEnabled()) + aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format"); + else + aprs_format_value.setToolTipText("Hardware doesn't support APRS"); } void set_flight_log_max_tool_tip() { @@ -413,6 +425,33 @@ public class TeleGPSConfigUI set_aprs_ssid_tool_tip(); row++; + /* APRS format */ + c = new GridBagConstraints(); + c.gridx = 0; c.gridy = row; + c.gridwidth = 4; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.LINE_START; + c.insets = il; + c.ipady = 5; + aprs_format_label = new JLabel("APRS format:"); + pane.add(aprs_format_label, c); + + c = new GridBagConstraints(); + c.gridx = 4; c.gridy = row; + c.gridwidth = 4; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.anchor = GridBagConstraints.LINE_START; + c.insets = ir; + c.ipady = 5; + aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name); + aprs_format_value.setEditable(false); + aprs_format_value.addItemListener(this); + aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length); + pane.add(aprs_format_value, c); + set_aprs_format_tool_tip(); + row++; + /* Callsign */ c = new GridBagConstraints(); c.gridx = 0; c.gridy = row; @@ -648,7 +687,11 @@ public class TeleGPSConfigUI String motion = tracker_motion_value.getSelectedItem().toString(); tracker_motion_label.setText(get_tracker_motion_label()); set_tracker_motion_values(); - set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5)); + try { + int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5); + set_tracker_motion(m); + } catch (ParseException pe) { + } } if (!was_dirty) set_clean(); @@ -848,7 +891,12 @@ public class TeleGPSConfigUI } public int tracker_motion() throws AltosConfigDataException { - return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString()); + String str = tracker_motion_value.getSelectedItem().toString(); + try { + return (int) (AltosConvert.height.parse_locale(str) + 0.5); + } catch (ParseException pe) { + throw new AltosConfigDataException("invalid tracker motion %s", str); + } } public void set_tracker_interval(int tracker_interval) { @@ -894,4 +942,16 @@ public class TeleGPSConfigUI Integer i = (Integer) aprs_ssid_value.getSelectedItem(); return i; } + + public void set_aprs_format(int new_aprs_format) { + aprs_format_value.setVisible(new_aprs_format >= 0); + aprs_format_label.setVisible(new_aprs_format >= 0); + + aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format)); + set_aprs_format_tool_tip(); + } + + public int aprs_format() throws AltosConfigDataException { + return aprs_format_value.getSelectedIndex(); + } } diff --git a/telegps/TeleGPSDisplayThread.java b/telegps/TeleGPSDisplayThread.java index 09610f59..fa394b17 100644 --- a/telegps/TeleGPSDisplayThread.java +++ b/telegps/TeleGPSDisplayThread.java @@ -21,8 +21,8 @@ import java.awt.*; import javax.swing.*; import java.io.*; import java.text.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSDisplayThread extends Thread { diff --git a/telegps/TeleGPSGraphUI.java b/telegps/TeleGPSGraphUI.java index d3adc748..8fe479cc 100644 --- a/telegps/TeleGPSGraphUI.java +++ b/telegps/TeleGPSGraphUI.java @@ -26,8 +26,8 @@ import javax.swing.*; import java.io.*; import java.util.concurrent.*; import java.util.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; @@ -38,7 +38,7 @@ public class TeleGPSGraphUI extends AltosUIFrame JTabbedPane pane; AltosGraph graph; AltosUIEnable enable; - AltosUIMap map; + AltosUIMapNew map; AltosState state; AltosFlightStats stats; AltosGraphDataSet graphDataSet; @@ -69,7 +69,7 @@ public class TeleGPSGraphUI extends AltosUIFrame graph = new AltosGraph(enable, stats, graphDataSet); statsTable = new AltosFlightStatsTable(stats); - map = new AltosUIMap(); + map = new AltosUIMapNew(); pane.add("Graph", graph.panel); pane.add("Configure Graph", enable); diff --git a/telegps/TeleGPSInfo.java b/telegps/TeleGPSInfo.java index f20a8830..6baccce9 100644 --- a/telegps/TeleGPSInfo.java +++ b/telegps/TeleGPSInfo.java @@ -21,8 +21,8 @@ import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSInfo extends AltosUIFlightTab { diff --git a/telegps/TeleGPSPreferences.java b/telegps/TeleGPSPreferences.java index 6d7b8985..d0b843e1 100644 --- a/telegps/TeleGPSPreferences.java +++ b/telegps/TeleGPSPreferences.java @@ -22,7 +22,7 @@ import java.awt.event.*; import java.beans.*; import javax.swing.*; import javax.swing.event.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSPreferences extends AltosUIConfigure diff --git a/telegps/TeleGPSState.java b/telegps/TeleGPSState.java index 18f500b9..dac7da52 100644 --- a/telegps/TeleGPSState.java +++ b/telegps/TeleGPSState.java @@ -21,8 +21,8 @@ import java.util.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSState extends AltosUIFlightTab { @@ -161,6 +161,26 @@ public class TeleGPSState extends AltosUIFlightTab { } } + class ReceiverBattery extends AltosUIVoltageIndicator { + + public double voltage(AltosState state) { return AltosLib.MISSING; } + + public double good() { return AltosLib.ao_battery_good; } + + public boolean hide(AltosState state, AltosListenerState listener_state, int i) { + return value(state, listener_state, i) == AltosLib.MISSING; + } + + public double value(AltosState state, AltosListenerState listener_state, int i) { + if (listener_state == null) + return AltosLib.MISSING; + return listener_state.battery; + } + + public ReceiverBattery (AltosUIFlightTab container, int y) { + super(container, y, "Receiver Battery", 2); + } + } public void labels(Container container, int y) { GridBagLayout layout = (GridBagLayout)(container.getLayout()); @@ -203,5 +223,6 @@ public class TeleGPSState extends AltosUIFlightTab { add(new FirmwareVersion(this, y++)); add(new FlightLogMax(this, y++)); add(new BatteryVoltage(this, y++)); + add(new ReceiverBattery(this, y++)); } } diff --git a/telegps/TeleGPSStatus.java b/telegps/TeleGPSStatus.java index 050a8449..896df9d4 100644 --- a/telegps/TeleGPSStatus.java +++ b/telegps/TeleGPSStatus.java @@ -19,8 +19,8 @@ package org.altusmetrum.telegps; import java.awt.*; import javax.swing.*; -import org.altusmetrum.altoslib_6.*; -import org.altusmetrum.altosuilib_6.*; +import org.altusmetrum.altoslib_8.*; +import org.altusmetrum.altosuilib_8.*; public class TeleGPSStatus extends JComponent implements AltosFlightDisplay { GridBagLayout layout; diff --git a/telegps/TeleGPSStatusUpdate.java b/telegps/TeleGPSStatusUpdate.java index 9c085a5b..eef0a034 100644 --- a/telegps/TeleGPSStatusUpdate.java +++ b/telegps/TeleGPSStatusUpdate.java @@ -18,7 +18,7 @@ package org.altusmetrum.telegps; import java.awt.event.*; -import org.altusmetrum.altoslib_6.*; +import org.altusmetrum.altoslib_8.*; public class TeleGPSStatusUpdate implements ActionListener { diff --git a/telegps/telegps-windows.nsi.in b/telegps/telegps-windows.nsi.in index 44656715..b0b5d6a6 100644 --- a/telegps/telegps-windows.nsi.in +++ b/telegps/telegps-windows.nsi.in @@ -103,16 +103,17 @@ Section "${REG_NAME} Application" File "freetts.jar" File "jfreechart.jar" File "jcommon.jar" + File "../icon/${WIN_APP_EXE}" File "*.dll" File "../icon/${WIN_APP_ICON}" - CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "${REG_NAME} Desktop Shortcut" - CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}" + CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}" SectionEnd Section "TeleGPS, TeleDongle and TeleBT Firmware" @@ -141,7 +142,6 @@ Section "File Associations" SetOutPath $INSTDIR - File "../icon/${WIN_APP_EXE}" File "../icon/${WIN_TELEM_EXE}" File "../icon/${WIN_EEPROM_EXE}" @@ -153,15 +153,13 @@ Section "File Associations" DeleteRegKey HKCR ".telem\${PROG_ID_EEPROM}" DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}" - SearchPath $1 "javaw.exe" - ; .eeprom elements WriteRegStr HKCR "${PROG_ID_EEPROM}" "" "Altus Metrum Log File" WriteRegStr HKCR "${PROG_ID_EEPROM}" "FriendlyTypeName" "Altus Metrum Log File" WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer" "" "${PROG_ID_EEPROM}" WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" "" '"$INSTDIR\${WIN_EEPROM_EXE}",-101' - WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"' + WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"' WriteRegStr HKCR ".eeprom" "" "${PROG_ID_EEPROM}" WriteRegStr HKCR ".eeprom" "PerceivedType" "Altus Metrum Log File" @@ -176,7 +174,7 @@ Section "File Associations" WriteRegStr HKCR "${PROG_ID_TELEM}" "FriendlyTypeName" "Altus Metrum Telemetry File" WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer" "" "${PROG_ID_TELEM}" WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" "" '"$INSTDIR\${WIN_TELEM_EXE}",-101' - WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"' + WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" "" '"$INSTDIR\${WIN_APP_EXE}" "%1"' WriteRegStr HKCR ".telem" "" "${PROG_ID_TELEM}" WriteRegStr HKCR ".telem" "PerceivedType" "Altus Metrum Telemetry File" |