+commit 8750dde659cec836fa6354651d5967b7aac1dff0
+Merge: dcd1feb8 24215a4a
+Author: Bdale Garbee <bdale@gag.com>
+Date: Tue Jun 8 22:56:04 2021 -0600
+
+ Merge branch 'master' into branch-1.9
+
+commit 24215a4a2049e51c335b76767f9ed13d186ac408
+Author: Bdale Garbee <bdale@gag.com>
+Date: Tue Jun 8 22:51:28 2021 -0600
+
+ doc: update copyright year assertions
+
+commit ec00f55171a6c5c827c1296178be43d311801be8
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Apr 12 18:39:48 2021 -0700
+
+ Version 1.9.7
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b5ab12ca11272479330a1d630da15cbf0d76735c
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 11:01:16 2021 -0700
+
+ doc: Update 1.9.7 release notes
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1d9aaab9209a7afea60a8b394e4d3d5325f0f01e
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Jun 7 16:14:25 2021 -0700
+
+ altosdroid: Bump to version 29
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3104c3d2b1e781592f5d6841ba1ef2ba6c49642b
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat May 15 20:19:56 2021 -0700
+
+ altosdroid: Update target API to 29
+
+ Required by current Android store
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82ef65a98e8e018fe8aa0665fd8a4af3fff3097a
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue May 11 22:28:32 2021 -0700
+
+ altosdroid: Revert getResource().getColor to old API
+
+ This should keep it compatible with API version 21
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 76847aadc1ea770099c6be05727dfa232e53205c
+Merge: daa635de 7643f408
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Jun 7 17:11:01 2021 -0600
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 7643f408834a872ed5d7ae67770b1b7c98f3b90c
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun May 30 15:08:25 2021 -0700
+
+ Version 1.9.6.5
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b45adfbc0eb769800068c2d432e9db52425ee316
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 10:52:47 2021 -0700
+
+ altoslib: Protect has_monitor_battery from unset product
+
+ AltosDroid can query has_monitor_battery before the product data has
+ been set.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9a427131788a3e477629a0de26f0f41b5e98333b
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 10:51:44 2021 -0700
+
+ altosdroid: Don't start bluetooth if address is null
+
+ If the address.address string is null, don't bother starting the
+ bluetooth service.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9eb0fbd7eff5694064f9d123220b523a98fef0a4
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 10:50:22 2021 -0700
+
+ altosdroid: protect USB read/write when connection is null
+
+ Check to see if connection is valid before attempting to read/write to
+ it.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 55a820e517f9705bc80c653b456ce5d8b3a634bc
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 10:48:58 2021 -0700
+
+ altosdroid: Synchronize access to the 'rockets' list for online maps
+
+ Online maps gets rockets added by the telem code and the same data are
+ used to create the maps UI. Synchronise access to that object to prevent
+ simutaneous operations.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 09a2a37b31b816236f023ba2a1d767646d5c8f34
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed May 19 10:47:37 2021 -0700
+
+ altosdroid: Safeguard preferences code in case it's called too early
+
+ Make sure there's a backend set up before accessing data as it seems
+ this code can be called before the preferences code is called before
+ the backend is created.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a7cc99d9db45c6c4ba929acf5b57e22d826c82a
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue May 18 23:26:12 2021 -0700
+
+ altosdroid: Skip clicks on BT device entries that are too short
+
+ Maybe these are some kind of extra object? In any case, nothing that
+ we care about, so just ignore the click.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit daa635de77da3a1926ceb2e2d91e31ec169e173c
+Merge: a2e71a40 362f11ff
+Author: Bdale Garbee <bdale@gag.com>
+Date: Tue May 18 23:54:48 2021 -0600
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 362f11fffb63c5c4d4e2ccfc59c0e6ae83a55d01
+Author: Keith Packard <keithp@keithp.com>
+Date: Tue May 18 22:37:01 2021 -0700
+
+ altoslib: Fix accel value flipping for TM v3.0
+
+ TM v3.0 uses the same log file format value as TM v2.0 but has a
+ different accelerometer, which requires a different function for
+ inverting the raw values. Detect v2.0 devices and use the old function
+ for them while using the new function for all others.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a575eebbf87243c3a314929a2469db5bac0c7b42
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon May 17 23:09:29 2021 -0700
+
+ altosuilib: Zap all flash when upgrading TeleGPS from pre-1.9.7
+
+ Old versions of TeleGPS firmware would end up spraying log data all
+ over flash as they mis-computed the place to append new log data.
+
+ When the right hardware is detected, a warning dialog will pop up and,
+ if agreed to, the log storage flash will be completely erased before
+ the firmware upgrade happens.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 565778b66e59069fc6a6d6518f28354eae954dc1
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon May 17 22:38:14 2021 -0700
+
+ altos: Simplify discovery of log end position
+
+ Binary search using log block indices rather than byte positions. This
+ makes the code much easier to understand as there isn't a mystic modulus.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8dec0d1be5a2d7633045c5c0e86b32a9e6b60299
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon May 17 22:33:21 2021 -0700
+
+ altos/telegps-*: Fix log end discovery at startup
+
+ We need to find the first unwritten log block to start appending data,
+ but the code was actually looking for the first empty 256-byte chunk,
+ which meant that we'd leave a gap of erased data after the previous
+ log. AltosUI would stop at that point and not recover the remaining
+ stored data.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a2e71a40e53602c0cebe4c36d3658201dc0c2bae
+Author: Bdale Garbee <bdale@gag.com>
+Date: Wed May 12 00:27:55 2021 -0600
+
+ ao-bringup: fix easymotor script to use .bin for dfu-util
+
+commit fa273e51e772540f61fffbdc4431fe07bcd57630
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Apr 12 18:38:50 2021 -0700
+
+ doc: Update for 1.9.7
+
+ Add release notes
+ Add specs for EasyMotor
+ Update copyright year to 2021
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4708930ebfbc056bb4faae9b23720d3be401978
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Apr 12 18:39:48 2021 -0700
+
+ Version 1.9.7
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b8437a3f994845dd84080cc20122494aaf901124
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Apr 12 18:36:45 2021 -0600
+
+ ao-bringup: add tools for flash/cal/test of EasyMotor v2
+
+commit 1451e2fd2092d720b0d49b93ac01bed7e88b831f
+Merge: 9c26fe3e b115522c
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Apr 12 18:27:17 2021 -0600
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 9c26fe3e4fddfd2a3f3e5a7da68ef65422053063
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Apr 12 18:26:46 2021 -0600
+
+ ao-tools: update ao-flash-stm32f0x to work with openocd in Debian unstable
+
+commit b115522c41228b26133f322ea68ddb187c0b68cc
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Apr 12 13:04:20 2021 -0700
+
+ doc: Fix typography in motortest doc
+
+ 1. Use correct quotes.
+ 2. Eliminate double space after punctuation.
+ 3. Use elipses instead of three full stops.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 410af114b4827e46a5a297dbb7c26dc087b932fb
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Apr 12 14:02:32 2021 -0600
+
+ altos: include EasyMotor v2 firmware in upcoming release
+
+commit 21da0503635a643529d457dccd7e12eb39029fdb
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Apr 12 13:51:06 2021 -0600
+
+ docs: minor text tweak in motor testing docs
+
+commit c6bcfa5ede86a718105cc334099e4a6b028b08c3
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Mar 31 09:23:14 2021 -0700
+
+ altoslib: Write IMU headers to CSV file when present
+
+ The IMU data were being written, but somehow the header was not
+ included.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d6be8a279ad233d998c6df8b2efafa34dd5a9a98
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Mar 28 18:03:56 2021 -0700
+
+ Version 1.9.6.4
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit da8d7fde56bfd7db02598d2880653fa71846abf7
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Mar 28 18:03:03 2021 -0700
+
+ JavaApplicationStub hacks for Big Sur tabbing mode
+
+ Java does not support the 'tabbing' stuff that's in Big Sur, so
+ disable it.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81a6f20fca5df08f3ac08d83a79439502c209df0
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Mar 27 09:37:49 2021 -0700
+
+ Add local hacks to JavaApplicationStub
+
+ Fix font rendering.
+ Add app directory to java.library.path.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2d45e21175453a69112fde22bf5b662d9e32adb
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Mar 22 00:06:14 2021 -0700
+
+ Update JavaApplicationStub to latest release
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c6ef894263068839782716fece54154effd3d0fe
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Mar 22 00:02:33 2021 -0700
+
+ Add Mac OS X 11 support to JavaApplicationStub
+
+commit 1d29a584c8387798fb1558fd54a09b1d8fbe90b7
+Author: Keith Packard <keithp@keithp.com>
+Date: Sun Mar 21 23:10:25 2021 -0700
+
+ altosuilib: Show launch sites at all visible locations on map
+
+ Take each launch site and draw it at every location on the map it
+ occurs (in case the map shows more than the full globe). This also
+ automatically handles scrolling the map more than one "rotation".
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 07eecc0ff6e1104f911e5f83d67f3e14dc68c59c
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Feb 27 12:51:56 2021 -0800
+
+ Version 1.9.6.2
+
+ Fix micropeak Download on Mac OS X
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 38b360b0b7080b06998d1cac1d6d09957fa44844
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Feb 27 13:38:54 2021 -0800
+
+ altosui: open /Library/AltusMetrum on Mac OS X after install
+
+ This helps the user find the documentation.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 997931d545c977250918a2d608f8c5756de2afcf
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Feb 27 13:23:16 2021 -0800
+
+ micropeak: Stick docs in Doc dir on Mac OS X
+
+ Follows altosui and telegps installation
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c07b0cd5881ae4e101c41ffa7a1dc6980c3ef357
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Feb 27 13:22:39 2021 -0800
+
+ altosui: Show dialog box if sudo fails on Mac OS X
+
+ If the user types the wrong password three times, sudo will give up
+ and return an error.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82bad3d62b91e67f6089e403c4bd4983bf65e449
+Author: Keith Packard <keithp@keithp.com>
+Date: Sat Feb 27 12:51:13 2021 -0800
+
+ micropeak: Use a menu for 'Download' on Mac OS X
+
+ Attempts to add buttons to the menu bar fail leaving no access to the
+ download command.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0c33e88479ce5fe578cec4296d6196356175d40a
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Feb 15 14:27:30 2021 -0700
+
+ doc: add motortest docs to publish targets
+
+commit b8e21caf9602b55e9a042f8f0b3cfed1d8975c15
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Feb 15 14:25:47 2021 -0700
+
+ doc: have motortest documents actually get built by default
+
+commit 6ac9b490efca17b15317965026c56b4a37d6be82
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Feb 15 14:12:46 2021 -0700
+
+ doc: make doc be about motor testing in general, not just EasyMotor
+
+commit afc90f63c6a0c2a511fddc97b72537e0a6f40cfd
+Author: Bdale Garbee <bdale@gag.com>
+Date: Sat Jan 30 11:48:01 2021 -0700
+
+ doc: add a brief note about TeleBT appearing inert until paired
+
+commit 7f4059bbe74ecf86842134739ec521dcb646f04f
+Author: Bdale Garbee <bdale@gag.com>
+Date: Sun Jan 17 19:29:28 2021 -0700
+
+ doc: first draft of manual for EasyMotor
+
+commit 3af81a45be69b4693396de4a5e2c386be566b933
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Dec 4 14:58:15 2020 -0800
+
+ altoslib: Add all known launch sites and names to map preload
+
+ This shows all of the known launch sites and their names in the map
+ preload screen.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a4c40ba682acc3ed1808d5a170ddae4114740a39
+Author: Bdale Garbee <bdale@gag.com>
+Date: Sat Nov 21 11:22:01 2020 -0700
+
+ doc: add documentation on how to re-flash a TeleMini v3 over USB
+
+commit 6550585af6d80306d037661be66004ccf8d09013
+Merge: c992792b 42226344
+Author: Bdale Garbee <bdale@gag.com>
+Date: Sat Nov 21 11:00:43 2020 -0700
+
+ Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 42226344f0b5443fdd93034dd51e608370717c46
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Nov 5 21:59:27 2020 -0800
+
+ ao-tools/ao-dump-up: Recognize MicroTest data
+
+ Spit out a special message when MicroTest data is read to make testing
+ µPusb easier.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9d4856d3f03fae159c120d8d8030c78eaf15253
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Nov 5 21:58:04 2020 -0800
+
+ altos: Add 'microtest' -- micropeak load for testing µPusb
+
+ This is custom MicroPeak firmware that just repeatedly generates
+ constant flight log data which can be used to validate µPusb boards.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 898ef1a085f9d4541c3987d8d4f0daac0093ed49
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 29 11:41:55 2020 -0700
+
+ Version 1.9.6.1
+
+ Intermediate release containing altoslib fixes for TeleMega v4.0 in
+ Antenna Down configuration.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6dc5b468f84f04f28ea83757cee0486f654fb234
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 29 11:40:25 2020 -0700
+
+ altoslib: Delay accel cal value adjustment until data all read
+
+ Instead of trying to compute these values as soon as the necessary
+ data are available, delay until all data are available as which data
+ are necessary depends on the target device, and some target devices
+ don't have some of the data values at all.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ceca79d6b20cf623e7a7214e400347fc0bc019dd
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 29 11:38:55 2020 -0700
+
+ altoslib: Fix accel inversion for TeleMega v4.0
+
+ TeleMega v4.0 uses the adxl375, just like EasyMega v2.0
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4358d83ba96b072cabd344e287fa77005968690f
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 29 11:37:03 2020 -0700
+
+ altoslib: Avoid sending negative accel cal values to flight computer
+
+ Pre-1.9.7 firmware does not handle negative values. Instead, send
+ large positive values which will wrap around to negative values inside
+ the flight computer.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d1a2932e080041cfe107e00e7b23213026d1fb81
+Author: Keith Packard <keithp@keithp.com>
+Date: Wed Oct 28 14:32:16 2020 -0700
+
+ ao-bringup: Delay before testing easymini for 0.25 seconds
+
+ EasyMini isn't quite ready to play when the USB is first detected as
+ the sensors haven't yet been initialized. Wait for 0.25 seconds to
+ give the board time to finish initializing.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a1d6499fd7ca2b3a9702e21af2a7584ef2b6480
+Author: Keith Packard <keithp@keithp.com>
+Date: Mon Oct 26 21:53:54 2020 -0700
+
+ altos: Don't wait after igniter in manual mode
+
+ There's no reason to delay between igniter firings in manual mode, and
+ it slows down testing.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c992792ba6a76a0bc1d31ccdcffa2d6ca52a9e55
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Nov 16 10:50:49 2020 -0700
+
+ put notes in Releasing about keeping doc/header.inc up to date
+
+commit 427e67f9e8914327243c0fdd1379365fe4e03623
+Author: Bdale Garbee <bdale@gag.com>
+Date: Mon Nov 16 10:47:53 2020 -0700
+
+ TeleLaunch docs should use telelaunch.txt, update copyright year, lose "draft"
+
+commit 6e001281b01176963836d7b7d0bd6973b788dd63
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 17:05:37 2020 -0700
+
+ altoslib, altosui: Don't show apogee/main for EasyTimer Fire Igniter
+
+ Don't create igniter lines for a device which doesn't have them.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a214f039e8404da7529b6799ab6a907c3744e1bc
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:14:13 2020 -0700
+
+ altos/easytimer-v1: Switch from including adxl375 to bmx160
+
+ These aren't really necessary at all as ao_data includes them, but
+ it's nice to have them listed for documentation purposes.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a3c0805f88c50f27dd4e78b9be2a4c3ef9e46c8
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:13:47 2020 -0700
+
+ altos/easymotor-v2: Increase default flight log size
+
+ Switch to 4 flights by default, with 2MB per flight.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9014be707c4325b55e0f2797796e7c96aea1e03
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:12:36 2020 -0700
+
+ altosui: Match against basestations when determining remote
+
+ These two cases were matching against !altimeters instead, which isn't
+ true for EasyMotor or EasyTimer.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 40c6aa050654d43f20c6a9c6bd67541e1747ae38
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:10:43 2020 -0700
+
+ altoslib, altosuilib: Get Idle Monitor working with EasyTimer and EasyMotor
+
+ This involve splitting out the gyro and mag sensor handling from the
+ 3-d accel stuff, displaying only information that is present. The IMU
+ support now allows for using the along axis as the primary
+ acceleration indicator. The Adxl375 now allows using all three axes as
+ the 3d accelerometer.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b6c066d7261d398cb7bfe6503518022194431337
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:02:07 2020 -0700
+
+ altoslib: Set all 3 axes of accel cal data when present
+
+ Use the new accel cal function in altos to set all three axes
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f7c8f0b14cf19804106860a5689cf1f37df20669
+Author: Keith Packard <keithp@keithp.com>
+Date: Fri Oct 23 16:00:01 2020 -0700
+
+ altos: Make accel cal take three axes when present
+
+ This allows AltosUI to save/restore all of the accel calibration
+ values instead of just the primary axis.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ecf782359d3038399049ec5cc0a2b3071e14c78f
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 22 20:48:26 2020 -0700
+
+ libaltos: Add windows override for EasyTimer USB ID
+
+ Windows doesn't use the product name from the device, instead it uses
+ whatever was in the .ini file, which was "TeleTerra" for the EasyTimer ID.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 365bfb9c0720d537c7d65baf871d5dcd7c08de35
+Author: Keith Packard <keithp@keithp.com>
+Date: Thu Oct 22 20:52:03 2020 -0700
+
+ Update 1.9.6 release date. Add note to Releasing about date
+
+ Need to update the release date as well as the release version; that's
+ where all of the dates in the various docs comes from.
+
+ Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dcd1feb83e5534ea3135358456b748f242878b46
+Author: Bdale Garbee <bdale@gag.com>
+Date: Thu Oct 22 16:19:33 2020 -0600
+
+ releaseing 1.9.6
+
commit 38bcc2b8b2b560271902eb8a3eba467866a38628
Merge: 628da1fe c16cb712
Author: Bdale Garbee <bdale@gag.com>
src/easymega-v1.0/easymega-v1.0-$(VERSION).ihx \
src/easymega-v2.0/easymega-v2.0-$(VERSION).ihx \
src/easymini-v1.0/easymini-v1.0-$(VERSION).ihx \
+ src/easymotor-v2/easymotor-v2-$(VERSION).ihx \
src/easytimer-v1/easytimer-v1-$(VERSION).ihx \
src/telebt-v3.0/telebt-v3.0-$(VERSION).ihx \
src/telebt-v4.0/telebt-v4.0-$(VERSION).ihx \
- make sure doc/Makefile points at that too
+ - confirm doc/header.inc has correct copyright year
+
These are Bdale's notes on how to do a release.
- make sure Debian build environment is up to date
- make sure there is a doc/release-notes-<version>.inc
- make sure that doc/*.txt have the right copyright year and the
new release is included
+ - confirm doc/header.inc has correct copyright year
- make absolutely sure checked-out tree is "clean"
- make absolutely sure any commits Keith might have pushed to branches
like debian are already pulled
src/easymega-v2.0/{*.elf,*.ihx,*.map} \
src/easymini-v1.0/{*.elf,*.ihx,*.map} \
src/easymini-v2.0/{*.elf,*.ihx,*.map} \
+ src/easymotor-v2/{*.elf,*.ihx,*.map} \
src/easytimer-v1/{*.elf,*.ihx,*.map} \
src/telebt-v3.0/{*.elf,*.ihx,*.map} \
src/telebt-v4.0/{*.elf,*.ihx,*.map} \
src/easymega-v2.0/flash-loader/*.elf \
src/easymini-v1.0/flash-loader/*.elf \
src/easymini-v2.0/flash-loader/{*.elf,*.bin,*.map} \
+ src/easymotor-v2/flash-loader/*.elf \
src/easytimer-v1/flash-loader/*.elf \
src/telebt-v3.0/flash-loader/*.elf \
src/telebt-v4.0/flash-loader/{*.elf,*.bin,*.map} \
defaultConfig {
applicationId "org.altusmetrum.AltosDroid"
minSdkVersion 21
- targetSdkVersion 28
+ targetSdkVersion 29
versionCode @ANDROID_VERSION@
versionName "@VERSION@"
}
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, getTheme());
+ mAgeOldColor = getResources().getColor(R.color.old_color);
}
private void ensureBluetooth() {
}
public static void set_active_device(DeviceAddress address) {
+ if (backend == null)
+ return;
synchronized(backend) {
active_device_address = address;
if (active_device_address != null) {
}
public static DeviceAddress active_device() {
+ if (backend == null)
+ return null;
+
synchronized(backend) {
return active_device_address;
}
static LinkedList<AltosDroidMapSourceListener> map_source_listeners;
public static void set_map_source(int map_source) {
+ if (backend == null)
+ return;
synchronized(backend) {
AltosDroidPreferences.map_source = map_source;
backend.putInt(mapSourcePreference, map_source);
}
public static int map_source() {
+ if (backend == null)
+ return MAP_SOURCE_ONLINE;
synchronized(backend) {
return map_source;
}
}
public static void register_map_source_listener(AltosDroidMapSourceListener l) {
+ if (backend == null)
+ return;
+
synchronized(backend) {
if (map_source_listeners == null)
map_source_listeners = new LinkedList<AltosDroidMapSourceListener>();
}
public static void unregister_map_source_listener(AltosDroidMapSourceListener l) {
+ if (backend == null)
+ return;
synchronized(backend) {
map_source_listeners.remove(l);
}
}
public static int font_size() {
+ if (backend == null)
+ return font_size_medium;
+
synchronized (backend) {
return font_size;
}
}
public static void set_font_size(int new_font_size) {
+ if (backend == null)
+ return;
synchronized (backend) {
if (font_size != new_font_size) {
font_size = new_font_size;
public static int tracker_sort() {
+ if (backend == null)
+ return 0;
+
synchronized(backend) {
return tracker_sort;
}
}
public static void set_tracker_sort(int new_tracker_sort) {
+ if (backend == null)
+ return;
+
synchronized(backend) {
if (tracker_sort != new_tracker_sort) {
tracker_sort = new_tracker_sort;
MapMark(double lat, double lon, int state) {
super(lat, lon, state);
}
+
+ MapMark(double lat, double lon, int state, String label) {
+ super(lat, lon, state, label);
+ }
}
public AltosMapMark new_mark(double lat, double lon, int state) {
return new MapMark(lat, lon, state);
}
+ public AltosMapMark new_mark(double lat, double lon, int state, String label) {
+ return new MapMark(lat, lon, state, label);
+ }
+
public int width() {
return getWidth();
}
}
private RocketOnline[] sorted_rockets() {
- RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]);
+ synchronized(rockets) {
+ RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]);
- Arrays.sort(rocket_array);
- return rocket_array;
+ Arrays.sort(rocket_array);
+ return rocket_array;
+ }
}
public void onMapClick(LatLng lat_lng) {
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);
+ synchronized(rockets) {
+ 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);
+ synchronized(rockets) {
+ RocketOnline rocket = rockets.get(serial);
+ rocket.remove();
+ rockets.remove(serial);
+ }
}
public void set_visible(boolean visible) {
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.containsKey(serial))
- remove_rocket(serial);
- }
+ synchronized(rockets) {
+ for (int serial : rockets.keySet()) {
+ if (!telem_state.containsKey(serial))
+ remove_rocket(serial);
+ }
- for (int serial : telem_state.keySet()) {
- set_rocket(serial, telem_state.get(serial));
+ for (int serial : telem_state.keySet()) {
+ set_rocket(serial, telem_state.get(serial));
+ }
}
}
}
int read(byte[] buffer, int len) {
+ if (connection == null)
+ return 0;
int ret = connection.bulkTransfer(in, buffer, len, -1);
AltosDebug.debug("read(%d) = %d\n", len, ret);
return ret;
}
int write(byte[] buffer, int len) {
+ if (connection == null)
+ return 0;
int ret = connection.bulkTransfer(out, buffer, len, -1);
AltosDebug.debug("write(%d) = %d\n", len, ret);
return ret;
// The on-click listener for all devices in the ListViews
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
+ // Get the device MAC address, which is the last 17 chars in the View
+ String info = ((TextView) v).getText().toString();
+
+ /* Ignore clicks on items that are too short */
+ if (info.length() <= 17)
+ return;
+
// Cancel discovery because it's costly and we're about to connect
mBtAdapter.cancelDiscovery();
- // Get the device MAC address, which is the last 17 chars in the View
- String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() - 17);
int newline = info.indexOf('\n');
}
private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
- if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled())
+ if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled() || address.address == null)
return;
disconnect(false);
plus, minus);
if (config_data.pad_orientation != AltosLib.MISSING)
link.printf("c o %d\n", config_data.pad_orientation);
- if (plus != AltosLib.MISSING && minus != AltosLib.MISSING)
- link.printf("c a %d %d\n", plus, minus);
+ if (plus != AltosLib.MISSING && minus != AltosLib.MISSING && plus != 0) {
+ if (plus < 0)
+ plus = 65536 + plus;
+ if (minus < 0)
+ minus = 65536 + minus;
+ if (config_data.accel_zero_along != AltosLib.MISSING)
+ link.printf("c a %d %d %d %d %d\n",
+ plus, minus,
+ config_data.accel_zero_along,
+ config_data.accel_zero_across,
+ config_data.accel_zero_through);
+ else
+ link.printf("c a %d %d\n", plus, minus);
+ }
link.flush_output();
stop_link();
}
public class AltosAdxl375 implements Cloneable {
- private int accel;
+ private int[] accels = new int[3];
private int axis;
public static final int X_AXIS = 0;
if (axis == AltosLib.MISSING)
throw new NumberFormatException("No ADXL375 axis specified");
if (items.length >= 3) {
- accel = Integer.parseInt(items[2 + axis]);
+ for (int i = 0; i < 3; i++)
+ accels[i] = Integer.parseInt(items[2+i]);
return true;
}
}
public AltosAdxl375 clone() {
AltosAdxl375 n = new AltosAdxl375(axis);
- n.accel = accel;
+ for (int i = 0; i < 3; i++)
+ n.accels[i] = accels[i];
return n;
}
- static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, AltosUnknownProduct {
+ private int accel_along() {
+ return accels[axis];
+ }
+
+ private int accel_across() {
+ if (axis == X_AXIS)
+ return accels[Y_AXIS];
+ else
+ return accels[X_AXIS];
+ }
+
+ private int accel_through() {
+ return accels[Z_AXIS];
+ }
+
+ static public void provide_data(AltosDataListener listener, AltosLink link, boolean three_axis, int imu_type) throws InterruptedException, AltosUnknownProduct {
try {
AltosCalData cal_data = listener.cal_data();
AltosAdxl375 adxl375 = new AltosAdxl375(link, cal_data.adxl375_axis);
if (adxl375 != null) {
- int accel = adxl375.accel;
- if (cal_data.adxl375_inverted)
+ int accel = adxl375.accel_along();
+ if (!cal_data.adxl375_inverted)
accel = -accel;
if (cal_data.pad_orientation == 1)
accel = -accel;
listener.set_acceleration(cal_data.acceleration(accel));
+ if (three_axis) {
+ cal_data.set_imu_type(imu_type);
+ double accel_along = cal_data.accel_along(-accel);
+ double accel_across = cal_data.accel_across(adxl375.accel_across());
+ double accel_through = cal_data.accel_through(adxl375.accel_through());
+ listener.set_accel_ground(accel_along,
+ accel_across,
+ accel_through);
+ listener.set_accel(accel_along,
+ accel_across,
+ accel_through);
+ }
}
} catch (TimeoutException te) {
} catch (NumberFormatException ne) {
}
public AltosAdxl375() {
- accel = AltosLib.MISSING;
+ for (int i = 0; i < 3; i++)
+ accels[i] = AltosLib.MISSING;
axis = AltosLib.MISSING;
}
out.printf(",");
write_3d_accel_header();
}
+ if (has_imu) {
+ out.printf(",");
+ write_imu_header();
+ }
if (has_igniter) {
out.printf(",");
write_igniter_header();
case AltosLib.AO_LOG_FORMAT_FULL:
return 0x7fff - value;
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
- case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
- case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
return 4095 - value;
+ case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
+ /*
+ * TeleMetrum v2 and later use the same log format, but
+ * have different accelerometers. This is the only place
+ * it matters in altoslib.
+ */
+ if (product.startsWith("TeleMetrum-v2"))
+ return 4095 - value;
+ /* fall through */
+ case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
+ case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
return -value;
default:
+ if (product.startsWith("EasyTimer-"))
+ return -value;
return AltosLib.MISSING;
}
}
public boolean has_monitor_battery() {
+ if (product == null)
+ return false;
if (product.startsWith("TeleBT"))
return true;
return false;
/* Return + accel calibration relative to a specific pad orientation */
public int accel_cal_plus(int pad_orientation) {
adjust_accel_cal();
+ if (!accel_cal_adjusted)
+ return AltosLib.MISSING;
+
switch (pad_orientation) {
case AltosLib.AO_PAD_ORIENTATION_ANTENNA_UP:
case AltosLib.AO_PAD_ORIENTATION_WORDS_UPRIGHT:
/* Return - accel calibration relative to a specific pad orientation */
public int accel_cal_minus(int pad_orientation) {
adjust_accel_cal();
+ if (!accel_cal_adjusted)
+ return AltosLib.MISSING;
+
switch (pad_orientation) {
case AltosLib.AO_PAD_ORIENTATION_ANTENNA_UP:
case AltosLib.AO_PAD_ORIENTATION_WORDS_UPRIGHT:
*/
private void adjust_accel_cal() {
if (!accel_cal_adjusted &&
+ product != null &&
pad_orientation != AltosLib.MISSING &&
accel_cal_plus != AltosLib.MISSING &&
- accel_cal_minus != AltosLib.MISSING &&
- log_format != AltosLib.AO_LOG_FORMAT_UNKNOWN)
+ accel_cal_minus != AltosLib.MISSING)
{
switch (pad_orientation) {
case AltosLib.AO_PAD_ORIENTATION_ANTENNA_UP:
}
}
} catch (Exception e) {}
-
- /* Fix accel cal as soon as all of the necessary values appear */
- adjust_accel_cal();
}
public AltosConfigData() {
return true;
if (product.startsWith("TeleMega-v4"))
return true;
+ if (product.startsWith("EasyMotor-v2"))
+ return true;
}
throw new AltosUnknownProduct(product);
}
return AltosAdxl375.X_AXIS;
if (product.startsWith("TeleMega-v4"))
return AltosAdxl375.X_AXIS;
+ if (product.startsWith("EasyMotor-v2"))
+ return AltosAdxl375.X_AXIS;
+
}
throw new AltosUnknownProduct(product);
}
link.printf("c o %d\n", pad_orientation);
int plus = accel_cal_plus(pad_orientation);
int minus = accel_cal_minus(pad_orientation);
- if (plus != AltosLib.MISSING && minus != AltosLib.MISSING)
- link.printf("c a %d %d\n", plus, minus);
+ if (plus != AltosLib.MISSING && minus != AltosLib.MISSING) {
+ if (plus < 0)
+ plus = 65536 + plus;
+ if (minus < 0)
+ minus = 65536 + minus;
+ if (accel_zero_along != AltosLib.MISSING &&
+ accel_zero_across != AltosLib.MISSING &&
+ accel_zero_through != AltosLib.MISSING)
+ link.printf("c a %d %d %d %d %d\n",
+ plus, minus,
+ accel_zero_along,
+ accel_zero_across,
+ accel_zero_through);
+ else
+ link.printf("c a %d %d\n", plus, minus);
+ }
/* HAS_LOG */
if (flight_log_max != 0 && flight_log_max != AltosLib.MISSING)
read_link(link, "done");
break;
}
+ adjust_accel_cal();
}
}
}
public static double convert_accel(double counts, int imu_type) {
- return counts / counts_per_g(imu_type) * AltosConvert.gravity;
+ double cpg = counts_per_g(imu_type);
+ if (cpg == AltosLib.MISSING)
+ return AltosLib.MISSING;
+ return counts / cpg * AltosConvert.gravity;
}
public static final double GYRO_FULLSCALE_DEGREES_MPU = 2000.0;
}
public static double gyro_degrees_per_second(double counts, int imu_type) {
- return counts / counts_per_degree(imu_type);
+ double cpd = counts_per_degree(imu_type);
+ if (cpd == AltosLib.MISSING)
+ return AltosLib.MISSING;
+ return counts / cpd;
}
public static final int imu_axis_x = 0;
}
public static double convert_gauss(double counts, int imu_type, int imu_axis) {
- return counts / counts_per_gauss(imu_type, imu_axis);
+ double cpg = counts_per_gauss(imu_type, imu_axis);
+ if (cpg == AltosLib.MISSING)
+ return AltosLib.MISSING;
+ return counts / cpg;
}
public boolean parse_string(String line) {
}
}
+ private static boolean is_primary_accel(int imu_type) {
+ switch (imu_type) {
+ case imu_type_easytimer_v1:
+ return true;
+ default:
+ return false;
+ }
+ }
+
public static int mag_through_axis(int imu_type) {
return imu_axis_z;
}
if (imu != null) {
if (imu.gyro_x != AltosLib.MISSING) {
+ cal_data.set_gyro_zero(0, 0, 0);
listener.set_gyro(cal_data.gyro_roll(imu.gyro_roll(imu_type)),
cal_data.gyro_pitch(imu.gyro_pitch(imu_type)),
cal_data.gyro_yaw(imu.gyro_yaw(imu_type)));
listener.set_accel(cal_data.accel_along(imu.accel_along(imu_type)),
cal_data.accel_across(imu.accel_across(imu_type)),
cal_data.accel_through(imu.accel_through(imu_type)));
+ if (is_primary_accel(imu_type)) {
+ int accel = imu.accel_along(imu_type);
+ if (!cal_data.adxl375_inverted)
+ accel = -accel;
+ if (cal_data.pad_orientation == 1)
+ accel = -accel;
+ listener.set_acceleration(cal_data.acceleration(accel));
+ }
if (imu.mag_x != AltosLib.MISSING) {
listener.set_mag(cal_data.mag_along(imu.mag_along(imu_type)),
cal_data.mag_across(imu.mag_across(imu_type)),
static final int idle_mma655x = 8;
static final int idle_ms5607 = 9;
static final int idle_adxl375 = 10;
+ static final int idle_adxl375_easymotor_v2 = 11;
static final int idle_sensor_tm = 100;
static final int idle_sensor_metrum = 101;
static final int idle_sensor_tgps2 = 107;
static final int idle_sensor_tmini3 = 108;
static final int idle_sensor_easytimer1 = 109;
+ static final int idle_sensor_easymotor2 = 110;
public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, TimeoutException, AltosUnknownProduct {
for (int idler : idlers) {
AltosMma655x.provide_data(listener, link);
break;
case idle_adxl375:
- AltosAdxl375.provide_data(listener, link);
+ AltosAdxl375.provide_data(listener, link, false, AltosLib.MISSING);
+ break;
+ case idle_adxl375_easymotor_v2:
+ AltosAdxl375.provide_data(listener, link, true, AltosIMU.imu_type_easymotor_v2);
break;
case idle_ms5607:
AltosMs5607.provide_data(listener, link);
case idle_sensor_easytimer1:
AltosSensorEasyTimer1.provide_data(listener, link);
break;
+ case idle_sensor_easymotor2:
+ AltosSensorEasyMotor2.provide_data(listener, link);
+ break;
}
}
}
new AltosIdler("EasyTimer-v1",
AltosIdler.idle_imu_et_v1,
AltosIdler.idle_sensor_easytimer1),
+ new AltosIdler("EasyMotor-v2",
+ AltosIdler.idle_adxl375_easymotor_v2,
+ AltosIdler.idle_sensor_easymotor2),
};
AltosLink link;
boolean remote;
boolean close_on_exit;
boolean link_started;
- boolean have_npyro = false;
+ boolean has_pyro_info = false;
+ boolean has_standard = false;
int npyro;
AltosConfigData config_data;
npyro = config_data.npyro;
else
npyro = 0;
- have_npyro = true;
+ if (config_data != null)
+ has_standard = config_data.ignite_mode != AltosLib.MISSING;
+
+ has_pyro_info = true;
}
public int npyro() throws InterruptedException, TimeoutException {
- if (!have_npyro) {
+ if (!has_pyro_info) {
start_link();
get_npyro();
stop_link();
return npyro;
}
+ public boolean has_standard() throws InterruptedException, TimeoutException {
+ if (!has_pyro_info) {
+ start_link();
+ get_npyro();
+ stop_link();
+ }
+ return has_standard;
+ }
+
public HashMap<String,Integer> status() throws InterruptedException, TimeoutException {
HashMap<String,Integer> status = new HashMap<String,Integer>();
return ret;
}
+ public void synchronize(int timeout) throws InterruptedException {
+ printf("v\n");
+ for (;;) {
+ String line = get_reply(timeout);
+
+ if (line == null)
+ break;
+ if (line.startsWith("software-version"))
+ break;
+ if (line.startsWith("altos-loader"))
+ break;
+ }
+ }
+
public void to_loader() throws InterruptedException {
printf("X\n");
flush_output();
static final long auto_scroll_delay = 20 * 1000;
public AltosMapTransform transform;
- AltosLatLon centre;
+ public AltosLatLon centre;
public void reset() {
// nothing
centre(lat_lon);
}
- public AltosMapMark add_mark(double lat, double lon, int state) {
+ public AltosMapMark add_mark(double lat, double lon, int state, String label) {
AltosMapMark mark;
synchronized(marks) {
- mark = map_interface.new_mark(lat, lon, state);
+ mark = map_interface.new_mark(lat, lon, state, label);
if (mark != null)
marks.add(mark);
}
return mark;
}
+ public AltosMapMark add_mark(double lat, double lon, int state) {
+ return add_mark(lat, lon, state, null);
+ }
+
public void del_mark(AltosMapMark mark) {
marks.remove(mark);
}
public abstract AltosMapMark new_mark(double lat, double lon, int state);
+ public abstract AltosMapMark new_mark(double lat, double lon, int state, String label);
+
public abstract AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale);
public abstract int width();
public AltosLatLon lat_lon;
public int state;
+ public String label;
static public int stroke_width = 6;
public abstract void paint(AltosMapTransform t);
- public AltosMapMark (double lat, double lon, int state) {
+ public AltosMapMark (double lat, double lon, int state, String label) {
lat_lon = new AltosLatLon(lat, lon);
this.state = state;
+ this.label = label;
+ }
+
+ public AltosMapMark (double lat, double lon, int state) {
+ this(lat, lon, state, null);
+ }
+
+ public AltosMapMark () {
+ this(0, 0, 0);
}
}
double offset_x, offset_y;
+ int width, height;
+
public AltosLatLon lat_lon (AltosPointDouble point) {
double lat, lon;
double rads;
return screen(point(lat, lon));
}
+ /* Return first longitude value which ends up on-screen */
+ public double first_lon(double lon) {
+ /* Find a longitude left of the screen */
+ for (;;) {
+ double x = lon * scale_x - offset_x;
+ if (x < 0)
+ break;
+ lon -= 360.0;
+ }
+ /* Find the first longitude on the screen */
+ for (;;) {
+ double x = lon * scale_x - offset_x;
+ if (x >= 0)
+ break;
+ lon += 360.0;
+ }
+ return lon;
+ }
+
+ /* Return last longitude value which ends up on-screen */
+ public double last_lon(double lon) {
+ lon = first_lon(lon);
+
+ for(;;) {
+ double next_lon = lon + 360.0;
+ double next_x = next_lon * scale_x - offset_x;
+ if (next_x >= width)
+ break;
+ lon = next_lon;
+ }
+ return lon;
+ }
+
private boolean has_location;
public boolean has_location() {
scale_x = 256/360.0 * Math.pow(2, zoom);
scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+ this.width = width;
+ this.height = height;
+
AltosPointDouble centre_pt = point(centre_lat_lon);
has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
--- /dev/null
+/*
+ * Copyright © 2020 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; 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.
+ */
+
+package org.altusmetrum.altoslib_14;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosSensorEasyMotor2 {
+ int tick;
+ int v_batt;
+ int motor_pressure;
+
+ public AltosSensorEasyMotor2(AltosLink link) throws InterruptedException, TimeoutException {
+ String[] items = link.adc();
+ for (int i = 0; i < items.length;) {
+ if (items[i].equals("tick:")) {
+ tick = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("motor_pressure:")) {
+ motor_pressure = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ if (items[i].equals("batt:")) {
+ v_batt = Integer.parseInt(items[i+1]);
+ i += 2;
+ continue;
+ }
+ i++;
+ }
+ }
+
+ static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
+ try {
+ AltosSensorEasyMotor2 sensor_easymotor2 = new AltosSensorEasyMotor2(link);
+
+ listener.set_battery_voltage(AltosConvert.easy_mini_2_voltage(sensor_easymotor2.v_batt));
+ double pa = AltosConvert.easy_motor_2_motor_pressure(sensor_easymotor2.motor_pressure,
+ listener.cal_data().ground_motor_pressure);
+ listener.set_motor_pressure(pa);
+
+ } catch (TimeoutException te) {
+ }
+ }
+}
+
AltosSensorMetrum.java \
AltosSensorTGPS1.java \
AltosSensorTGPS2.java \
+ AltosSensorEasyMotor2.java \
AltosState.java \
AltosStateName.java \
AltosStringInputStream.java \
device = AltosDeviceUIDialog.show(in_owner, Altos.product_any);
remote = false;
- if (!device.matchProduct(Altos.product_altimeter))
+ if (device.matchProduct(Altos.product_basestation))
remote = true;
serial = device.getSerial();
ButtonGroup group;
Boolean opened;
+ boolean has_standard;
int npyro;
final static int timeout = 1 * 1000;
public void run () {
try {
ignite = new AltosIgnite(link,
- !device.matchProduct(Altos.product_altimeter));
+ device.matchProduct(Altos.product_basestation));
} catch (Exception e) {
send_exception(e);
}
reply = "status";
} else if (command.equals("get_npyro")) {
- reply = String.format("npyro %d", ignite.npyro());
+ reply = String.format("npyro %d %d", ignite.npyro(), ignite.has_standard() ? 1 : 0);
} else if (command.equals("quit")) {
ignite.close();
break;
} else if (reply.equals("fired")) {
fired();
} else if (reply.startsWith("npyro")) {
- npyro = Integer.parseInt(reply.substring(6));
+ String items[] = reply.split("\\s+");
+ npyro = Integer.parseInt(items[1]);
if (npyro == AltosLib.MISSING)
npyro = 0;
+ has_standard = Integer.parseInt(items[2]) != 0;
make_ui();
}
}
y++;
- igniters = new Igniter[2 + npyro];
+ int nstandard = 0;
+ if (has_standard)
+ nstandard = 2;
- igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++);
- igniters[1] = new Igniter(this, "Main", AltosIgnite.Main, y++);
+ igniters = new Igniter[nstandard + npyro];
+
+ if (has_standard) {
+ igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++);
+ igniters[1] = new Igniter(this, "Main", AltosIgnite.Main, y++);
+ }
for (int p = 0; p < npyro; p++) {
String name = String.format("%d", p);
String label = String.format("%c", 'A' + p);
- igniters[2+p] = new Igniter(this, label, name, y++);
+ igniters[nstandard+p] = new Igniter(this, label, name, y++);
}
c.gridx = 0;
#!/bin/bash
-#
-# Fix fonts. I don't know why the getting the
-# basename of the app set to . matters, but it does
-#
-case "$0" in
- /*)
- cd `dirname "$0"`
- ./`basename "$0"` "$@"
- exit $?
- ;;
-esac
-export FREETYPE_PROPERTIES=truetype:interpreter-version=35
##################################################################################
# #
# universalJavaApplicationStub #
# #
# @author Tobias Fischer #
# @url https://github.com/tofi86/universalJavaApplicationStub #
-# @date 2018-08-24 #
-# @version 3.0.4 #
+# @date 2021-02-21 #
+# @version 3.2.0 #
# #
##################################################################################
# #
# The MIT License (MIT) #
# #
-# Copyright (c) 2014-2018 Tobias Fischer #
+# Copyright (c) 2014-2021 Tobias Fischer #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy #
# of this software and associated documentation files (the "Software"), to deal #
# #
##################################################################################
-
+#
+# Fix fonts. I don't know why the getting the
+# basename of the app set to . matters, but it does
+#
+case "$0" in
+ /*)
+ cd `dirname "$0"`
+ ./`basename "$0"` "$@"
+ exit $?
+ ;;
+esac
+export FREETYPE_PROPERTIES=truetype:interpreter-version=35
# function 'stub_logger()'
#
JavaFolder="${AppleJavaFolder}"
ResourcesFolder="${AppleResourcesFolder}"
+ # set expandable variables
+ APP_ROOT="${AppPackageFolder}"
APP_PACKAGE="${AppPackageFolder}"
JAVAROOT="${AppleJavaFolder}"
USER_HOME="$HOME"
# AppPackageRoot is the standard WorkingDirectory when the script is started
WorkingDirectory="${AppPackageRoot}"
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
WorkingDirectory=$(eval echo "${WorkingDirectory}")
else
JVMClassPath=${JVMClassPath_RAW}
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
# read the JVM Options in either Array or String style
else
JVMDefaultOptions=${JVMDefaultOptions_RAW}
fi
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#84)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read StartOnMainThread and add as -XstartOnFirstThread
JVMStartOnMainThread=$(plist_get_java ':StartOnMainThread')
JVMDefaultOptions+=" -XstartOnFirstThread"
fi
- # read the JVM Arguments as an array and retain spaces
+ # read the JVM Arguments in either Array or String style (#76) and retain spaces
IFS=$'\t\n'
- MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ MainArgs_RAW=$(plist_get_java ':Arguments' | xargs)
+ if [[ $MainArgs_RAW == *Array* ]] ; then
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments' | tr -d '\n' | sed -E 's/Array \{ *(.*) *\}/\1/g' | sed 's/ */ /g')))
+ else
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ fi
unset IFS
# post processing of the array follows further below...
ResourcesFolder="${OracleResourcesFolder}"
WorkingDirectory="${OracleJavaFolder}"
+ # set expandable variables
APP_ROOT="${AppPackageFolder}"
+ APP_PACKAGE="${AppPackageFolder}"
+ JAVAROOT="${OracleJavaFolder}"
+ USER_HOME="$HOME"
# read the MainClass name
JVMMainClass="$(plist_get ':JVMMainClassName')"
JVMClassPath_RAW=$(plist_get ':JVMClassPath')
if [[ $JVMClassPath_RAW == *Array* ]] ; then
JVMClassPath=.$(plist_get ':JVMClassPath' | grep " " | sed 's/^ */:/g' | tr -d '\n' | xargs)
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
elif [[ ! -z ${JVMClassPath_RAW} ]] ; then
JVMClassPath=${JVMClassPath_RAW}
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
else
# Do NOT expand the default 'AppName.app/Contents/Java/*' classpath (#42)
fi
- # read the JVM Default Options
+ # read the JVM Default Options by parsing the :JVMDefaultOptions <dict>
+ # and pulling all <string> values starting with a dash (-)
JVMDefaultOptions=$(plist_get ':JVMDefaultOptions' | grep -o " \-.*" | tr -d '\n' | xargs)
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#99)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read the Main Arguments from JVMArguments key as an array and retain spaces (see #46 for naming details)
IFS=$'\t\n'
fi
+# (#75) check for undefined icons or icon names without .icns extension and prepare
+# an osascript statement for those cases when the icon can be shown in the dialog
+DialogWithIcon=""
+if [ ! -z ${CFBundleIconFile} ]; then
+ if [[ ${CFBundleIconFile} == *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}" ]] ; then
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ elif [[ ${CFBundleIconFile} != *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}.icns" ]] ; then
+ CFBundleIconFile+=".icns"
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ fi
+fi
+
# JVMVersion: post processing and optional splitting
if [[ ${JVMVersion} == *";"* ]]; then
stub_logger "[JavaRequirement] JVM minimum version: ${JVMVersion}"
stub_logger "[JavaRequirement] JVM maximum version: ${JVMMaxVersion}"
-# MainArgs: replace occurences of $APP_ROOT with its content
+# MainArgs: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
MainArgsArr=()
for i in "${MainArgs[@]}"
do
MainArgsArr+=("$(eval echo "$i")")
done
-# JVMOptions: replace occurences of $APP_ROOT with its content
+# JVMOptions: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMOptionsArr=()
for i in "${JVMOptions[@]}"
do
# internationalized messages
############################################
-LANG=$(defaults read -g AppleLocale)
-stub_logger "[Language] $LANG"
+# supported languages / available translations
+stubLanguages="^(fr|de|zh|es|en)-"
+
+# read user preferred languages as defined in macOS System Preferences (#101)
+stub_logger '[LanguageSearch] Checking preferred languages in macOS System Preferences...'
+appleLanguages=($(defaults read -g AppleLanguages | grep '\s"' | tr -d ',' | xargs))
+stub_logger "[LanguageSearch] ... found [${appleLanguages[*]}]"
+
+language=""
+for i in "${appleLanguages[@]}"
+do
+ langValue="${i%-*}"
+ if [[ "$i" =~ $stubLanguages ]]; then
+ stub_logger "[LanguageSearch] ... selected '$i' ('$langValue') as the default language for the launcher stub"
+ language=${langValue}
+ break
+ fi
+done
+if [ -z "${language}" ]; then
+ language="en"
+ stub_logger "[LanguageSearch] ... selected fallback 'en' as the default language for the launcher stub"
+fi
+stub_logger "[Language] $language"
-# French localization
-if [[ $LANG == fr* ]] ; then
+
+case "${language}" in
+# French
+fr)
MSG_ERROR_LAUNCHING="ERREUR au lancement de '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' n'est pas spécifié.\nL'application Java ne peut pas être lancée."
- MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
+ MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version de Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
MSG_NO_SUITABLE_JAVA="La version de Java installée sur votre système ne convient pas.\nCe programme nécessite Java %s"
MSG_JAVA_VERSION_OR_LATER="ou ultérieur"
MSG_JAVA_VERSION_LATEST="(dernière mise à jour)"
MSG_NO_SUITABLE_JAVA_CHECK="Merci de bien vouloir installer la version de Java requise."
MSG_INSTALL_JAVA="Java doit être installé sur votre système.\nRendez-vous sur java.com et suivez les instructions d'installation..."
MSG_LATER="Plus tard"
- MSG_VISIT_JAVA_DOT_COM="Visiter java.com"
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
-# German localization
-elif [[ $LANG == de* ]] ; then
+# German
+de)
MSG_ERROR_LAUNCHING="FEHLER beim Starten von '${CFBundleName}'."
MSG_MISSING_MAINCLASS="Die 'MainClass' ist nicht spezifiziert!\nDie Java-Anwendung kann nicht gestartet werden!"
MSG_JVMVERSION_REQ_INVALID="Die Syntax der angeforderten Java-Version ist ungültig: %s\nBitte kontaktieren Sie den Entwickler der App."
MSG_NO_SUITABLE_JAVA_CHECK="Stellen Sie sicher, dass die angeforderte Java-Version installiert ist."
MSG_INSTALL_JAVA="Auf Ihrem System muss die 'Java'-Software installiert sein.\nBesuchen Sie java.com für weitere Installationshinweise."
MSG_LATER="Später"
- MSG_VISIT_JAVA_DOT_COM="java.com öffnen"
+ MSG_VISIT_JAVA_DOT_COM="Java von Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java von AdoptOpenJDK"
+ ;;
-# Simplifyed Chinese localization
-elif [[ $LANG == zh* ]] ; then
+# Simplified Chinese
+zh)
MSG_ERROR_LAUNCHING="无法启动 '${CFBundleName}'."
MSG_MISSING_MAINCLASS="没有指定 'MainClass'!\nJava程序无法启动!"
MSG_JVMVERSION_REQ_INVALID="Java版本参数语法错误: %s\n请联系该应用的开发者。"
MSG_NO_SUITABLE_JAVA_CHECK="请确保系统中安装了所需的Java版本"
MSG_INSTALL_JAVA="你需要在Mac中安装Java运行环境!\n访问 java.com 了解如何安装。"
MSG_LATER="稍后"
- MSG_VISIT_JAVA_DOT_COM="访问 java.com"
-
-# English default localization
-else
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+
+# Spanish
+es)
+ MSG_ERROR_LAUNCHING="ERROR iniciando '${CFBundleName}'."
+ MSG_MISSING_MAINCLASS="¡'MainClass' no especificada!\n¡La aplicación Java no puede iniciarse!"
+ MSG_JVMVERSION_REQ_INVALID="La sintaxis de la versión Java requerida no es válida: %s\nPor favor, contacte con el desarrollador de la aplicación."
+ MSG_NO_SUITABLE_JAVA="¡No se encontró una versión de Java adecuada en su sistema!\nEste programa requiere Java %s"
+ MSG_JAVA_VERSION_OR_LATER="o posterior"
+ MSG_JAVA_VERSION_LATEST="(ultima actualización)"
+ MSG_JAVA_VERSION_MAX="superior a %s"
+ MSG_NO_SUITABLE_JAVA_CHECK="Asegúrese de instalar la versión Java requerida."
+ MSG_INSTALL_JAVA="¡Necesita tener JAVA instalado en su Mac!\nVisite java.com para consultar las instrucciones para su instalación..."
+ MSG_LATER="Más tarde"
+ MSG_VISIT_JAVA_DOT_COM="Java de Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java de AdoptOpenJDK"
+ ;;
+
+# English | default
+en|*)
MSG_ERROR_LAUNCHING="ERROR launching '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' isn't specified!\nJava application cannot be started!"
MSG_JVMVERSION_REQ_INVALID="The syntax of the required Java version is invalid: %s\nPlease contact the App developer."
MSG_NO_SUITABLE_JAVA_CHECK="Make sure you install the required Java version."
MSG_INSTALL_JAVA="You need to have JAVA installed on your Mac!\nVisit java.com for installation instructions..."
MSG_LATER="Later"
- MSG_VISIT_JAVA_DOT_COM="Visit java.com"
-fi
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+esac
################################################################################
function is_valid_requirement_pattern() {
local java_req=$1
- java8pattern='1\.[4-8](\.0)?(\.0_[0-9]+)?[*+]?'
+ java8pattern='1\.[4-8](\.[0-9]+)?(\.0_[0-9]+)?[*+]?'
java9pattern='(9|1[0-9])(-ea|[*+]|(\.[0-9]+){1,2}[*+]?)?'
# test matches either old Java versioning scheme (up to 1.8) or new scheme (starting with 9)
if [[ ${java_req} =~ ^(${java8pattern}|${java9pattern})$ ]]; then
if [[ $JAVA_HOME == /* ]] ; then
# if "$JAVA_HOME" starts with a Slash it's an absolute path
JAVACMD="$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as absolute path to the executable '$JAVACMD'"
else
# otherwise it's a relative path to "$AppPackageFolder"
JAVACMD="$AppPackageFolder/$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as relative path inside the App bundle to the executable '$JAVACMD'"
fi
JAVACMD_version=$(get_comparable_java_version $(get_java_version_from_cmd "${JAVACMD}"))
else
- stub_logger "[JavaSearch] ... didn't found JAVA_HOME"
+ stub_logger "[JavaSearch] ... haven't found JAVA_HOME"
fi
# check for any other or a specific Java version
# also if $JAVA_HOME exists but isn't executable
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
- stub_logger "[JavaSearch] Checking for JavaVirtualMachines on the system ..."
+
+ # add a warning in the syslog if JAVA_HOME is not executable or not found (#100)
+ if [ -n "$JAVA_HOME" ] ; then
+ stub_logger "[JavaSearch] ... but no 'java' executable was found at the JAVA_HOME location!"
+ fi
+
+ stub_logger "[JavaSearch] Searching for JavaVirtualMachines on the system ..."
# reset variables
JAVACMD=""
JAVACMD_version=""
# log exit cause
stub_logger "[EXIT 4] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 4
fi
# log exit cause
stub_logger "[EXIT 5] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 5
fi
# find installed JavaVirtualMachines (JDK + JRE)
allJVMs=()
- # read JDK's from '/usr/libexec/java_home -V' command
- while read -r line; do
- version=$(echo $line | awk -F $',' '{print $1;}')
- path=$(echo $line | awk -F $'" ' '{print $2;}')
- path+="/bin/java"
- allJVMs+=("$version:$path")
- done < <(/usr/libexec/java_home -V 2>&1 | grep '^[[:space:]]')
- # unset while loop variables
- unset version path
+
+ # read JDK's from '/usr/libexec/java_home --xml' command with PlistBuddy and a custom Dict iterator
+ # idea: https://stackoverflow.com/a/14085460/1128689 and https://scriptingosx.com/2018/07/parsing-dscl-output-in-scripts/
+ javaXml=$(/usr/libexec/java_home --xml)
+ javaCounter=$(/usr/libexec/PlistBuddy -c "Print" /dev/stdin <<< $javaXml | grep "Dict" | wc -l | tr -d ' ')
+
+ # iterate over all Dict entries
+ # but only if there are any JVMs at all (#93)
+ if [ "$javaCounter" -gt "0" ] ; then
+ for idx in $(seq 0 $((javaCounter - 1)))
+ do
+ version=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMVersion" /dev/stdin <<< $javaXml)
+ path=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMHomePath" /dev/stdin <<< $javaXml)
+ path+="/bin/java"
+ allJVMs+=("$version:$path")
+ done
+ # unset for loop variables
+ unset version path
+ fi
+
+ # add SDKMAN! java versions (#95)
+ if [ -d ~/.sdkman/candidates/java/ ] ; then
+ for sdkjdk in ~/.sdkman/candidates/java/*/
+ do
+ if [[ ${sdkjdk} =~ /current/$ ]] ; then
+ continue
+ fi
+
+ sdkjdkcmd="${sdkjdk}bin/java"
+ version=$(get_java_version_from_cmd "${sdkjdkcmd}")
+ allJVMs+=("$version:$sdkjdkcmd")
+ done
+ # unset for loop variables
+ unset version
+ fi
# add Apple JRE if available
if [ -x "${apple_jre_plugin}" ] ; then
# determine JVMs matching the min/max version requirement
+
+ stub_logger "[JavaSearch] Filtering the result list for JVMs matching the min/max version requirement ..."
+
minC=$(get_comparable_java_version ${JVMVersion})
maxC=$(get_comparable_java_version ${JVMMaxVersion})
matchingJVMs=()
# debug output
for i in "${matchingJVMs[@]}"
do
- stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+ stub_logger "[JavaSearch] ... matches all requirements: $i"
done
stub_logger "[JavaCommand] '$JAVACMD'"
stub_logger "[JavaVersion] $(get_java_version_from_cmd "${JAVACMD}")${JAVACMD_version:+ / $JAVACMD_version}"
+# Make sure tabbing mode is disabled for the selected java version
+
+CFBundleIdentifier=net.java.openjdk.$(get_java_version_from_cmd "${JAVACMD}").java
+if [ x$(defaults read ${CFBundleIdentifier} AppleWindowTabbingMode) != "xnever" ]; then
+ defaults write ${CFBundleIdentifier} AppleWindowTabbingMode never
+fi
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
stub_logger "[EXIT 3] ${MSG_NO_SUITABLE_JAVA_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 3
# log exit cause
stub_logger "[EXIT 1] ${MSG_ERROR_LAUNCHING}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 1
fi
# log exit cause
stub_logger "[EXIT 2] ${MSG_MISSING_MAINCLASS}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 2
fi
# - main class
# - main class arguments
# - passthrough arguments from Terminal or Drag'n'Drop to Finder icon
-stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" -splash:\"${ResourcesFolder}/${JVMSplashFile}\" -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
+stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" ${JVMSplashFile:+ -splash:\"${ResourcesFolder}/${JVMSplashFile}\"} -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
exec "${JAVACMD}" \
-Djava.library.path="${AppleJavaFolder}" \
-cp "${JVMClassPath}" \
- -splash:"${ResourcesFolder}/${JVMSplashFile}" \
+ ${JVMSplashFile:+ -splash:"${ResourcesFolder}/${JVMSplashFile}"} \
-Xdock:icon="${ResourcesFolder}/${CFBundleIconFile}" \
-Xdock:name="${CFBundleName}" \
${JVMOptionsArr:+"${JVMOptionsArr[@]}" }\
FIRMWARE_EMEGA_2_0=$(top_srcdir)/src/easymega-v2.0/easymega-v2.0-$(VERSION).ihx
FIRMWARE_EMEGA=$(FIRMWARE_EMEGA_1_0) $(FIRMWARE_EMEGA_2_0)
+FIRMWARE_EMOTOR_2=$(top_srcdir)/src/easymotor-v2/easymotor-v2-$(VERSION).ihx
+FIRMWARE_EMOTOR=$(FIRMWARE_EMOTOR_2)
+
FIRMWARE_ETIMER_1=$(top_srcdir)/src/easytimer-v1/easytimer-v1-$(VERSION).ihx
FIRMWARE_ETIMER=$(FIRMWARE_ETIMER_1)
FIRMWARE_TFIRE8_2_0=$(top_srcdir)/src/telefireeight-v2.0/telefireeight-v2.0-$(VERSION).ihx
FIRMWARE_TFIRE8=$(FIRMWARE_TFIRE8_1_0) $(FIRMWARE_TFIRE8_2_0)
-FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TELEMINI) $(FIRMWARE_TD) $(FIRMWARE_TBT) $(FIRMWARE_TMEGA) $(FIRMWARE_EMINI) $(FIRMWARE_TGPS) $(FIRMWARE_EMEGA) $(FIRMWARE_ETIMER) $(FIRMWARE_TLCO) $(FIRMWARE_TFIRE8)
+FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TELEMINI) $(FIRMWARE_TD) $(FIRMWARE_TBT) $(FIRMWARE_TMEGA) $(FIRMWARE_EMINI) $(FIRMWARE_TGPS) $(FIRMWARE_EMEGA) $(FIRMWARE_EMOTOR) $(FIRMWARE_ETIMER) $(FIRMWARE_TLCO) $(FIRMWARE_TFIRE8)
ALTUSMETRUM_DOC=$(top_srcdir)/doc/altusmetrum.pdf
ALTOS_DOC=$(top_srcdir)/doc/altos.pdf
File "../src/easymini-v2.0/easymini-v2.0-${VERSION}.ihx"
File "../src/easymega-v1.0/easymega-v1.0-${VERSION}.ihx"
File "../src/easymega-v2.0/easymega-v2.0-${VERSION}.ihx"
+ File "../src/easymotor-v2/easymotor-v2-${VERSION}.ihx"
File "../src/easytimer-v1/easytimer-v1-${VERSION}.ihx"
File "../src/telelco-v2.0/telelco-v2.0-${VERSION}.ihx"
File "../src/telefireeight-v1.0/telefireeight-v1.0-${VERSION}.ihx"
;;
*)
SUDO_ASKPASS="${dir}/ask-pass" sudo -A "$0" "$@"
+ case $? in
+ 0)
+ ;;
+ *)
+ osascript -e 'display dialog "Installation failed. Incorrect password?" buttons {"OK"} default button 1 with title "Installation Status"' > /dev/null
+ ;;
+ esac
exit 0
;;
esac
;;
esac
done
+open "${LIBRARY}"
osascript -e 'display dialog "Installation of'"${INSTALLED}"' complete" with title "Installation Complete" buttons {"OK"} default button 1' >/dev/null
"TeleShield"
};
+ private static final String[] log_erased_devices = {
+ "TeleGPS"
+ };
+
private boolean is_pair_programmed() {
if (file != null) {
return false;
}
+ private boolean is_log_erased() {
+ if (device != null) {
+ String name = device.toString();
+ for (int i = 0; i < log_erased_devices.length; i++) {
+ if (name.startsWith(log_erased_devices[i]))
+ return true;
+ }
+ }
+ return false;
+ }
+
public void actionPerformed(ActionEvent e) {
if (e.getSource() == cancel) {
if (programmer != null)
flash_task flasher;
+ boolean erase_answer;
class open_task implements Runnable {
AltosDevice device;
Thread t;
open_dialog dialog;
+ AltosLink link;
public void do_exception(final Exception e) {
+ if (link != null) {
+ try {
+ link.close();
+ } catch (Exception ex) {}
+ }
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
});
}
- public void do_failure() {
- SwingUtilities.invokeLater(
- new Runnable() {
- public void run() {
- try { dialog.open_failure(); } catch (Exception ex) { }
- }
- });
- }
-
- public void do_cancel() {
+ public boolean do_notify_erase(final AltosConfigData config_data) {
+ erase_answer = false;
+ final Semaphore erase_answer_done = new Semaphore(0);
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
- try { dialog.open_cancel(); } catch (Exception ex) { }
+ int ret = JOptionPane.showConfirmDialog(dialog.owner,
+ String.format("Updating %s from firmware %s will erase stored data, continue?",
+ config_data.product,
+ config_data.version),
+ "Erase Stored Data?",
+ JOptionPane.YES_NO_OPTION);
+ erase_answer = ret == JOptionPane.YES_OPTION;
+ erase_answer_done.release();
}
});
+ try {
+ erase_answer_done.acquire();
+ } catch (Exception ex) {
+ return false;
+ }
+ return erase_answer;
}
public void run () {
+ link = null;
try {
- AltosLink link = null;
boolean new_device = false;
for (;;) {
throw new IOException(String.format("%s: open failed",
device.toShortString()));
+ System.out.printf("Checking device ready\n");
+
/* See if the link is usable already */
if (is_pair_programmed() || link.is_loader()) {
System.out.printf("Device ready for use\n");
return;
}
+ System.out.printf("Checking log erased\n");
+
+ if (is_log_erased()) {
+ System.out.printf("Fetching config data\n");
+ AltosConfigData config_data = link.config_data();
+ System.out.printf("version %s\n", config_data.version);
+ /* Completely erase TeleGPS flash when firmware is old */
+ if (config_data.compare_version("1.9.7") < 0)
+ {
+ if (!do_notify_erase(config_data))
+ throw new IOException(String.format("%s: not erasing log",
+ device.toShortString()));
+ System.out.printf("Erasing log\n");
+ link.printf("Z DoIt\n");
+ link.synchronize(120 * 1000);
+ }
+ }
+
java.util.List<AltosDevice> prev_devices =
AltosUSBDevice.list(AltosLib.product_altusmetrum);
do_exception(fe);
} catch (IOException ie) {
do_exception (ie);
+ } catch (TimeoutException te) {
+ do_exception (te);
} catch (InterruptedException ie) {
+ do_exception (ie);
}
}
done = true;
}
- public void open_failure() {
- System.out.printf("open_failure\n");
- setVisible(false);
- done = true;
- }
-
- public void open_cancel() {
- System.out.printf("open_cancel\n");
- setVisible(false);
- done = true;
- }
-
public AltosLink do_open(open_task open) throws InterruptedException {
this.open = open;
setVisible(true);
info_add_row(3, "Accel along", "%8.1f m/s²", state.accel_along());
info_add_row(3, "Accel across", "%8.1f m/s²", state.accel_across());
info_add_row(3, "Accel through", "%8.1f m/s²", state.accel_through());
+ }
+ if (state != null && state.gyro_roll() != AltosLib.MISSING) {
info_add_row(3, "Gyro roll", "%8.1f °/s", state.gyro_roll());
info_add_row(3, "Gyro pitch", "%8.1f °/s", state.gyro_pitch());
info_add_row(3, "Gyro yaw", "%8.1f °/s", state.gyro_yaw());
- if (state.mag_along() != AltosLib.MISSING) {
- /* Report mag in nanoteslas (1 G = 100000 nT (or γ)) */
- info_add_row(3, "Mag along", "%8.1f µT", state.mag_along() * 100.0);
- info_add_row(3, "Mag across", "%8.1f µT", state.mag_across() * 100.0);
- info_add_row(3, "Mag Through", "%8.1f µT", state.mag_through() * 100.0);
- info_add_row(3, "Mag Bearing", "%8.1f°", Math.atan2(state.mag_across(), state.mag_through()) * 180/Math.PI);
- }
+ }
+ if (state != null && state.mag_along() != AltosLib.MISSING) {
+ /* Report mag in nanoteslas (1 G = 100000 nT (or γ)) */
+ info_add_row(3, "Mag along", "%8.1f µT", state.mag_along() * 100.0);
+ info_add_row(3, "Mag across", "%8.1f µT", state.mag_across() * 100.0);
+ info_add_row(3, "Mag Through", "%8.1f µT", state.mag_through() * 100.0);
+ info_add_row(3, "Mag Bearing", "%8.1f°", Math.atan2(state.mag_across(), state.mag_through()) * 180/Math.PI);
}
if (state != null && state.igniter_voltage != null) {
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 < AltosUIMap.stateColors.length)
- g.setColor(AltosUIMap.stateColors[state]);
- else
- g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+ double lat = lat_lon.lat;
+ double lon;
+ double first_lon = t.first_lon(lat_lon.lon);
+ double last_lon = t.last_lon(lat_lon.lon);
+ for (lon = first_lon; lon <= last_lon; lon += 360.0) {
+ 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 < 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);
+
+ if (label != null) {
+ Rectangle2D bounds;
+ bounds = line_font.getStringBounds(label, g.getFontRenderContext());
+ float x = (float) pt.x;
+ float y = (float) pt.y + (float) bounds.getHeight() / 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(label, x + dx, y + dy);
+ if (0 <= state && state < AltosUIMap.stateColors.length)
+ g.setColor(AltosUIMap.stateColors[state]);
+ else
+ g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
+ g.drawString(label, x, y);
+ }
+ }
+ }
- 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, String label) {
+ super(lat, lon, state, label);
}
MapMark(double lat, double lon, int state) {
return new MapMark(lat, lon, state);
}
+ public AltosMapMark new_mark(double lat, double lon, int state, String label) {
+ return new MapMark(lat, lon, state, label);
+ }
+
public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
}
map.add_mark(lat, lon, status);
}
+ public void add_mark(double lat, double lon, int status, String label) {
+ map.add_mark(lat, lon, status, label);
+ }
+
public void clear_marks() {
map.clear_marks();
}
JProgressBar pbar;
JLabel site_list_label;
+ java.util.List<AltosLaunchSite> sites;
JComboBox<AltosLaunchSite> site_list;
JToggleButton load_button;
return 1 << AltosMap.maptype_hybrid;
}
+ void add_mark(double lat, double lon, int state, String label) {
+ map.add_mark(lat, lon, state, label);
+ }
+
+ void reset_marks() {
+ map.clear_marks();
+ AltosLatLon centre = null;
+ String centre_name = null;
+ if (map != null && map.map != null)
+ centre = map.map.centre;
+ for (AltosLaunchSite site : sites) {
+ if (centre != null && centre.lat == site.latitude && centre.lon == site.longitude)
+ centre_name = site.name;
+ else
+ add_mark(site.latitude, site.longitude, AltosLib.ao_flight_main, site.name);
+ }
+ if (centre != null)
+ add_mark(centre.lat, centre.lon, AltosLib.ao_flight_boost, centre_name);
+ }
+
void center_map(double latitude, double longitude) {
map.map.centre(new AltosLatLon(latitude, longitude));
- map.clear_marks();
- map.add_mark(latitude, longitude, AltosLib.ao_flight_boost);
+ reset_marks();
}
void center_map() {
}
public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
+ this.sites = sites;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int i = 1;
site_list.insertItemAt(site, i);
i++;
}
+ reset_marks();
}
});
}
echo -e '\e[34m'Testing $product $serial $dev'\e[39m'
echo ""
+ sleep 0.25
+
./test-igniters-nowait "$dev" drogue main
echo ""
echo -e '\e[31m'"$PRODUCT-$VERSION serial $serial failed"'\e[39m'
exit 1
fi
-
echo ""
echo -e '\e[32m'"$PRODUCT-v$VERSION" serial "$serial" is ready to ship'\e[39m'
if [ $result -ne 2 ]; then
exit $result
fi
- echo 'No device, sleeping...'
- sleep 1
+ sleep 0.25
done
--- /dev/null
+#!/bin/sh
+
+VERSION=2
+PRODUCT=EasyMotor
+BASE=`echo $PRODUCT | tr 'A-Z' 'a-z'`
+
+echo "$PRODUCT-v$VERSION Test Program"
+echo "Copyright 2021 by Bdale Garbee. Released under GPL v3"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION powered from USB"
+echo
+
+ret=1
+ao-list | while read product serial dev; do
+ case "$product" in
+ "$PRODUCT-v$VERSION")
+
+ echo "Testing $product $serial $dev"
+ echo ""
+
+ FLASHSIZE=8388608
+
+ echo "Testing flash"
+ ../ao-tools/ao-test-flash/ao-test-flash --tty="$dev" "$FLASHSIZE"
+
+ case $? in
+ 0)
+ ;;
+ *)
+ echo "failed"
+ exit 1
+ esac
+ echo""
+
+ echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+ ret=0
+ ;;
+ esac
+done
--- /dev/null
+#!/bin/sh
+
+PRODUCT=EasyMotor
+VERSION=2
+REPO=~/altusmetrumllc/Binaries
+
+if [ -x /usr/bin/dfu-util ]; then
+ DFU_UTIL=/usr/bin/dfu-util
+else
+ echo "Can't find dfu-util! Aborting."
+ exit 1
+fi
+
+if [ -x /usr/bin/ao-usbload ]; then
+ USBLOAD=/usr/bin/ao-usbload
+else
+ echo "Can't find ao-usbload! Aborting."
+ exit 1
+fi
+
+echo "$PRODUCT v$VERSION Turn-On and Calibration Program"
+echo "Copyright 2021 by Bdale Garbee. Released under GPL v3"
+echo
+echo "Expectations:"
+echo "\t$PRODUCT v$VERSION"
+echo "\t\twith USB cable attached"
+echo
+
+case $# in
+ 1)
+ SERIAL="$1"
+ echo "$PRODUCT-$VERSION serial number: $SERIAL"
+ ;;
+ 0)
+ echo -n "$PRODUCT-$VERSION serial number: "
+ read SERIAL
+ ;;
+ *)
+ echo "Usage: $0 <serial-number>" 1>&2
+ exit 1;
+ ;;
+esac
+
+
+echo $DFU_UTIL
+
+$DFU_UTIL -v -v -R -a 0 -s 0x08000000:leave -D $REPO/loaders/easymotor-v$VERSION*.bin
+
+sleep 3
+
+$USBLOAD --serial=$SERIAL $REPO/easymotor-v$VERSION*.elf || exit 1
+
+sleep 5
+
+dev=`ao-list | awk '/'"$PRODUCT"'-v'"$VERSION"'/ { print $3; exit(0); }'`
+
+case "$dev" in
+/dev/tty*)
+ echo "$PRODUCT found on $dev"
+ ;;
+*)
+ echo 'No '"$PRODUCT"'-v'"$VERSION"' found'
+ exit 1
+ ;;
+esac
+
+failed=1
+while [ $failed = 1 ]; do
+ ../ao-tools/ao-cal-accel/ao-cal-accel $dev
+ failed=$?
+done
+
+./test-easymotor
+
+exit $?
#include <unistd.h>
#include <getopt.h>
#include <string.h>
+#include <stdbool.h>
#include <ctype.h>
#include "cc-usb.h"
#include "cc.h"
}
}
+static const uint8_t test_data[] = {
+ 0xfc, 0xfd, 0xfe, 0xff, 0xf8, 0xf9, 0xfa, 0xfb, 0x40, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x17, 0x08,
+};
+
+static bool test_failed;
+static int test_pos;
+
+static void
+check_test(uint8_t b)
+{
+ if (test_pos >= sizeof (test_data) || test_data[test_pos++] != b)
+ test_failed = true;
+}
+
static uint8_t
get_hexc(struct cc_usb *cc)
{
int h = (a << 4) + b;
file_crc = log_crc(file_crc, h);
+ check_test(h);
return h;
}
current_crc = swap16(~file_crc & 0xffff);
crc = get_16(cc); /* crc */
putchar ('\n');
- if (crc == current_crc)
- printf("CRC valid\n");
+ if (crc == current_crc) {
+ if (!test_failed)
+ printf("\033[32mValid MicroTest Data\033[39m\n");
+ else
+ printf("CRC valid\n");
+ }
else
printf("CRC invalid\n");
cc_usb_close(cc);
file="$1"
echo "program $file verify reset" > $cmds
openocd \
- -f interface/stlink-v2.cfg \
- -f target/stm32f0x_stlink.cfg \
+ -f interface/stlink.cfg \
+ -f target/stm32f0x.cfg \
-f $cmds \
-c shutdown
dnl Process this file with autoconf to create configure.
AC_PREREQ(2.57)
-AC_INIT([altos], 1.9.6)
-ANDROID_VERSION=27
+AC_INIT([altos], 1.9.7)
+ANDROID_VERSION=29
AC_CONFIG_SRCDIR([src/kernel/ao.h])
AM_INIT_AUTOMAKE([foreign dist-bzip2])
AM_MAINTAINER_MODE
-RELEASE_DATE=2020-09-29
+RELEASE_DATE=2021-06-08
AC_SUBST(RELEASE_DATE)
DOC_DATE=`LC_ALL=C date -d $RELEASE_DATE +'%d %b %Y'`
endif
RELNOTES_INC=\
+ release-notes-1.9.7.inc \
release-notes-1.9.6.inc \
release-notes-1.9.5.inc \
release-notes-1.9.4.inc \
ADOC_FILES=$(TXT_FILES:.txt=.adoc) $(INC_FILES:.inc=.adoc)
-TELELAUNCH_TXT_FILES=altusmetrum.txt
+MOTORTEST_TXT_FILES=motortest.txt
+
+MOTORTEST_INC_FILES=\
+ motortest-configuration.inc \
+ motortest-installation.inc \
+ motortest-intro.inc \
+ motortest-operation.inc
+
+MOTORTEST_ADOC_FILES=$(MOTORTEST_TXT_FILES:.txt=.adoc) $(MOTORTEST_INC_FILES:.inc=.adoc)
+
+TELELAUNCH_TXT_FILES=telelaunch.txt
TELELAUNCH_INC_FILES=\
header.inc \
AM_HTML=am.html
-PUBLISH_HTML=altusmetrum.html micropeak.html telegps.html easymini.html telelaunch.html $(ONEFILE_HTML_FILES)
+PUBLISH_HTML=altusmetrum.html micropeak.html telegps.html easymini.html motortest.html telelaunch.html $(ONEFILE_HTML_FILES)
HTML=$(PUBLISH_HTML) $(RELNOTES_HTML)
if ASCIIDOCTOR_PDF
-PDF=altusmetrum.pdf micropeak.pdf telegps.pdf easymini.pdf telelaunch.pdf $(ONEFILE_PDF_FILES) \
+PDF=altusmetrum.pdf micropeak.pdf telegps.pdf easymini.pdf motortest.pdf telelaunch.pdf $(ONEFILE_PDF_FILES) \
$(OUTLINE_PDF_FILES)
endif
altusmetrum.pdf altusmetrum.html: $(ADOC_FILES) $(IMAGES)
+motortest.pdf motortest.html: $(MOTORTEST_ADOC_FILES) $(IMAGES)
+
telelaunch.pdf telelaunch.html: $(TELELAUNCH_ADOC_FILES) $(IMAGES)
telegps.html telegps.pdf: $(TELEGPS_ADOC_FILES) $(IMAGES)
scp -p $(ICONS) keithp.com:~keithp/public_html/altos/images/icons
clean:
- rm -f am.html $(HTML) $(PDF) $(ADOC_FILES) $(TELEGPS_ADOC_FILES) $(MICROPEAK_ADOC_FILES) $(TELELAUNCH_ADOC_FILES)
+ rm -f am.html $(HTML) $(PDF) $(ADOC_FILES) $(TELEGPS_ADOC_FILES) $(MICROPEAK_ADOC_FILES) $(TELELAUNCH_ADOC_FILES) $(MOTORTEST_ADOC_FILES)
distclean: clean
rm -f $(HTML) $(PDF)
=== Connecting to TeleBT over Bluetooth™
+ Note that when turning TeleBT on, you may see a brief LED
+ flash, but there will be no "activity" indicated until you
+ pair with the device from AltosDroid.
+
Press the Android 'Menu' button or soft-key to see the
configuration options available. Select the 'Connect a
device' option and then the 'Scan for devices' entry
left:
content: '{page-number}'
right:
- content: '© 2020 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
+ content: '© 2021 Bdale Garbee and Keith Packard. Creative Commons ShareAlike 3.0 License'
verso:
left:
content: $footer_recto_right_content
:revdate: 1 Jan 1970
:icons:
:icontype: svg
-:copyright: Bdale Garbee and Keith Packard 2020
+:copyright: Bdale Garbee and Keith Packard 2021
:doctype: book
:numbered:
:stylesheet: am.css
:easymega: 1
:telegps: 1
:easytimer: 1
+:easymotor: 1
:application: AltosUI
:pdf-stylesdir: .
:pdf-style: altusmetrum
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.7.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.6.adoc[]
:title-logo-image: image:../themes/background.png[]
:revnumber: v{version}
:revdate: 01 Jan 1970
-:copyright: Bdale Garbee and Keith Packard 2020
+:copyright: Bdale Garbee and Keith Packard 2021
:doctype: book
:numbered:
:toc:
[license]
== License
-Copyright © 2018 Bdale Garbee and Keith Packard
+Copyright © 2021 Bdale Garbee and Keith Packard
This document is released under the terms of the link:http://creativecommons.org/licenses/by-sa/3.0/[Creative Commons ShareAlike 3.0 License]
Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
:revnumber: v{version}
:revdate: 01 Jan 1970
-:copyright: Bdale Garbee and Keith Packard 2020
+:copyright: Bdale Garbee and Keith Packard 2021
:stylesheet: am.css
:linkcss:
:toc:
--- /dev/null
+== Configuration
+
+ There is very little that must be configured to make EasyMotor work.
+ In fact, the default configuration from the factory is typically
+ sufficient without change.
+
+ === Connecting to a Unit
+
+ To change any EasyMotor configuration, you need to attach
+ a battery and a power switch, then use a micro USB cable
+ to connect the board to a computer running AltosUI.
+
+ === Changing the Configuration
+
+ All available configuration options can be set using the
+ “Configure Altimeter” menu selection within the AltosUI
+ program.
+
--- /dev/null
+== Installation
+
+ EasyMotor needs to be rigidly attached in the airframe, and the
+ long axis of the circuit board needs to be aligned with the axis
+ of flight. By default, the round beeper on the board should be
+ “up” towards the nose cone, and the screw terminal strips should
+ be “down” towards the fins and motor nozzle end of the rocket.
+
+ === Power Switch and Battery
+
+ In addition to the circuit board itself, EasyMotor needs
+ a power switch and battery to operate. Unlike most other
+ Altus Metrum products, EasyMotor does not work with
+ single-cell LiPo batteries. That's because commonly
+ available inexpensive pressure sensors need 5V, which is
+ more than a single-cell LiPo provides. Any battery that
+ provides from 6.5 to about 15 volts should work. Good
+ choices are the common 9V alkaline battery, or the very
+ small and light A23 12V alkaline batteries.
+
+ Because he often mounts EasyMotor to the motor's forward
+ bulkhead instead of to the airframe itself, Bdale often
+ uses a length of “shooter wire” from an e-match or used
+ motor igniter as a power switch, routing the wire out of
+ the typical fin can vent hole and using “twist and tape”
+ to power up the board. Whatever works!
+
+ === Pressure Sensor
+
+ The primary motivation for designing EasyMotor was to have
+ a reliable way of recording motor chamber pressure during
+ flight. To that end, EasyMotor supports attachment of a
+ low-cost analog pressure sensor. The board provides 5V
+ to power the sensor, and an input for measuring and
+ logging the output voltage from the sensor.
+
+ The kind of sensor EasyMotor is designed to work with
+ takes 5V in and has a linear analog output that ranges
+ from 0.5V at 0 to 4.5V at the maximum pressure supported
+ by the sensor. Very inexpensive sensors that have a
+ “1/8 NPT” threaded input, a “Buick-style” 3-pin connector,
+ and typically ship with a short cable and mating
+ connector, are readily available on eBay and AliExpress.
+
+ To log in-flight chamber pressure, a typical approach
+ might be to drill a 1/8" sampling hole all the way
+ through the center of the motor's forward closure, then
+ drill and tap partially through the closure with a “1/8
+ NPT” pipe tap. Fill the touch hole with grease, screw in
+ the pressure sensor, and attach the sensor leads to
+ EasyMotor.
--- /dev/null
+== Introduction and Overview
+
+Welcome to the Altus Metrum community! Our circuits and software reflect
+our passion for both hobby rocketry and Free Software. We hope their
+capabilities and performance will delight you in every way, but by
+releasing all of our hardware and software designs under open licenses,
+we also hope to empower you to take as active a role in our collective
+future as you wish!
+
+Thank you for your interest in motor testing products from Altus Metrum.
+Our first such product is EasyMotor, an in-flight motor data collection
+board for hobby rockets. EasyMotor is a small circuit board that is meant
+to log motor chamber pressure and rocket acceleration during flight. With
+this data it's possible to determine whether a research motor is performing
+as expected. With additional information about masses and airframe drag,
+it is even possible to closely estimate complete motor performance.
+
+With EasyMotor, the dilemma of “do I burn this on a test stand to learn more
+about how it actually works, or do I go fly it” is no more! You can fly your
+motor and get real performance data about it too!
+
+Because documentation is just as prone as software to contain “bugs”, and
+can always be improved… If you have questions that aren't answered in this
+manual, or just need a little help figuring things out, we strongly suggest
+joining the Altus Metrum user email list, which you can do by visiting
+https://lists.gag.com/mailman/listinfo/altusmetrum.
+
--- /dev/null
+== Operation
+
+ Operating an EasyMotor board is pretty easy. Turn the power on
+ before launch, typically during the usual pre-flight electronics
+ checklist after the rocket is installed on a launch rail.
+
+ The board will beep out a Morse code “P” every few seconds
+ indicating that it's in pad mode and ready to detect launch.
+ Once launch is detected, the board logs pressure and acceleration
+ data 100 times per second throughout the flight.
+
+ After flight, AltosUI can be used to download the flight data,
+ then export it to a comma separated values (CSV) file. Such a
+ file can easily be loaded into a spreadsheet for analysis.
--- /dev/null
+= Release Notes for Version 1.9.7
+include::release-head.adoc[]
+:doctype: article
+
+ Version 1.9.7
+
+ == AltOS
+
+ * Fix TeleGPS logging so that new data are appended to an existing log correctly
+
+ == AltosUI
+
+ * Support Mac OS X 11 (Big Sur)
+
+ * Support Monitor Idle on Easy Timer
+
+ * Fix TeleMega v4.0 and TeleMetrum v3.0 configuration in Antenna Down mode
+
+ * Show launch sites in Load Maps view
+
+ * Add IMU header names to CSV files
+
+ * Clean up TeleGPS log corruption due to firmware bugs during firmware update
+
+ == AltosDroid
+
+ * Support older devices back to Android version 5.1
+
+ * Fix a number of issues that could result in app crashes
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.7.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.6.adoc[]
|3.7-12V
endif::easytimer[]
+ ifdef::easymotor[]
+ |EasyMotor v2.0
+ |-
+ |ADXL375 200g
+ |-
+ |-
+ |-
+ |-
+ |6.5-15V
+ endif::easymotor[]
+
|===
endif::easymega[]
ifdef::easytimer[]
- |EasyMini
+ |EasyTimer
|Debug USB Battery
|Pyro A Pyro B Battery
|0.8 inch (2.03cm)
|24mm coupler
endif::easytimer[]
+ ifdef::easymotor[]
+ |EasyMotor
+ |Debug USB
+ |+5V Pres GND Switch Battery
+ |0.8 inch (2.03cm)
+ |1½ inch (3.81cm)
+ |24mm coupler
+ endif::easymotor[]
+
|===
[appendix]
== Release Notes
+ :leveloffset: 2
+ include::release-notes-1.9.7.adoc[]
+
+ <<<<
:leveloffset: 2
include::release-notes-1.9.6.adoc[]
:title-logo-image: image:../themes/background.png[]
:revnumber: v{version}
:revdate: 01 Jan 1970
-:copyright: Bdale Garbee and Keith Packard 2020
+:copyright: Bdale Garbee and Keith Packard 2021
:stylesheet: am.css
:linkcss:
:toc:
memory. For example, the default 435.750 MHz would be
configured using
- c F 435750
+ c F 435750 +
c w
Note that the 'f' parameter is a frequency calibration value
:revdate: 01 Jan 1970
:icons:
:icontype: svg
-:revremark: initial draft
-:copyright: Bdale Garbee 2020
+:copyright: Bdale Garbee 2021
:doctype: book
:numbered:
:stylesheet: am.css
Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
:revnumber: v{version}
:revdate: 01 Jan 1970
-:copyright: Bdale Garbee and Keith Packard 2020
+:copyright: Bdale Garbee and Keith Packard 2021
:stylesheet: am.css
:linkcss:
:doctype: article
you have before trying to reprogram them.
endif::telemega[]
+ TeleMini v3 can be updated directly over USB, but has no USB connector
+ on the board. Instead, the USB signals are present on a row of 6
+ holes adjacent to the copyright assertion in the silk screen. Thus,
+ updating firmware on TeleMini v3 requires making up a special cable,
+ after which you can treat it just like TeleMetrum or TeleMega. Many
+ USB cables seem to follow the color code of red is +5V, black is GND,
+ green is USB +, and white is USB -. On TeleMini v3, pin 3 which has
+ a square copper pad is ground, pin 1 is USB -, and pin 2 is USB +.
+
You may wish to begin by ensuring you have current firmware
images. These are distributed as part of the AltOS software
bundle that also includes the AltosUI ground station program.
ifdef::telemega[]
- === Updating TeleMega, TeleMetrum v2 or newer, EasyMega, EasyMini, TeleDongle v3 or TeleBT v3 Firmware
+ === Updating TeleMega, TeleMetrum v2 or newer, TeleMini v3, EasyMega, EasyMini, TeleDongle v3 or TeleBT v3 Firmware
endif::telemega[]
ifndef::telemega[]
the target device. Power up the device.
. Using a Micro USB cable, connect the target device to your
- computer's USB socket.
+ computer's USB socket. If the target is a TeleMini v3,
+ make up and attach a special USB cable.
. Run AltosUI, and select 'Flash Image' from the File menu.
unsigned int vid, pid;
char *name;
} name_map[] = {
+ { .vid = 0xfffe, .pid = 0x000d, .name = "EasyTimer" },
{ .vid = 0xfffe, .pid = 0x0028, .name = "EasyMega" },
{ .vid = 0xfffe, .pid = 0x002c, .name = "EasyMotor" },
{ .name = NULL },
cp -a MicroPeak.app macosx/
cp -a $(MACOSX_README) macosx/ReadMe-MicroPeak.rtf
cp -a $(MACOSX_INSTALL) macosx
- cp -a $(DOC) macosx
+ mkdir -p macosx/Doc
+ cp -a $(DOC) macosx/Doc
cp -p Info.plist macosx/MicroPeak.app/Contents
cp -p $(MACOSX_DRIVERS) macosx
mkdir -p macosx/MicroPeak.app/Contents/Resources/Java
#!/bin/bash
-#
-# Fix fonts. I don't know why the getting the
-# basename of the app set to . matters, but it does
-#
-case "$0" in
- /*)
- cd `dirname "$0"`
- ./`basename "$0"` "$@"
- exit $?
- ;;
-esac
-export FREETYPE_PROPERTIES=truetype:interpreter-version=35
##################################################################################
# #
# universalJavaApplicationStub #
# #
# @author Tobias Fischer #
# @url https://github.com/tofi86/universalJavaApplicationStub #
-# @date 2018-08-24 #
-# @version 3.0.4 #
+# @date 2021-02-21 #
+# @version 3.2.0 #
# #
##################################################################################
# #
# The MIT License (MIT) #
# #
-# Copyright (c) 2014-2018 Tobias Fischer #
+# Copyright (c) 2014-2021 Tobias Fischer #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy #
# of this software and associated documentation files (the "Software"), to deal #
# #
##################################################################################
-
+#
+# Fix fonts. I don't know why the getting the
+# basename of the app set to . matters, but it does
+#
+case "$0" in
+ /*)
+ cd `dirname "$0"`
+ ./`basename "$0"` "$@"
+ exit $?
+ ;;
+esac
+export FREETYPE_PROPERTIES=truetype:interpreter-version=35
# function 'stub_logger()'
#
JavaFolder="${AppleJavaFolder}"
ResourcesFolder="${AppleResourcesFolder}"
+ # set expandable variables
+ APP_ROOT="${AppPackageFolder}"
APP_PACKAGE="${AppPackageFolder}"
JAVAROOT="${AppleJavaFolder}"
USER_HOME="$HOME"
# AppPackageRoot is the standard WorkingDirectory when the script is started
WorkingDirectory="${AppPackageRoot}"
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
WorkingDirectory=$(eval echo "${WorkingDirectory}")
else
JVMClassPath=${JVMClassPath_RAW}
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
# read the JVM Options in either Array or String style
else
JVMDefaultOptions=${JVMDefaultOptions_RAW}
fi
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#84)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read StartOnMainThread and add as -XstartOnFirstThread
JVMStartOnMainThread=$(plist_get_java ':StartOnMainThread')
JVMDefaultOptions+=" -XstartOnFirstThread"
fi
- # read the JVM Arguments as an array and retain spaces
+ # read the JVM Arguments in either Array or String style (#76) and retain spaces
IFS=$'\t\n'
- MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ MainArgs_RAW=$(plist_get_java ':Arguments' | xargs)
+ if [[ $MainArgs_RAW == *Array* ]] ; then
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments' | tr -d '\n' | sed -E 's/Array \{ *(.*) *\}/\1/g' | sed 's/ */ /g')))
+ else
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ fi
unset IFS
# post processing of the array follows further below...
ResourcesFolder="${OracleResourcesFolder}"
WorkingDirectory="${OracleJavaFolder}"
+ # set expandable variables
APP_ROOT="${AppPackageFolder}"
+ APP_PACKAGE="${AppPackageFolder}"
+ JAVAROOT="${OracleJavaFolder}"
+ USER_HOME="$HOME"
# read the MainClass name
JVMMainClass="$(plist_get ':JVMMainClassName')"
JVMClassPath_RAW=$(plist_get ':JVMClassPath')
if [[ $JVMClassPath_RAW == *Array* ]] ; then
JVMClassPath=.$(plist_get ':JVMClassPath' | grep " " | sed 's/^ */:/g' | tr -d '\n' | xargs)
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
elif [[ ! -z ${JVMClassPath_RAW} ]] ; then
JVMClassPath=${JVMClassPath_RAW}
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
else
# Do NOT expand the default 'AppName.app/Contents/Java/*' classpath (#42)
fi
- # read the JVM Default Options
+ # read the JVM Default Options by parsing the :JVMDefaultOptions <dict>
+ # and pulling all <string> values starting with a dash (-)
JVMDefaultOptions=$(plist_get ':JVMDefaultOptions' | grep -o " \-.*" | tr -d '\n' | xargs)
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#99)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read the Main Arguments from JVMArguments key as an array and retain spaces (see #46 for naming details)
IFS=$'\t\n'
fi
+# (#75) check for undefined icons or icon names without .icns extension and prepare
+# an osascript statement for those cases when the icon can be shown in the dialog
+DialogWithIcon=""
+if [ ! -z ${CFBundleIconFile} ]; then
+ if [[ ${CFBundleIconFile} == *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}" ]] ; then
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ elif [[ ${CFBundleIconFile} != *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}.icns" ]] ; then
+ CFBundleIconFile+=".icns"
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ fi
+fi
+
# JVMVersion: post processing and optional splitting
if [[ ${JVMVersion} == *";"* ]]; then
stub_logger "[JavaRequirement] JVM minimum version: ${JVMVersion}"
stub_logger "[JavaRequirement] JVM maximum version: ${JVMMaxVersion}"
-# MainArgs: replace occurences of $APP_ROOT with its content
+# MainArgs: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
MainArgsArr=()
for i in "${MainArgs[@]}"
do
MainArgsArr+=("$(eval echo "$i")")
done
-# JVMOptions: replace occurences of $APP_ROOT with its content
+# JVMOptions: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMOptionsArr=()
for i in "${JVMOptions[@]}"
do
# internationalized messages
############################################
-LANG=$(defaults read -g AppleLocale)
-stub_logger "[Language] $LANG"
+# supported languages / available translations
+stubLanguages="^(fr|de|zh|es|en)-"
+
+# read user preferred languages as defined in macOS System Preferences (#101)
+stub_logger '[LanguageSearch] Checking preferred languages in macOS System Preferences...'
+appleLanguages=($(defaults read -g AppleLanguages | grep '\s"' | tr -d ',' | xargs))
+stub_logger "[LanguageSearch] ... found [${appleLanguages[*]}]"
+
+language=""
+for i in "${appleLanguages[@]}"
+do
+ langValue="${i%-*}"
+ if [[ "$i" =~ $stubLanguages ]]; then
+ stub_logger "[LanguageSearch] ... selected '$i' ('$langValue') as the default language for the launcher stub"
+ language=${langValue}
+ break
+ fi
+done
+if [ -z "${language}" ]; then
+ language="en"
+ stub_logger "[LanguageSearch] ... selected fallback 'en' as the default language for the launcher stub"
+fi
+stub_logger "[Language] $language"
-# French localization
-if [[ $LANG == fr* ]] ; then
+
+case "${language}" in
+# French
+fr)
MSG_ERROR_LAUNCHING="ERREUR au lancement de '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' n'est pas spécifié.\nL'application Java ne peut pas être lancée."
- MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
+ MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version de Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
MSG_NO_SUITABLE_JAVA="La version de Java installée sur votre système ne convient pas.\nCe programme nécessite Java %s"
MSG_JAVA_VERSION_OR_LATER="ou ultérieur"
MSG_JAVA_VERSION_LATEST="(dernière mise à jour)"
MSG_NO_SUITABLE_JAVA_CHECK="Merci de bien vouloir installer la version de Java requise."
MSG_INSTALL_JAVA="Java doit être installé sur votre système.\nRendez-vous sur java.com et suivez les instructions d'installation..."
MSG_LATER="Plus tard"
- MSG_VISIT_JAVA_DOT_COM="Visiter java.com"
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
-# German localization
-elif [[ $LANG == de* ]] ; then
+# German
+de)
MSG_ERROR_LAUNCHING="FEHLER beim Starten von '${CFBundleName}'."
MSG_MISSING_MAINCLASS="Die 'MainClass' ist nicht spezifiziert!\nDie Java-Anwendung kann nicht gestartet werden!"
MSG_JVMVERSION_REQ_INVALID="Die Syntax der angeforderten Java-Version ist ungültig: %s\nBitte kontaktieren Sie den Entwickler der App."
MSG_NO_SUITABLE_JAVA_CHECK="Stellen Sie sicher, dass die angeforderte Java-Version installiert ist."
MSG_INSTALL_JAVA="Auf Ihrem System muss die 'Java'-Software installiert sein.\nBesuchen Sie java.com für weitere Installationshinweise."
MSG_LATER="Später"
- MSG_VISIT_JAVA_DOT_COM="java.com öffnen"
+ MSG_VISIT_JAVA_DOT_COM="Java von Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java von AdoptOpenJDK"
+ ;;
-# Simplifyed Chinese localization
-elif [[ $LANG == zh* ]] ; then
+# Simplified Chinese
+zh)
MSG_ERROR_LAUNCHING="无法启动 '${CFBundleName}'."
MSG_MISSING_MAINCLASS="没有指定 'MainClass'!\nJava程序无法启动!"
MSG_JVMVERSION_REQ_INVALID="Java版本参数语法错误: %s\n请联系该应用的开发者。"
MSG_NO_SUITABLE_JAVA_CHECK="请确保系统中安装了所需的Java版本"
MSG_INSTALL_JAVA="你需要在Mac中安装Java运行环境!\n访问 java.com 了解如何安装。"
MSG_LATER="稍后"
- MSG_VISIT_JAVA_DOT_COM="访问 java.com"
-
-# English default localization
-else
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+
+# Spanish
+es)
+ MSG_ERROR_LAUNCHING="ERROR iniciando '${CFBundleName}'."
+ MSG_MISSING_MAINCLASS="¡'MainClass' no especificada!\n¡La aplicación Java no puede iniciarse!"
+ MSG_JVMVERSION_REQ_INVALID="La sintaxis de la versión Java requerida no es válida: %s\nPor favor, contacte con el desarrollador de la aplicación."
+ MSG_NO_SUITABLE_JAVA="¡No se encontró una versión de Java adecuada en su sistema!\nEste programa requiere Java %s"
+ MSG_JAVA_VERSION_OR_LATER="o posterior"
+ MSG_JAVA_VERSION_LATEST="(ultima actualización)"
+ MSG_JAVA_VERSION_MAX="superior a %s"
+ MSG_NO_SUITABLE_JAVA_CHECK="Asegúrese de instalar la versión Java requerida."
+ MSG_INSTALL_JAVA="¡Necesita tener JAVA instalado en su Mac!\nVisite java.com para consultar las instrucciones para su instalación..."
+ MSG_LATER="Más tarde"
+ MSG_VISIT_JAVA_DOT_COM="Java de Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java de AdoptOpenJDK"
+ ;;
+
+# English | default
+en|*)
MSG_ERROR_LAUNCHING="ERROR launching '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' isn't specified!\nJava application cannot be started!"
MSG_JVMVERSION_REQ_INVALID="The syntax of the required Java version is invalid: %s\nPlease contact the App developer."
MSG_NO_SUITABLE_JAVA_CHECK="Make sure you install the required Java version."
MSG_INSTALL_JAVA="You need to have JAVA installed on your Mac!\nVisit java.com for installation instructions..."
MSG_LATER="Later"
- MSG_VISIT_JAVA_DOT_COM="Visit java.com"
-fi
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+esac
################################################################################
function is_valid_requirement_pattern() {
local java_req=$1
- java8pattern='1\.[4-8](\.0)?(\.0_[0-9]+)?[*+]?'
+ java8pattern='1\.[4-8](\.[0-9]+)?(\.0_[0-9]+)?[*+]?'
java9pattern='(9|1[0-9])(-ea|[*+]|(\.[0-9]+){1,2}[*+]?)?'
# test matches either old Java versioning scheme (up to 1.8) or new scheme (starting with 9)
if [[ ${java_req} =~ ^(${java8pattern}|${java9pattern})$ ]]; then
if [[ $JAVA_HOME == /* ]] ; then
# if "$JAVA_HOME" starts with a Slash it's an absolute path
JAVACMD="$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as absolute path to the executable '$JAVACMD'"
else
# otherwise it's a relative path to "$AppPackageFolder"
JAVACMD="$AppPackageFolder/$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as relative path inside the App bundle to the executable '$JAVACMD'"
fi
JAVACMD_version=$(get_comparable_java_version $(get_java_version_from_cmd "${JAVACMD}"))
else
- stub_logger "[JavaSearch] ... didn't found JAVA_HOME"
+ stub_logger "[JavaSearch] ... haven't found JAVA_HOME"
fi
# check for any other or a specific Java version
# also if $JAVA_HOME exists but isn't executable
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
- stub_logger "[JavaSearch] Checking for JavaVirtualMachines on the system ..."
+
+ # add a warning in the syslog if JAVA_HOME is not executable or not found (#100)
+ if [ -n "$JAVA_HOME" ] ; then
+ stub_logger "[JavaSearch] ... but no 'java' executable was found at the JAVA_HOME location!"
+ fi
+
+ stub_logger "[JavaSearch] Searching for JavaVirtualMachines on the system ..."
# reset variables
JAVACMD=""
JAVACMD_version=""
# log exit cause
stub_logger "[EXIT 4] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 4
fi
# log exit cause
stub_logger "[EXIT 5] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 5
fi
# find installed JavaVirtualMachines (JDK + JRE)
allJVMs=()
- # read JDK's from '/usr/libexec/java_home -V' command
- while read -r line; do
- version=$(echo $line | awk -F $',' '{print $1;}')
- path=$(echo $line | awk -F $'" ' '{print $2;}')
- path+="/bin/java"
- allJVMs+=("$version:$path")
- done < <(/usr/libexec/java_home -V 2>&1 | grep '^[[:space:]]')
- # unset while loop variables
- unset version path
+
+ # read JDK's from '/usr/libexec/java_home --xml' command with PlistBuddy and a custom Dict iterator
+ # idea: https://stackoverflow.com/a/14085460/1128689 and https://scriptingosx.com/2018/07/parsing-dscl-output-in-scripts/
+ javaXml=$(/usr/libexec/java_home --xml)
+ javaCounter=$(/usr/libexec/PlistBuddy -c "Print" /dev/stdin <<< $javaXml | grep "Dict" | wc -l | tr -d ' ')
+
+ # iterate over all Dict entries
+ # but only if there are any JVMs at all (#93)
+ if [ "$javaCounter" -gt "0" ] ; then
+ for idx in $(seq 0 $((javaCounter - 1)))
+ do
+ version=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMVersion" /dev/stdin <<< $javaXml)
+ path=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMHomePath" /dev/stdin <<< $javaXml)
+ path+="/bin/java"
+ allJVMs+=("$version:$path")
+ done
+ # unset for loop variables
+ unset version path
+ fi
+
+ # add SDKMAN! java versions (#95)
+ if [ -d ~/.sdkman/candidates/java/ ] ; then
+ for sdkjdk in ~/.sdkman/candidates/java/*/
+ do
+ if [[ ${sdkjdk} =~ /current/$ ]] ; then
+ continue
+ fi
+
+ sdkjdkcmd="${sdkjdk}bin/java"
+ version=$(get_java_version_from_cmd "${sdkjdkcmd}")
+ allJVMs+=("$version:$sdkjdkcmd")
+ done
+ # unset for loop variables
+ unset version
+ fi
# add Apple JRE if available
if [ -x "${apple_jre_plugin}" ] ; then
# determine JVMs matching the min/max version requirement
+
+ stub_logger "[JavaSearch] Filtering the result list for JVMs matching the min/max version requirement ..."
+
minC=$(get_comparable_java_version ${JVMVersion})
maxC=$(get_comparable_java_version ${JVMMaxVersion})
matchingJVMs=()
# debug output
for i in "${matchingJVMs[@]}"
do
- stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+ stub_logger "[JavaSearch] ... matches all requirements: $i"
done
stub_logger "[JavaCommand] '$JAVACMD'"
stub_logger "[JavaVersion] $(get_java_version_from_cmd "${JAVACMD}")${JAVACMD_version:+ / $JAVACMD_version}"
+# Make sure tabbing mode is disabled for the selected java version
+
+CFBundleIdentifier=net.java.openjdk.$(get_java_version_from_cmd "${JAVACMD}").java
+if [ x$(defaults read ${CFBundleIdentifier} AppleWindowTabbingMode) != "xnever" ]; then
+ defaults write ${CFBundleIdentifier} AppleWindowTabbingMode never
+fi
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
stub_logger "[EXIT 3] ${MSG_NO_SUITABLE_JAVA_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 3
# log exit cause
stub_logger "[EXIT 1] ${MSG_ERROR_LAUNCHING}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 1
fi
# log exit cause
stub_logger "[EXIT 2] ${MSG_MISSING_MAINCLASS}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 2
fi
# - main class
# - main class arguments
# - passthrough arguments from Terminal or Drag'n'Drop to Finder icon
-stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" -splash:\"${ResourcesFolder}/${JVMSplashFile}\" -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
+stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" ${JVMSplashFile:+ -splash:\"${ResourcesFolder}/${JVMSplashFile}\"} -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
exec "${JAVACMD}" \
-Djava.library.path="${AppleJavaFolder}" \
-cp "${JVMClassPath}" \
- -splash:"${ResourcesFolder}/${JVMSplashFile}" \
+ ${JVMSplashFile:+ -splash:"${ResourcesFolder}/${JVMSplashFile}"} \
-Xdock:icon="${ResourcesFolder}/${CFBundleIconFile}" \
-Xdock:name="${CFBundleName}" \
${JVMOptionsArr:+"${JVMOptionsArr[@]}" }\
final static String download_command = "download";
final static String download_label = "Download";
+ static final String[][] download_menu_entries = new String[][] {
+ { download_label, download_command }
+ };
+
MicroPeak SetData(MicroData data) {
MicroPeak mp = this;
if (this.data != null) {
}
- private JMenu make_menu(String label, String[][] items) {
+ private void make_menu(String label, String[][] items) {
JMenu menu = new JMenu(label);
for (int i = 0; i < items.length; i++) {
if (MAC_OS_X) {
add_menu(menu, items[i][0], items[i][1]);
}
menu_bar.add(menu);
- return menu;
}
public MicroPeak() {
menu_bar = new JMenuBar();
setJMenuBar(menu_bar);
- JMenu file_menu = make_menu("File", file_menu_entries);
+ make_menu("File", file_menu_entries);
- JButton download_button = new JButton (download_label);
- download_button.setActionCommand(download_command);
- download_button.addActionListener(this);
- menu_bar.add(download_button);
+ if (MAC_OS_X) {
+ make_menu(download_label, download_menu_entries);
+ } else {
+ JButton download_button = new JButton (download_label);
+ download_button.setActionCommand(download_command);
+ download_button.addActionListener(this);
+ menu_bar.add(download_button);
+ }
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
container.validate();
doLayout();
validate();
+ Insets i = getInsets();
+ Dimension ps = pane.getPreferredSize();
+ ps.width += i.left + i.right;
+ ps.height += i.top + i.bottom;
+ setSize(ps);
pack();
setVisible(true);
}
ARMM3DIRS=\
easymega-v1.0 easymega-v1.0/flash-loader \
easymega-v2.0 easymega-v2.0/flash-loader \
+ easymotor-v2 easymotor-v2/flash-loader \
easytimer-v1 easytimer-v1/flash-loader \
telemega-v0.1 telemega-v0.1/flash-loader \
telemega-v1.0 telemega-v1.0/flash-loader \
#define SERIAL_3_PC10_PC11 0
#define SERIAL_3_PD8_PD9 0
-#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (512 * 1024)
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX (1984 * 1024)
#define AO_CONFIG_MAX_SIZE 1024
#define LOG_ERASE_MARK 0x55
#define LOG_MAX_ERASE 128
#include <ao.h>
#include <ao_ms5607.h>
-#include <ao_adxl375.h>
+#include <ao_bmx160.h>
#include <ao_log.h>
#include <ao_exti.h>
#include <ao_packet.h>
if (minor < 14)
ao_config.radio_amp = AO_CONFIG_DEFAULT_RADIO_AMP;
#endif
-#if HAS_GYRO
+#if HAS_IMU
if (minor < 15) {
ao_config.accel_zero_along = 0;
ao_config.accel_zero_across = 0;
{
printf("Accel cal +1g: %d -1g: %d\n",
ao_config.accel_plus_g, ao_config.accel_minus_g);
-#if HAS_GYRO
+#if HAS_IMU
printf ("IMU cal along %d across %d through %d\n",
ao_config.accel_zero_along,
ao_config.accel_zero_across,
#define ACCEL_CALIBRATE_SAMPLES 1024
#define ACCEL_CALIBRATE_SHIFT 10
-#if HAS_GYRO
+#if HAS_IMU
static int16_t accel_cal_along;
static int16_t accel_cal_across;
static int16_t accel_cal_through;
uint16_t i;
int32_t accel_total;
uint8_t cal_data_ring;
-#if HAS_GYRO
+#if HAS_IMU
int32_t accel_along_total = 0;
int32_t accel_across_total = 0;
int32_t accel_through_total = 0;
ao_sleep(&ao_sample_data);
while (i && cal_data_ring != ao_sample_data) {
accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
-#if HAS_GYRO
+#if HAS_IMU
accel_along_total += (int32_t) ao_data_along(&ao_data_ring[cal_data_ring]);
accel_across_total += (int32_t) ao_data_across(&ao_data_ring[cal_data_ring]);
accel_through_total += (int32_t) ao_data_through(&ao_data_ring[cal_data_ring]);
i--;
}
}
-#if HAS_GYRO
+#if HAS_IMU
accel_cal_along = accel_along_total >> ACCEL_CALIBRATE_SHIFT;
accel_cal_across = accel_across_total >> ACCEL_CALIBRATE_SHIFT;
accel_cal_through = accel_through_total >> ACCEL_CALIBRATE_SHIFT;
ao_config_accel_calibrate_set(void)
{
int16_t up, down;
- uint16_t r;
-#if HAS_GYRO
+ bool auto_cal;
+#if HAS_IMU
int16_t accel_along_up = 0, accel_along_down = 0;
int16_t accel_across_up = 0, accel_across_down = 0;
int16_t accel_through_up = 0, accel_through_down = 0;
#endif
- r = ao_cmd_decimal();
+ up = ao_cmd_decimal();
if (ao_cmd_status != ao_cmd_success)
return;
- if (r == 0) {
+ down = ao_cmd_decimal();
+ auto_cal = (up == 0 && ao_cmd_status != ao_cmd_success);
+ if (auto_cal) {
up = ao_config_accel_calibrate_auto("up");
-#if HAS_GYRO
+#if HAS_IMU
accel_along_up = accel_cal_along;
accel_across_up = accel_cal_across;
accel_through_up = accel_cal_through;
#endif
down = ao_config_accel_calibrate_auto("down");
-#if HAS_GYRO
+#if HAS_IMU
accel_along_down = accel_cal_along;
accel_across_down = accel_cal_across;
accel_through_down = accel_cal_through;
#endif
- } else {
- up = r;
- r = ao_cmd_decimal();
- if (ao_cmd_status != ao_cmd_success)
- return;
- down = r;
}
if (up >= down) {
printf("Invalid accel: up (%d) down (%d)\n",
_ao_config_edit_start();
ao_config.accel_plus_g = up;
ao_config.accel_minus_g = down;
-#if HAS_GYRO
- if (r == 0) {
+#if HAS_IMU
+ if (auto_cal) {
ao_config.accel_zero_along = (accel_along_up + accel_along_down) / 2;
ao_config.accel_zero_across = (accel_across_up + accel_across_down) / 2;
ao_config.accel_zero_through = (accel_through_up + accel_through_down) / 2;
+ } else {
+ int16_t v;
+
+ v = ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success) {
+ ao_config.accel_zero_along = v;
+ v = ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success) {
+ ao_config.accel_zero_across = v;
+ v = ao_cmd_decimal();
+ if (ao_cmd_status == ao_cmd_success)
+ ao_config.accel_zero_through = v;
+ }
+ }
}
#endif
_ao_config_edit_finish();
#if HAS_RADIO_AMP
uint8_t radio_amp; /* minor version 14 */
#endif
-#if HAS_GYRO
+#if HAS_IMU
int16_t accel_zero_along; /* minor version 15 */
int16_t accel_zero_across; /* minor version 15 */
int16_t accel_zero_through; /* minor version 15 */
#endif
static void
-ao_igniter_fire(enum ao_igniter igniter)
+ao_igniter_fire(enum ao_igniter igniter, bool wait)
{
if (!ao_ignition[igniter].fired) {
ao_ignition[igniter].firing = 1;
break;
}
ao_ignition[igniter].firing = 0;
- ao_delay(AO_IGNITER_CHARGE_TIME);
+ if (wait)
+ ao_delay(AO_IGNITER_CHARGE_TIME);
}
}
switch(ao_config.ignite_mode) {
case AO_IGNITE_MODE_DUAL:
if (ao_flight_drogue <= ao_flight_state && ao_flight_state < ao_flight_landed)
- ao_igniter_fire(ao_igniter_drogue);
+ ao_igniter_fire(ao_igniter_drogue, true);
if (ao_flight_main <= ao_flight_state && ao_flight_state < ao_flight_landed)
- ao_igniter_fire(ao_igniter_main);
+ ao_igniter_fire(ao_igniter_main, true);
break;
case AO_IGNITE_MODE_APOGEE:
if (ao_flight_drogue <= ao_flight_state && ao_flight_state < ao_flight_landed) {
- ao_igniter_fire(ao_igniter_drogue);
- ao_igniter_fire(ao_igniter_main);
+ ao_igniter_fire(ao_igniter_drogue, true);
+ ao_igniter_fire(ao_igniter_main, true);
}
break;
case AO_IGNITE_MODE_MAIN:
if (ao_flight_main <= ao_flight_state && ao_flight_state < ao_flight_landed) {
- ao_igniter_fire(ao_igniter_drogue);
- ao_igniter_fire(ao_igniter_main);
+ ao_igniter_fire(ao_igniter_drogue, true);
+ ao_igniter_fire(ao_igniter_main, true);
}
break;
case AO_IGNITE_MODE_BOOSTER:
if (ao_flight_fast <= ao_flight_state && ao_flight_state < ao_flight_landed)
- ao_igniter_fire(ao_igniter_main);
+ ao_igniter_fire(ao_igniter_main, true);
if (ao_flight_drogue <= ao_flight_state && ao_flight_state < ao_flight_landed)
- ao_igniter_fire(ao_igniter_drogue);
+ ao_igniter_fire(ao_igniter_drogue, true);
break;
}
}
#if HAS_IGNITE
if (ao_cmd_lex_c == 'm' && ao_match_word("main")) {
ao_ignition[ao_igniter_main].fired = 0;
- ao_igniter_fire(ao_igniter_main);
+ ao_igniter_fire(ao_igniter_main, false);
return;
}
if (ao_cmd_lex_c == 'd' && ao_match_word("drogue")) {
ao_ignition[ao_igniter_drogue].fired = 0;
- ao_igniter_fire(ao_igniter_drogue);
+ ao_igniter_fire(ao_igniter_drogue, false);
return;
}
#endif
ao_log_end_pos = ao_log_pos_block_end(0);
if (ao_flight_number) {
- uint32_t full = ao_log_current_pos;
- uint32_t empty = ao_log_end_pos - AO_LOG_SIZE;
+ uint32_t full = (ao_log_current_pos) / AO_LOG_SIZE;
+ uint32_t empty = (ao_log_end_pos - AO_LOG_SIZE) / AO_LOG_SIZE;
/* If there's already a flight started, then find the
* end of it
*/
for (;;) {
- ao_log_current_pos = (full + empty) >> 1;
- ao_log_current_pos -= ao_log_current_pos % AO_LOG_SIZE;
+ uint32_t current = (full + empty) >> 1;
+ ao_log_current_pos = current * AO_LOG_SIZE;
- if (ao_log_current_pos == full) {
+ if (current == full) {
if (ao_log_check(ao_log_current_pos) != AO_LOG_EMPTY)
ao_log_current_pos += AO_LOG_SIZE;
break;
}
- if (ao_log_current_pos == empty)
+ if (current == empty)
break;
if (ao_log_check(ao_log_current_pos) != AO_LOG_EMPTY) {
- full = ao_log_current_pos;
+ full = current;
} else {
- empty = ao_log_current_pos;
+ empty = current;
}
}
ret = 1;
ao_log_write(&ao_log_data);
}
+static uint8_t
+ao_log_check_empty(void)
+{
+ uint8_t *b = (void *) &ao_log_data;
+ unsigned i;
+
+ for (i = 0; i < sizeof (ao_log_type); i++)
+ if (*b++ != AO_STORAGE_ERASED_BYTE)
+ return 0;
+ return 1;
+}
+
int8_t
ao_log_check(uint32_t pos)
{
- if (ao_storage_is_erased(pos & ~(ao_storage_block - 1)))
- return 0;
-
if (!ao_storage_read(pos,
&ao_log_data,
sizeof (struct ao_log_gps)))
return AO_LOG_INVALID;
+ if (ao_log_check_empty())
+ return AO_LOG_EMPTY;
+
if (!ao_log_check_data())
return AO_LOG_INVALID;
+
return AO_LOG_VALID;
}
return 1;
}
-#ifndef AO_STORAGE_ERASED_BYTE
-#define AO_STORAGE_ERASED_BYTE 0xff
-#endif
-
uint8_t
ao_storage_is_erased(uint32_t pos)
{
#define USE_STORAGE_CONFIG 1
#endif
+#ifndef AO_STORAGE_ERASED_BYTE
+#define AO_STORAGE_ERASED_BYTE 0xff
+#endif
+
#if USE_STORAGE_CONFIG
/* Byte offset of config block. Will be ao_storage_block bytes long */
extern ao_pos_t ao_storage_config;
--- /dev/null
+#
+# Tiny AltOS build
+#
+#
+TOPDIR=..
+include $(TOPDIR)/attiny/Makefile.defs
+
+PROGNAME=microtest-v1.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+ALTOS_SRC = \
+ ao_microtest.c \
+ ao_spi_attiny.c \
+ ao_led_tiny.c \
+ ao_clock.c \
+ ao_ms5607.c \
+ ao_exti.c \
+ ao_notask.c \
+ ao_eeprom_tiny.c \
+ ao_panic.c \
+ ao_log_micro.c \
+ ao_async.c \
+ ao_microflight.c \
+ ao_microkalman.c
+
+INC=\
+ ao.h \
+ ao_pins.h \
+ ao_arch.h \
+ ao_arch_funcs.h \
+ ao_exti.h \
+ ao_ms5607.h \
+ ao_log_micro.h \
+ ao_micropeak.h \
+ ao_product.h \
+ altitude-pa.h
+
+IDPRODUCT=0
+PRODUCT=MicroTest-v1.0
+PRODUCT_DEF=-DMICROPEAK
+CFLAGS = $(PRODUCT_DEF) $(ATTINY_CFLAGS)
+
+SRC=$(ALTOS_SRC)
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+CHECK=sh ../util/check-avr-mem
+
+$(PROG): Makefile $(OBJ)
+ $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
+ $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
+
+$(HEX): $(PROG)
+ avr-size $(PROG)
+ $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
+
+load: $(HEX)
+ $(LOADCMD) $(LOADARG)$(HEX)
+
+load-slow: $(HEX)
+ $(LOADCMD) $(LOADSLOW) $(LOADARG)$(HEX)
+
+distclean: clean
+
+clean:
+ rm -f *.o *.elf *.ihx *.map
+ rm -f ao_product.h
+
+load-product:
+ ./$(SCRIPT) fast
+
+load-product-slow:
+ ./$(SCRIPT) slow
+
+install:
+
+uninstall:
+
+$(OBJ): $(INC)
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_micropeak.h>
+#include <ao_ms5607.h>
+#include <ao_async.h>
+#include <ao_log_micro.h>
+
+static struct ao_ms5607_value value;
+
+alt_t ground_alt, max_alt;
+alt_t ao_max_height;
+
+void
+ao_pa_get(void)
+{
+ ao_ms5607_sample(&ao_ms5607_current);
+ ao_ms5607_convert(&ao_ms5607_current, &value);
+ pa = value.pres;
+}
+static void
+ao_pips(void)
+{
+ uint8_t i;
+ for (i = 0; i < 10; i++) {
+ ao_led_toggle(AO_LED_REPORT);
+ ao_delay(AO_MS_TO_TICKS(80));
+ }
+ ao_delay(AO_MS_TO_TICKS(200));
+}
+
+#define POLY 0x8408
+
+static uint16_t
+ao_log_micro_crc(uint16_t crc, uint8_t byte)
+{
+ uint8_t i;
+
+ for (i = 0; i < 8; i++) {
+ if ((crc & 0x0001) ^ (byte & 0x0001))
+ crc = (crc >> 1) ^ POLY;
+ else
+ crc = crc >> 1;
+ byte >>= 1;
+ }
+ return crc;
+}
+
+struct header {
+ uint32_t pa_ground_offset;
+ uint32_t pa_min_offset;
+ N_SAMPLES_TYPE nsamples;
+};
+
+static const struct header head = {
+ .pa_ground_offset = 0xfffefdfc,
+ .pa_min_offset = 0xfbfaf9f8,
+ .nsamples = 64
+};
+
+static void
+ao_test_micro_dump(void)
+{
+ N_SAMPLES_TYPE n_samples;
+ uint16_t nbytes;
+ uint8_t byte;
+ uint16_t b;
+ uint16_t crc = 0xffff;
+
+ n_samples = head.nsamples;
+ nbytes = STARTING_LOG_OFFSET + sizeof (uint16_t) * n_samples;
+
+ /*
+ * Rewrite n_samples so that it includes the log ID value with
+ * 32-bit n_samples split into two chunks
+ */
+ if (sizeof (n_samples) > 2) {
+ N_SAMPLES_TYPE n_samples_low;
+ N_SAMPLES_TYPE n_samples_high;
+ n_samples_low = n_samples & ((1 << AO_LOG_ID_SHIFT) - 1);
+ n_samples_high = (n_samples - n_samples_low) << AO_LOG_ID_WIDTH;
+ n_samples = n_samples_low | n_samples_high;
+ }
+#if AO_LOG_ID
+ n_samples |= AO_LOG_ID << AO_LOG_ID_SHIFT;
+#endif
+ ao_async_start();
+ ao_async_byte('M');
+ ao_async_byte('P');
+ for (b = 0; b < nbytes; b++) {
+ if ((b & 0xf) == 0)
+ ao_log_newline();
+ if (b < sizeof (head))
+ byte = ((uint8_t *) &head)[b];
+ else
+ byte = b;
+#if AO_LOG_ID
+ if (N_SAMPLES_OFFSET <= b && b < (N_SAMPLES_OFFSET + sizeof(n_samples))) {
+ byte = n_samples >> ((b - N_SAMPLES_OFFSET) << 3);
+ }
+#endif
+ ao_log_hex(byte);
+ crc = ao_log_micro_crc(crc, byte);
+ }
+ ao_log_newline();
+ crc = ~crc;
+ ao_log_hex(crc >> 8);
+ ao_log_hex(crc);
+ ao_log_newline();
+ ao_async_stop();
+}
+
+int
+main(void)
+{
+ ao_led_init();
+ ao_timer_init();
+
+ /* Init external hardware */
+ ao_spi_init();
+ ao_ms5607_init();
+ ao_ms5607_setup();
+
+ for (;;)
+ {
+ ao_delay(AO_MS_TO_TICKS(1000));
+
+ ao_pips();
+ ao_test_micro_dump();
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+#include <avr/pgmspace.h>
+
+#define AO_LED_ORANGE (1<<4)
+#define AO_LED_SERIAL 4
+#define AO_LED_PANIC AO_LED_ORANGE
+#define AO_LED_REPORT AO_LED_ORANGE
+#define LEDS_AVAILABLE (AO_LED_ORANGE)
+#define USE_SERIAL_1_STDIN 0
+#define HAS_USB 0
+#define PACKET_HAS_SLAVE 0
+#define HAS_SERIAL_1 0
+#define HAS_TASK 0
+#define HAS_MS5607 1
+#define HAS_MS5611 0
+#define HAS_SENSOR_ERRORS 0
+#define HAS_EEPROM 0
+#define HAS_BEEP 0
+#define AVR_CLOCK 250000UL
+
+/* SPI */
+#define SPI_PORT PORTB
+#define SPI_PIN PINB
+#define SPI_DIR DDRB
+#define AO_MS5607_CS_PORT PORTB
+#define AO_MS5607_CS_PIN 3
+
+/* MS5607 */
+#define AO_MS5607_SPI_INDEX 0
+#define AO_MS5607_MISO_PORT PORTB
+#define AO_MS5607_MISO_PIN 0
+#define AO_MS5607_BARO_OVERSAMPLE 4096
+#define AO_MS5607_TEMP_OVERSAMPLE 1024
+
+/* I2C */
+#define I2C_PORT PORTB
+#define I2C_PIN PINB
+#define I2C_DIR DDRB
+#define I2C_PIN_SCL PINB2
+#define I2C_PIN_SDA PINB0
+
+#define AO_CONST_ATTRIB PROGMEM
+typedef int32_t alt_t;
+#define FETCH_ALT(o) ((alt_t) pgm_read_dword(&altitude_table[o]))
+
+#define AO_ALT_VALUE(x) ((x) * (alt_t) 10)
+
+#endif /* _AO_PINS_H_ */
#!/bin/bash
-#
-# Fix fonts. I don't know why the getting the
-# basename of the app set to . matters, but it does
-#
-case "$0" in
- /*)
- cd `dirname "$0"`
- ./`basename "$0"` "$@"
- exit $?
- ;;
-esac
-export FREETYPE_PROPERTIES=truetype:interpreter-version=35
##################################################################################
# #
# universalJavaApplicationStub #
# #
# @author Tobias Fischer #
# @url https://github.com/tofi86/universalJavaApplicationStub #
-# @date 2018-08-24 #
-# @version 3.0.4 #
+# @date 2021-02-21 #
+# @version 3.2.0 #
# #
##################################################################################
# #
# The MIT License (MIT) #
# #
-# Copyright (c) 2014-2018 Tobias Fischer #
+# Copyright (c) 2014-2021 Tobias Fischer #
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy #
# of this software and associated documentation files (the "Software"), to deal #
# #
##################################################################################
-
+#
+# Fix fonts. I don't know why the getting the
+# basename of the app set to . matters, but it does
+#
+case "$0" in
+ /*)
+ cd `dirname "$0"`
+ ./`basename "$0"` "$@"
+ exit $?
+ ;;
+esac
+export FREETYPE_PROPERTIES=truetype:interpreter-version=35
# function 'stub_logger()'
#
JavaFolder="${AppleJavaFolder}"
ResourcesFolder="${AppleResourcesFolder}"
+ # set expandable variables
+ APP_ROOT="${AppPackageFolder}"
APP_PACKAGE="${AppPackageFolder}"
JAVAROOT="${AppleJavaFolder}"
USER_HOME="$HOME"
# AppPackageRoot is the standard WorkingDirectory when the script is started
WorkingDirectory="${AppPackageRoot}"
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
WorkingDirectory=$(eval echo "${WorkingDirectory}")
else
JVMClassPath=${JVMClassPath_RAW}
fi
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
# read the JVM Options in either Array or String style
else
JVMDefaultOptions=${JVMDefaultOptions_RAW}
fi
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#84)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read StartOnMainThread and add as -XstartOnFirstThread
JVMStartOnMainThread=$(plist_get_java ':StartOnMainThread')
JVMDefaultOptions+=" -XstartOnFirstThread"
fi
- # read the JVM Arguments as an array and retain spaces
+ # read the JVM Arguments in either Array or String style (#76) and retain spaces
IFS=$'\t\n'
- MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ MainArgs_RAW=$(plist_get_java ':Arguments' | xargs)
+ if [[ $MainArgs_RAW == *Array* ]] ; then
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments' | tr -d '\n' | sed -E 's/Array \{ *(.*) *\}/\1/g' | sed 's/ */ /g')))
+ else
+ MainArgs=($(xargs -n1 <<<$(plist_get_java ':Arguments')))
+ fi
unset IFS
# post processing of the array follows further below...
ResourcesFolder="${OracleResourcesFolder}"
WorkingDirectory="${OracleJavaFolder}"
+ # set expandable variables
APP_ROOT="${AppPackageFolder}"
+ APP_PACKAGE="${AppPackageFolder}"
+ JAVAROOT="${OracleJavaFolder}"
+ USER_HOME="$HOME"
# read the MainClass name
JVMMainClass="$(plist_get ':JVMMainClassName')"
JVMClassPath_RAW=$(plist_get ':JVMClassPath')
if [[ $JVMClassPath_RAW == *Array* ]] ; then
JVMClassPath=.$(plist_get ':JVMClassPath' | grep " " | sed 's/^ */:/g' | tr -d '\n' | xargs)
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
elif [[ ! -z ${JVMClassPath_RAW} ]] ; then
JVMClassPath=${JVMClassPath_RAW}
- # expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMClassPath=$(eval echo "${JVMClassPath}")
else
# Do NOT expand the default 'AppName.app/Contents/Java/*' classpath (#42)
fi
- # read the JVM Default Options
+ # read the JVM Default Options by parsing the :JVMDefaultOptions <dict>
+ # and pulling all <string> values starting with a dash (-)
JVMDefaultOptions=$(plist_get ':JVMDefaultOptions' | grep -o " \-.*" | tr -d '\n' | xargs)
+ # expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME (#99)
+ JVMDefaultOptions=$(eval echo "${JVMDefaultOptions}")
# read the Main Arguments from JVMArguments key as an array and retain spaces (see #46 for naming details)
IFS=$'\t\n'
fi
+# (#75) check for undefined icons or icon names without .icns extension and prepare
+# an osascript statement for those cases when the icon can be shown in the dialog
+DialogWithIcon=""
+if [ ! -z ${CFBundleIconFile} ]; then
+ if [[ ${CFBundleIconFile} == *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}" ]] ; then
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ elif [[ ${CFBundleIconFile} != *.icns ]] && [[ -f "${ResourcesFolder}/${CFBundleIconFile}.icns" ]] ; then
+ CFBundleIconFile+=".icns"
+ DialogWithIcon=" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ fi
+fi
+
# JVMVersion: post processing and optional splitting
if [[ ${JVMVersion} == *";"* ]]; then
stub_logger "[JavaRequirement] JVM minimum version: ${JVMVersion}"
stub_logger "[JavaRequirement] JVM maximum version: ${JVMMaxVersion}"
-# MainArgs: replace occurences of $APP_ROOT with its content
+# MainArgs: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
MainArgsArr=()
for i in "${MainArgs[@]}"
do
MainArgsArr+=("$(eval echo "$i")")
done
-# JVMOptions: replace occurences of $APP_ROOT with its content
+# JVMOptions: expand variables $APP_PACKAGE, $APP_ROOT, $JAVAROOT, $USER_HOME
JVMOptionsArr=()
for i in "${JVMOptions[@]}"
do
# internationalized messages
############################################
-LANG=$(defaults read -g AppleLocale)
-stub_logger "[Language] $LANG"
+# supported languages / available translations
+stubLanguages="^(fr|de|zh|es|en)-"
+
+# read user preferred languages as defined in macOS System Preferences (#101)
+stub_logger '[LanguageSearch] Checking preferred languages in macOS System Preferences...'
+appleLanguages=($(defaults read -g AppleLanguages | grep '\s"' | tr -d ',' | xargs))
+stub_logger "[LanguageSearch] ... found [${appleLanguages[*]}]"
+
+language=""
+for i in "${appleLanguages[@]}"
+do
+ langValue="${i%-*}"
+ if [[ "$i" =~ $stubLanguages ]]; then
+ stub_logger "[LanguageSearch] ... selected '$i' ('$langValue') as the default language for the launcher stub"
+ language=${langValue}
+ break
+ fi
+done
+if [ -z "${language}" ]; then
+ language="en"
+ stub_logger "[LanguageSearch] ... selected fallback 'en' as the default language for the launcher stub"
+fi
+stub_logger "[Language] $language"
-# French localization
-if [[ $LANG == fr* ]] ; then
+
+case "${language}" in
+# French
+fr)
MSG_ERROR_LAUNCHING="ERREUR au lancement de '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' n'est pas spécifié.\nL'application Java ne peut pas être lancée."
- MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
+ MSG_JVMVERSION_REQ_INVALID="La syntaxe de la version de Java demandée est invalide: %s\nVeuillez contacter le développeur de l'application."
MSG_NO_SUITABLE_JAVA="La version de Java installée sur votre système ne convient pas.\nCe programme nécessite Java %s"
MSG_JAVA_VERSION_OR_LATER="ou ultérieur"
MSG_JAVA_VERSION_LATEST="(dernière mise à jour)"
MSG_NO_SUITABLE_JAVA_CHECK="Merci de bien vouloir installer la version de Java requise."
MSG_INSTALL_JAVA="Java doit être installé sur votre système.\nRendez-vous sur java.com et suivez les instructions d'installation..."
MSG_LATER="Plus tard"
- MSG_VISIT_JAVA_DOT_COM="Visiter java.com"
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
-# German localization
-elif [[ $LANG == de* ]] ; then
+# German
+de)
MSG_ERROR_LAUNCHING="FEHLER beim Starten von '${CFBundleName}'."
MSG_MISSING_MAINCLASS="Die 'MainClass' ist nicht spezifiziert!\nDie Java-Anwendung kann nicht gestartet werden!"
MSG_JVMVERSION_REQ_INVALID="Die Syntax der angeforderten Java-Version ist ungültig: %s\nBitte kontaktieren Sie den Entwickler der App."
MSG_NO_SUITABLE_JAVA_CHECK="Stellen Sie sicher, dass die angeforderte Java-Version installiert ist."
MSG_INSTALL_JAVA="Auf Ihrem System muss die 'Java'-Software installiert sein.\nBesuchen Sie java.com für weitere Installationshinweise."
MSG_LATER="Später"
- MSG_VISIT_JAVA_DOT_COM="java.com öffnen"
+ MSG_VISIT_JAVA_DOT_COM="Java von Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java von AdoptOpenJDK"
+ ;;
-# Simplifyed Chinese localization
-elif [[ $LANG == zh* ]] ; then
+# Simplified Chinese
+zh)
MSG_ERROR_LAUNCHING="无法启动 '${CFBundleName}'."
MSG_MISSING_MAINCLASS="没有指定 'MainClass'!\nJava程序无法启动!"
MSG_JVMVERSION_REQ_INVALID="Java版本参数语法错误: %s\n请联系该应用的开发者。"
MSG_NO_SUITABLE_JAVA_CHECK="请确保系统中安装了所需的Java版本"
MSG_INSTALL_JAVA="你需要在Mac中安装Java运行环境!\n访问 java.com 了解如何安装。"
MSG_LATER="稍后"
- MSG_VISIT_JAVA_DOT_COM="访问 java.com"
-
-# English default localization
-else
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+
+# Spanish
+es)
+ MSG_ERROR_LAUNCHING="ERROR iniciando '${CFBundleName}'."
+ MSG_MISSING_MAINCLASS="¡'MainClass' no especificada!\n¡La aplicación Java no puede iniciarse!"
+ MSG_JVMVERSION_REQ_INVALID="La sintaxis de la versión Java requerida no es válida: %s\nPor favor, contacte con el desarrollador de la aplicación."
+ MSG_NO_SUITABLE_JAVA="¡No se encontró una versión de Java adecuada en su sistema!\nEste programa requiere Java %s"
+ MSG_JAVA_VERSION_OR_LATER="o posterior"
+ MSG_JAVA_VERSION_LATEST="(ultima actualización)"
+ MSG_JAVA_VERSION_MAX="superior a %s"
+ MSG_NO_SUITABLE_JAVA_CHECK="Asegúrese de instalar la versión Java requerida."
+ MSG_INSTALL_JAVA="¡Necesita tener JAVA instalado en su Mac!\nVisite java.com para consultar las instrucciones para su instalación..."
+ MSG_LATER="Más tarde"
+ MSG_VISIT_JAVA_DOT_COM="Java de Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java de AdoptOpenJDK"
+ ;;
+
+# English | default
+en|*)
MSG_ERROR_LAUNCHING="ERROR launching '${CFBundleName}'."
MSG_MISSING_MAINCLASS="'MainClass' isn't specified!\nJava application cannot be started!"
MSG_JVMVERSION_REQ_INVALID="The syntax of the required Java version is invalid: %s\nPlease contact the App developer."
MSG_NO_SUITABLE_JAVA_CHECK="Make sure you install the required Java version."
MSG_INSTALL_JAVA="You need to have JAVA installed on your Mac!\nVisit java.com for installation instructions..."
MSG_LATER="Later"
- MSG_VISIT_JAVA_DOT_COM="Visit java.com"
-fi
+ MSG_VISIT_JAVA_DOT_COM="Java by Oracle"
+ MSG_VISIT_ADOPTOPENJDK="Java by AdoptOpenJDK"
+ ;;
+esac
################################################################################
function is_valid_requirement_pattern() {
local java_req=$1
- java8pattern='1\.[4-8](\.0)?(\.0_[0-9]+)?[*+]?'
+ java8pattern='1\.[4-8](\.[0-9]+)?(\.0_[0-9]+)?[*+]?'
java9pattern='(9|1[0-9])(-ea|[*+]|(\.[0-9]+){1,2}[*+]?)?'
# test matches either old Java versioning scheme (up to 1.8) or new scheme (starting with 9)
if [[ ${java_req} =~ ^(${java8pattern}|${java9pattern})$ ]]; then
if [[ $JAVA_HOME == /* ]] ; then
# if "$JAVA_HOME" starts with a Slash it's an absolute path
JAVACMD="$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as absolute path to the executable '$JAVACMD'"
else
# otherwise it's a relative path to "$AppPackageFolder"
JAVACMD="$AppPackageFolder/$JAVA_HOME/bin/java"
+ stub_logger "[JavaSearch] ... parsing JAVA_HOME as relative path inside the App bundle to the executable '$JAVACMD'"
fi
JAVACMD_version=$(get_comparable_java_version $(get_java_version_from_cmd "${JAVACMD}"))
else
- stub_logger "[JavaSearch] ... didn't found JAVA_HOME"
+ stub_logger "[JavaSearch] ... haven't found JAVA_HOME"
fi
# check for any other or a specific Java version
# also if $JAVA_HOME exists but isn't executable
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
- stub_logger "[JavaSearch] Checking for JavaVirtualMachines on the system ..."
+
+ # add a warning in the syslog if JAVA_HOME is not executable or not found (#100)
+ if [ -n "$JAVA_HOME" ] ; then
+ stub_logger "[JavaSearch] ... but no 'java' executable was found at the JAVA_HOME location!"
+ fi
+
+ stub_logger "[JavaSearch] Searching for JavaVirtualMachines on the system ..."
# reset variables
JAVACMD=""
JAVACMD_version=""
# log exit cause
stub_logger "[EXIT 4] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 4
fi
# log exit cause
stub_logger "[EXIT 5] ${MSG_JVMVERSION_REQ_INVALID_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_JVMVERSION_REQ_INVALID_EXPANDED}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 5
fi
# find installed JavaVirtualMachines (JDK + JRE)
allJVMs=()
- # read JDK's from '/usr/libexec/java_home -V' command
- while read -r line; do
- version=$(echo $line | awk -F $',' '{print $1;}')
- path=$(echo $line | awk -F $'" ' '{print $2;}')
- path+="/bin/java"
- allJVMs+=("$version:$path")
- done < <(/usr/libexec/java_home -V 2>&1 | grep '^[[:space:]]')
- # unset while loop variables
- unset version path
+
+ # read JDK's from '/usr/libexec/java_home --xml' command with PlistBuddy and a custom Dict iterator
+ # idea: https://stackoverflow.com/a/14085460/1128689 and https://scriptingosx.com/2018/07/parsing-dscl-output-in-scripts/
+ javaXml=$(/usr/libexec/java_home --xml)
+ javaCounter=$(/usr/libexec/PlistBuddy -c "Print" /dev/stdin <<< $javaXml | grep "Dict" | wc -l | tr -d ' ')
+
+ # iterate over all Dict entries
+ # but only if there are any JVMs at all (#93)
+ if [ "$javaCounter" -gt "0" ] ; then
+ for idx in $(seq 0 $((javaCounter - 1)))
+ do
+ version=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMVersion" /dev/stdin <<< $javaXml)
+ path=$(/usr/libexec/PlistBuddy -c "print :$idx:JVMHomePath" /dev/stdin <<< $javaXml)
+ path+="/bin/java"
+ allJVMs+=("$version:$path")
+ done
+ # unset for loop variables
+ unset version path
+ fi
+
+ # add SDKMAN! java versions (#95)
+ if [ -d ~/.sdkman/candidates/java/ ] ; then
+ for sdkjdk in ~/.sdkman/candidates/java/*/
+ do
+ if [[ ${sdkjdk} =~ /current/$ ]] ; then
+ continue
+ fi
+
+ sdkjdkcmd="${sdkjdk}bin/java"
+ version=$(get_java_version_from_cmd "${sdkjdkcmd}")
+ allJVMs+=("$version:$sdkjdkcmd")
+ done
+ # unset for loop variables
+ unset version
+ fi
# add Apple JRE if available
if [ -x "${apple_jre_plugin}" ] ; then
# determine JVMs matching the min/max version requirement
+
+ stub_logger "[JavaSearch] Filtering the result list for JVMs matching the min/max version requirement ..."
+
minC=$(get_comparable_java_version ${JVMVersion})
maxC=$(get_comparable_java_version ${JVMMaxVersion})
matchingJVMs=()
# debug output
for i in "${matchingJVMs[@]}"
do
- stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+ stub_logger "[JavaSearch] ... matches all requirements: $i"
done
stub_logger "[JavaCommand] '$JAVACMD'"
stub_logger "[JavaVersion] $(get_java_version_from_cmd "${JAVACMD}")${JAVACMD_version:+ / $JAVACMD_version}"
+# Make sure tabbing mode is disabled for the selected java version
+
+CFBundleIdentifier=net.java.openjdk.$(get_java_version_from_cmd "${JAVACMD}").java
+if [ x$(defaults read ${CFBundleIdentifier} AppleWindowTabbingMode) != "xnever" ]; then
+ defaults write ${CFBundleIdentifier} AppleWindowTabbingMode never
+fi
if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
stub_logger "[EXIT 3] ${MSG_NO_SUITABLE_JAVA_EXPANDED}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_NO_SUITABLE_JAVA_EXPANDED}\n${MSG_NO_SUITABLE_JAVA_CHECK}\" with title \"${CFBundleName}\" buttons {\" OK \", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 3
# log exit cause
stub_logger "[EXIT 1] ${MSG_ERROR_LAUNCHING}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\"} default button \"${MSG_VISIT_JAVA_DOT_COM}\" with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)" \
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_INSTALL_JAVA}\" with title \"${CFBundleName}\" buttons {\"${MSG_LATER}\", \"${MSG_VISIT_JAVA_DOT_COM}\", \"${MSG_VISIT_ADOPTOPENJDK}\"} default button 1${DialogWithIcon}" \
-e "set response to button returned of the result" \
- -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"http://java.com\""
+ -e "if response is \"${MSG_VISIT_JAVA_DOT_COM}\" then open location \"https://www.java.com/download/\"" \
+ -e "if response is \"${MSG_VISIT_ADOPTOPENJDK}\" then open location \"https://adoptopenjdk.net/releases.html\""
# exit with error
exit 1
fi
# log exit cause
stub_logger "[EXIT 2] ${MSG_MISSING_MAINCLASS}"
# display error message with AppleScript
- osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1 with icon path to resource \"${CFBundleIconFile}\" in bundle (path to me)"
+ osascript -e "tell application \"System Events\" to display dialog \"${MSG_ERROR_LAUNCHING}\n\n${MSG_MISSING_MAINCLASS}\" with title \"${CFBundleName}\" buttons {\" OK \"} default button 1${DialogWithIcon}"
# exit with error
exit 2
fi
# - main class
# - main class arguments
# - passthrough arguments from Terminal or Drag'n'Drop to Finder icon
-stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" -splash:\"${ResourcesFolder}/${JVMSplashFile}\" -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
+stub_logger "[Exec] \"$JAVACMD\" -cp \"${JVMClassPath}\" ${JVMSplashFile:+ -splash:\"${ResourcesFolder}/${JVMSplashFile}\"} -Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" -Xdock:name=\"${CFBundleName}\" ${JVMOptionsArr:+$(printf "'%s' " "${JVMOptionsArr[@]}") }${JVMDefaultOptions:+$JVMDefaultOptions }${JVMMainClass}${MainArgsArr:+ $(printf "'%s' " "${MainArgsArr[@]}")}${ArgsPassthru:+ $(printf "'%s' " "${ArgsPassthru[@]}")}"
exec "${JAVACMD}" \
-Djava.library.path="${AppleJavaFolder}" \
-cp "${JVMClassPath}" \
- -splash:"${ResourcesFolder}/${JVMSplashFile}" \
+ ${JVMSplashFile:+ -splash:"${ResourcesFolder}/${JVMSplashFile}"} \
-Xdock:icon="${ResourcesFolder}/${CFBundleIconFile}" \
-Xdock:name="${CFBundleName}" \
${JVMOptionsArr:+"${JVMOptionsArr[@]}" }\