Merge branch 'branch-1.9' into debian
authorBdale Garbee <bdale@gag.com>
Wed, 9 Jun 2021 04:57:38 +0000 (22:57 -0600)
committerBdale Garbee <bdale@gag.com>
Wed, 9 Jun 2021 04:57:38 +0000 (22:57 -0600)
82 files changed:
ChangeLog
Makefile.am
Releasing
altosdroid/app/build.gradle.in
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosDroidPreferences.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOffline.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosMapOnline.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/AltosUsb.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/DeviceListActivity.java
altosdroid/app/src/main/java/org/altusmetrum/AltosDroid/TelemetryService.java
altoslib/AltosAccelCal.java
altoslib/AltosAdxl375.java
altoslib/AltosCSV.java
altoslib/AltosConfigData.java
altoslib/AltosIMU.java
altoslib/AltosIdleFetch.java
altoslib/AltosIgnite.java
altoslib/AltosLink.java
altoslib/AltosMap.java
altoslib/AltosMapInterface.java
altoslib/AltosMapMark.java
altoslib/AltosMapTransform.java
altoslib/AltosSensorEasyMotor2.java [new file with mode: 0644]
altoslib/Makefile.am
altosui/AltosIdleMonitorUI.java
altosui/AltosIgniteUI.java
altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub
altosui/Makefile.am
altosui/altos-windows.nsi.in
altosui/install-macosx
altosuilib/AltosFlashUI.java
altosuilib/AltosInfoTable.java
altosuilib/AltosUIMap.java
altosuilib/AltosUIMapPreload.java
ao-bringup/test-easymini
ao-bringup/test-easymotor [new file with mode: 0755]
ao-bringup/turnon_easymotor [new file with mode: 0755]
ao-tools/ao-dump-up/ao-dump-up.c
ao-tools/ao-flash/ao-flash-stm32f0x
configure.ac
doc/Makefile.am
doc/altosdroid.inc
doc/altusmetrum-theme.yml
doc/altusmetrum.txt
doc/easymini-release-notes.inc
doc/easymini.txt
doc/header.inc
doc/micropeak.txt
doc/motortest-configuration.inc [new file with mode: 0644]
doc/motortest-installation.inc [new file with mode: 0644]
doc/motortest-intro.inc [new file with mode: 0644]
doc/motortest-operation.inc [new file with mode: 0644]
doc/release-notes-1.9.7.inc [new file with mode: 0644]
doc/release-notes.inc
doc/specs.inc
doc/telegps-release-notes.inc
doc/telegps.txt
doc/telelaunch-configuration.inc
doc/telelaunch.txt
doc/telemetry.txt
doc/updating-firmware.inc
libaltos/altos.dll
libaltos/altos64.dll
libaltos/libaltos_windows.c
micropeak/Makefile.am
micropeak/MicroPeak.app/Contents/MacOS/JavaApplicationStub
micropeak/MicroPeak.java
src/Makefile
src/easymotor-v2/ao_pins.h
src/easytimer-v1/ao_easytimer.c
src/kernel/ao_config.c
src/kernel/ao_config.h
src/kernel/ao_ignite.c
src/kernel/ao_log.c
src/kernel/ao_log_gps.c
src/kernel/ao_storage.c
src/kernel/ao_storage.h
src/microtest/Makefile [new file with mode: 0644]
src/microtest/ao_microtest.c [new file with mode: 0644]
src/microtest/ao_pins.h [new file with mode: 0644]
telegps/TeleGPS.app/Contents/MacOS/JavaApplicationStub

index f7f62ff34fd66c08c8882d6d2da97c5f7ef2ce89..0a092e364b08052c37e5b79c0430d57dd72c0f10 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,653 @@
+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>
index 328d5700cfd1fbbdc74d2b8a8be8ec7d3bc194e2..fa38cf2fcdd1031aa2dba6f957f70b665b5db173 100644 (file)
@@ -53,6 +53,7 @@ fat_altos = \
        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 \
index c29e61c31182da3680380cdae3d308b7b1d3fbe9..b54e236f552eade76a526cf05254ec6ac4b77663 100644 (file)
--- a/Releasing
+++ b/Releasing
@@ -27,6 +27,8 @@ These are Keith's notes on how to do a release
 
        - 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
@@ -59,6 +61,7 @@ These are Bdale's notes on how to do a release.
        - 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
@@ -106,6 +109,7 @@ These are Bdale's notes on how to do a release.
           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} \
@@ -125,6 +129,7 @@ These are Bdale's notes on how to do a release.
           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} \
index 5dbd30ee81b5606178f68dbbd3307936b2a02908..763b1657675f91897755ace5971573949cd6d666 100644 (file)
@@ -18,7 +18,7 @@ android {
     defaultConfig {
         applicationId "org.altusmetrum.AltosDroid"
         minSdkVersion 21
-        targetSdkVersion 28
+        targetSdkVersion 29
         versionCode @ANDROID_VERSION@
         versionName "@VERSION@"
     }
index e564e78478612742fe29eb5112bc16eb77f5c7a5..404b63ace7df8582e90c2e50ecb06ff6c04d1169 100644 (file)
@@ -630,7 +630,7 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener,
                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() {
index f49f11004a30c419da4b311abe36d110064bfce5..4d9204d9ac79c71b210e3a53d54467d999a6aa0f 100644 (file)
@@ -72,6 +72,8 @@ public class AltosDroidPreferences extends AltosPreferences {
        }
 
        public static void set_active_device(DeviceAddress address) {
+               if (backend == null)
+                       return;
                synchronized(backend) {
                        active_device_address = address;
                        if (active_device_address != null) {
@@ -86,6 +88,9 @@ public class AltosDroidPreferences extends AltosPreferences {
        }
 
        public static DeviceAddress active_device() {
+               if (backend == null)
+                       return null;
+
                synchronized(backend) {
                        return active_device_address;
                }
@@ -94,6 +99,8 @@ public class AltosDroidPreferences extends AltosPreferences {
        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);
@@ -107,12 +114,17 @@ public class AltosDroidPreferences extends AltosPreferences {
        }
 
        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>();
@@ -121,18 +133,25 @@ public class AltosDroidPreferences extends AltosPreferences {
        }
 
        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;
@@ -144,12 +163,18 @@ public class AltosDroidPreferences extends AltosPreferences {
 
 
        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;
index cca958c50f873f1f0afbed954f241bebfdfd8bbb..60c209e105cf22fee5cb891be1d008c5cff87657 100644 (file)
@@ -223,12 +223,20 @@ public class AltosMapOffline extends View implements ScaleGestureDetector.OnScal
                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();
        }
index c35bbb4d308c32ae608583ac5fe813ad69a2e3ef..76cd990be7f5b474f38fbddea13f3c8653db1341 100644 (file)
@@ -174,10 +174,12 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke
        }
 
        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) {
@@ -260,22 +262,26 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke
                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) {
@@ -286,13 +292,15 @@ public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarke
        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));
+                               }
                        }
                }
 
index 35514483f026af968a6a31c13bc55f734b4a250b..ad47707d90c9706d3bba397ca384cb66dfac7974 100644 (file)
@@ -210,12 +210,16 @@ public class AltosUsb extends AltosDroidLink {
        }
 
        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;
index 1c3e1dbab21019bd9bfa82423e820a2129720c00..60fba9d367126bec3317e120fd05c0bee3a00381 100644 (file)
@@ -152,11 +152,16 @@ public class DeviceListActivity extends Activity {
        // 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');
index 31616c825371d5e4f2f411343edf4c2464b622b0..2c2095df68186847225e4480993754123be524a2 100644 (file)
@@ -416,7 +416,7 @@ public class TelemetryService extends Service implements AltosIdleMonitorListene
        }
 
        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);
index 38c4182cfef5da2b5fe9cb997722473e0e471b7a..8d0e992710f39cb265dfdbbb421b306f86e99e35 100644 (file)
@@ -181,8 +181,20 @@ public class AltosAccelCal implements Runnable {
                                                  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();
                        }
index 621cceeb723f1e7f5d4ecc38b46de16635455d99..5e4905412a281665137c9c0813c69229658a717d 100644 (file)
@@ -22,7 +22,7 @@ import java.util.concurrent.*;
 
 public class AltosAdxl375 implements Cloneable {
 
-       private int     accel;
+       private int[]   accels = new int[3];
        private int     axis;
 
        public static final int X_AXIS = 0;
@@ -35,7 +35,8 @@ public class AltosAdxl375 implements Cloneable {
                        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;
                        }
                }
@@ -45,22 +46,50 @@ public class AltosAdxl375 implements Cloneable {
        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) {
@@ -68,7 +97,8 @@ public class AltosAdxl375 implements Cloneable {
        }
 
        public AltosAdxl375() {
-               accel = AltosLib.MISSING;
+               for (int i = 0; i < 3; i++)
+                       accels[i] = AltosLib.MISSING;
                axis = AltosLib.MISSING;
        }
 
index 765eb44598f337b6fd9e58da4413c5346a021ebf..16c57ec187104e5f8449f3ba030d496f734bd894 100644 (file)
@@ -408,6 +408,10 @@ public class AltosCSV implements AltosWriter {
                        out.printf(",");
                        write_3d_accel_header();
                }
+               if (has_imu) {
+                       out.printf(",");
+                       write_imu_header();
+               }
                if (has_igniter) {
                        out.printf(",");
                        write_igniter_header();
index 83f3ca07a55dfd35b3541dc70e6c239ae0203ec3..b6105f92fc3cb27265e087fb530d84da2f2f79e8 100644 (file)
@@ -199,19 +199,32 @@ public class AltosConfigData {
                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;
@@ -318,6 +331,9 @@ public class AltosConfigData {
        /* 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:
@@ -335,6 +351,9 @@ public class AltosConfigData {
        /* 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:
@@ -354,10 +373,10 @@ public class AltosConfigData {
         */
        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:
@@ -536,9 +555,6 @@ public class AltosConfigData {
                                }
                        }
                } catch (Exception e) {}
-
-               /* Fix accel cal as soon as all of the necessary values appear */
-               adjust_accel_cal();
        }
 
        public AltosConfigData() {
@@ -636,6 +652,8 @@ public class AltosConfigData {
                                return true;
                        if (product.startsWith("TeleMega-v4"))
                                return true;
+                       if (product.startsWith("EasyMotor-v2"))
+                               return true;
                }
                throw new AltosUnknownProduct(product);
        }
@@ -648,6 +666,9 @@ public class AltosConfigData {
                                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);
        }
@@ -831,8 +852,22 @@ public class AltosConfigData {
                        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)
@@ -893,5 +928,6 @@ public class AltosConfigData {
                        read_link(link, "done");
                        break;
                }
+               adjust_accel_cal();
        }
 }
index 2944a2a5e1131035b74eb21f2bca7a4cf0420406..f3c85e9aa112945f6e8b0c0cb98d1d2927a3e2ae 100644 (file)
@@ -56,7 +56,10 @@ public class AltosIMU implements Cloneable {
        }
 
        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;
@@ -82,7 +85,10 @@ public class AltosIMU implements Cloneable {
        }
 
        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;
@@ -112,7 +118,10 @@ public class AltosIMU implements Cloneable {
        }
 
        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) {
@@ -301,6 +310,15 @@ public class AltosIMU implements Cloneable {
                }
        }
 
+       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;
        }
@@ -318,6 +336,7 @@ public class AltosIMU implements Cloneable {
 
                        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)));
@@ -328,6 +347,14 @@ public class AltosIMU implements Cloneable {
                                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)),
index 1ac075e394c13b01b03658c1a26857dbf053aac4..c4a32788d4617dd319ae190a5fe5667ef510ad7d 100644 (file)
@@ -38,6 +38,7 @@ class AltosIdler {
        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;
@@ -49,6 +50,7 @@ class AltosIdler {
        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) {
@@ -81,7 +83,10 @@ class AltosIdler {
                                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);
@@ -116,6 +121,9 @@ class AltosIdler {
                        case idle_sensor_easytimer1:
                                AltosSensorEasyTimer1.provide_data(listener, link);
                                break;
+                       case idle_sensor_easymotor2:
+                               AltosSensorEasyMotor2.provide_data(listener, link);
+                               break;
                        }
                }
        }
@@ -219,6 +227,9 @@ public class AltosIdleFetch implements AltosDataProvider {
                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;
index d4acda374c6a30de2689e998d20411576052e670..d1ec5104386025bd4d347a03fe9e4e973b702638 100644 (file)
@@ -27,7 +27,8 @@ public class AltosIgnite {
        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;
 
@@ -106,11 +107,14 @@ public class AltosIgnite {
                        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();
@@ -118,6 +122,15 @@ public class AltosIgnite {
                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>();
 
index b713b3dcc9cfebfb0bffc7f145c216b9aea0be79..9346563da1cd5ce230cf235f4131a9c335868449 100644 (file)
@@ -507,6 +507,20 @@ public abstract class AltosLink implements Runnable {
                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();
index 519ecfdf1f31f800a89a02484930e0bae897ff04..3877c7dce7a3631419c1c7e4ac37011c493fc267 100644 (file)
@@ -82,7 +82,7 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
        static final long auto_scroll_delay = 20 * 1000;
 
        public AltosMapTransform        transform;
-       AltosLatLon             centre;
+       public AltosLatLon              centre;
 
        public void reset() {
                // nothing
@@ -289,10 +289,10 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
                        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);
                }
@@ -300,6 +300,10 @@ public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
                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);
        }
index 0ce49d128c4babbad57ddc3d514e579fd38d8203..560f85da20e401e68e78a36609110cdee1c6e7ba 100644 (file)
@@ -30,6 +30,8 @@ public interface AltosMapInterface {
 
        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();
index dcb75430d1bca0df099c107eff583dfa15f46531..0676f54f566b24f38e0b4c0e07b5df651943a317 100644 (file)
@@ -27,13 +27,23 @@ public abstract class AltosMapMark {
 
        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);
        }
 }
index ba9d1ae46121ca5ce72895562dd13ec736e88a38..3f8f290c9f87faa419358de317a3210ea879d583 100644 (file)
@@ -29,6 +29,8 @@ public class AltosMapTransform {
 
        double  offset_x, offset_y;
 
+       int     width, height;
+
        public AltosLatLon lat_lon (AltosPointDouble point) {
                double lat, lon;
                double rads;
@@ -110,6 +112,39 @@ public class AltosMapTransform {
                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() {
@@ -120,6 +155,9 @@ public class AltosMapTransform {
                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);
diff --git a/altoslib/AltosSensorEasyMotor2.java b/altoslib/AltosSensorEasyMotor2.java
new file mode 100644 (file)
index 0000000..b3720a3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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) {
+               }
+       }
+}
+
index 75af5252490281365643d976166c2f722400adb6..9c8c66910c4f86c511399fd0497abc93ec7b3613 100644 (file)
@@ -114,6 +114,7 @@ altoslib_JAVA = \
        AltosSensorMetrum.java \
        AltosSensorTGPS1.java \
        AltosSensorTGPS2.java \
+       AltosSensorEasyMotor2.java \
        AltosState.java \
        AltosStateName.java \
        AltosStringInputStream.java \
index 50994bb27293d55c71e3af9b5311339a49b4ffc6..ba894c71885663a4a2962c719718c73f25c2a369 100644 (file)
@@ -209,7 +209,7 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
 
                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();
index 8256722a513269405ece3db08e16b5c7d8c0a5ca..5a09252ecc14babaa5d101a7758ae3e33ff4d970 100644 (file)
@@ -42,6 +42,7 @@ public class AltosIgniteUI
        ButtonGroup     group;
        Boolean         opened;
 
+       boolean         has_standard;
        int             npyro;
 
        final static int        timeout = 1 * 1000;
@@ -127,7 +128,7 @@ public class AltosIgniteUI
                public void run () {
                        try {
                                ignite = new AltosIgnite(link,
-                                                        !device.matchProduct(Altos.product_altimeter));
+                                                        device.matchProduct(Altos.product_basestation));
 
                        } catch (Exception e) {
                                send_exception(e);
@@ -151,7 +152,7 @@ public class AltosIgniteUI
                                                }
                                                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;
@@ -212,9 +213,11 @@ public class AltosIgniteUI
                } 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();
                }
        }
@@ -417,15 +420,21 @@ public class AltosIgniteUI
 
                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;
index 145a260ba76c07cfcb7dffb469b6191049f4d556..aa96638f5cf9822d1ca1fda58eccc6579903a4f2 100755 (executable)
@@ -1,16 +1,4 @@
 #!/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                                                   #
@@ -23,14 +11,14 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 # @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  #
@@ -52,7 +40,18 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 ##################################################################################
 
-
+#
+# 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()'
 #
@@ -178,6 +177,8 @@ if [ $exitcode -eq 0 ]; then
        JavaFolder="${AppleJavaFolder}"
        ResourcesFolder="${AppleResourcesFolder}"
 
+       # set expandable variables
+       APP_ROOT="${AppPackageFolder}"
        APP_PACKAGE="${AppPackageFolder}"
        JAVAROOT="${AppleJavaFolder}"
        USER_HOME="$HOME"
@@ -192,7 +193,7 @@ if [ $exitcode -eq 0 ]; then
                # 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}")
 
 
@@ -215,7 +216,7 @@ if [ $exitcode -eq 0 ]; then
        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
@@ -225,6 +226,8 @@ if [ $exitcode -eq 0 ]; then
        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')
@@ -232,9 +235,14 @@ if [ $exitcode -eq 0 ]; then
                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...
 
@@ -252,7 +260,11 @@ else
        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')"
@@ -270,12 +282,12 @@ else
        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
@@ -284,8 +296,11 @@ 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'
@@ -299,6 +314,18 @@ else
 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
@@ -309,14 +336,14 @@ fi
 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
@@ -327,14 +354,37 @@ done
 # 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)"
@@ -342,10 +392,12 @@ if [[ $LANG == fr* ]] ; then
        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."
@@ -356,10 +408,12 @@ elif [[ $LANG == de* ]] ; then
        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请联系该应用的开发者。"
@@ -370,10 +424,28 @@ elif [[ $LANG == zh* ]] ; then
        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."
@@ -384,8 +456,10 @@ else
        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
 
 
 
@@ -468,7 +542,7 @@ function get_comparable_java_version() {
 ################################################################################
 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
@@ -501,20 +575,28 @@ if [ -n "$JAVA_HOME" ] ; 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=""
@@ -525,7 +607,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -535,7 +617,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -543,15 +625,41 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
 
        # 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
@@ -571,6 +679,9 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; 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=()
@@ -655,7 +766,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
        # debug output
        for i in "${matchingJVMs[@]}"
        do
-               stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+               stub_logger "[JavaSearch] ... matches all requirements: $i"
        done
 
 
@@ -692,7 +803,13 @@ fi
 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
 
@@ -712,9 +829,10 @@ 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
 
@@ -722,9 +840,10 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -739,7 +858,7 @@ if [ -z "${JVMMainClass}" ]; then
        # 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
@@ -773,11 +892,11 @@ stub_logger "[WorkingDirectory] ${WorkingDirectory}"
 # - 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[@]}" }\
index 78d3ec15b011adad2f5e87976ca1dfe3e36f8bc2..1cbb919f16ce299310014934bbd23e86cd0f46ec 100644 (file)
@@ -146,6 +146,9 @@ FIRMWARE_EMEGA_1_0=$(top_srcdir)/src/easymega-v1.0/easymega-v1.0-$(VERSION).ihx
 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)
 
@@ -160,7 +163,7 @@ FIRMWARE_TFIRE8_1_0=$(top_srcdir)/src/telefireeight-v1.0/telefireeight-v1.0-$(VE
 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
index ad155a2c4ff387aeed0a5bceea335d229cfab34f..196dd7411d9e7f0d75777643c34041391c98d295 100644 (file)
@@ -136,6 +136,7 @@ Section "Firmware"
        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"
index 8f0067e5a030190d3fbb25656a8338361a4b57bf..e97a002ae2e08fb4f7a7219efa86b2c4a87c7ae4 100755 (executable)
@@ -8,6 +8,13 @@ case `id -u` in
     ;;
     *)
        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
@@ -44,4 +51,5 @@ for file in *; do
            ;;
     esac
 done
+open "${LIBRARY}"
 osascript -e 'display dialog "Installation of'"${INSTALLED}"' complete" with title "Installation Complete" buttons {"OK"} default button 1' >/dev/null
index 6b78aea7f69a2706a3e2d439159ec2ecfe42a6f5..3665aaf1e5a5fd472391e054b2238aa86ed623a0 100644 (file)
@@ -80,6 +80,10 @@ public class AltosFlashUI
                "TeleShield"
        };
 
+       private static final String[] log_erased_devices = {
+               "TeleGPS"
+       };
+
        private boolean is_pair_programmed() {
 
                if (file != null) {
@@ -99,6 +103,17 @@ public class AltosFlashUI
                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)
@@ -443,13 +458,20 @@ public class AltosFlashUI
 
        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() {
@@ -467,27 +489,33 @@ public class AltosFlashUI
                                });
                }
 
-               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 (;;) {
@@ -510,6 +538,8 @@ public class AltosFlashUI
                                                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");
@@ -517,6 +547,24 @@ public class AltosFlashUI
                                                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);
 
@@ -569,7 +617,10 @@ public class AltosFlashUI
                                do_exception(fe);
                        } catch (IOException ie) {
                                do_exception (ie);
+                       } catch (TimeoutException te) {
+                               do_exception (te);
                        } catch (InterruptedException ie) {
+                               do_exception (ie);
                        }
                }
 
@@ -614,18 +665,6 @@ public class AltosFlashUI
                        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);
index 226f112f9542dff7c328b91514704bb4931a9bb8..89d453030ddb3bcfd44f86e050e1577261bffaa2 100644 (file)
@@ -275,16 +275,18 @@ public class AltosInfoTable extends JTable implements AltosFlightDisplay, Hierar
                        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) {
index 5f1b7ae6f03429a4db89c39ad38763e59c6e05b6..0a80959fe80a9c02416968f8e35beed860f03f3e 100644 (file)
@@ -48,20 +48,48 @@ public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosM
 
        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) {
@@ -403,6 +431,10 @@ public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosM
                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);
        }
@@ -476,6 +508,10 @@ public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosM
                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();
        }
index 9ba273553885242686840c1f74230ba5798dc400..6d0d8b70576e881cae6ec9473eccad5627acd424 100644 (file)
@@ -139,6 +139,7 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I
        JProgressBar    pbar;
 
        JLabel          site_list_label;
+       java.util.List<AltosLaunchSite> sites;
        JComboBox<AltosLaunchSite>      site_list;
 
        JToggleButton   load_button;
@@ -227,10 +228,29 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I
                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() {
@@ -294,6 +314,7 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I
        }
 
        public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
+               this.sites = sites;
                SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                        int     i = 1;
@@ -301,6 +322,7 @@ public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, I
                                                site_list.insertItemAt(site, i);
                                                i++;
                                        }
+                                       reset_marks();
                                }
                        });
        }
index ddcfcd5457d18b6ea703937687c0c59fbc9d18b8..c4479680120e4d722353bc1834a06535c3f657d7 100755 (executable)
@@ -21,6 +21,8 @@ while [ $found -eq 0 ]; do
                echo -e '\e[34m'Testing $product $serial $dev'\e[39m'
                echo ""
                
+               sleep 0.25
+
                ./test-igniters-nowait "$dev" drogue main
                echo ""
 
@@ -42,7 +44,6 @@ while [ $found -eq 0 ]; do
                    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'
@@ -57,6 +58,5 @@ while [ $found -eq 0 ]; do
     if [ $result -ne 2 ]; then
        exit $result
     fi
-    echo 'No device, sleeping...'
-    sleep 1
+    sleep 0.25
 done
diff --git a/ao-bringup/test-easymotor b/ao-bringup/test-easymotor
new file mode 100755 (executable)
index 0000000..a99ca9d
--- /dev/null
@@ -0,0 +1,40 @@
+#!/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
diff --git a/ao-bringup/turnon_easymotor b/ao-bringup/turnon_easymotor
new file mode 100755 (executable)
index 0000000..306e0ad
--- /dev/null
@@ -0,0 +1,75 @@
+#!/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 $?
index 6866ef592997e7fe24bf9954d5c6fa81c3b56faa..31bae4719127332713d5ab3dd932bd6278507d7d 100644 (file)
@@ -21,6 +21,7 @@
 #include <unistd.h>
 #include <getopt.h>
 #include <string.h>
+#include <stdbool.h>
 #include <ctype.h>
 #include "cc-usb.h"
 #include "cc.h"
@@ -52,6 +53,29 @@ static int get_nonwhite(struct cc_usb *cc, int timeout)
        }
 }
 
+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)
 {
@@ -94,6 +118,7 @@ get_hex(struct cc_usb *cc)
        int     h = (a << 4) + b;
 
        file_crc = log_crc(file_crc, h);
+       check_test(h);
        return h;
 }
 
@@ -191,8 +216,12 @@ main (int argc, char **argv)
        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);
index 45643a4ffbfe15eea251a2c52b57c7537630e735..89e266438693e4f10d5734d6282cffb08543a5c4 100755 (executable)
@@ -10,7 +10,7 @@ trap "rm $cmds" 0 1 15
 file="$1"
 echo "program $file verify reset" > $cmds
 openocd \
-       -f interface/stlink-v2.cfg \
-       -f target/stm32f0x_stlink.cfg \
+       -f interface/stlink.cfg \
+       -f target/stm32f0x.cfg \
        -f $cmds \
        -c shutdown
index 67e846004d9224709aeb0b3f4c4792066b151927..f389393a9d53c4cc7b742cdb76f975cdeffbd5fd 100644 (file)
@@ -18,13 +18,13 @@ dnl
 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'`
index db742953ab568c9c82b5f26309d9313d2808fa4d..560c868f2c367580b0941236115549c586563504 100644 (file)
@@ -17,6 +17,7 @@ FAKETIME=TZ=UTC faketime -f '$(RELEASE_DATE) 00:00:00 i0'
 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 \
@@ -163,7 +164,17 @@ INC_FILES=\
 
 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 \
@@ -244,12 +255,12 @@ ONEFILE_HTML_FILES=$(ONEFILE_TXT_FILES:.txt=.html)
 
 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
 
@@ -308,6 +319,8 @@ map-loading.adoc: $(MAP_SVG_FILES)
 
 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)
@@ -345,7 +358,7 @@ publish-keithp:     am.html $(DOC) $(FONTS)
        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)
index dce81c3bbe65b206b2f9656f35a68f696709f766..08e0b7e9d2a28d14030d66cbbc6f4a335adaa565 100644 (file)
       
        === 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
index f3026222c01088959c2fa10638b0b6fba5463fb0..bed7c141fdb116a17a3520abb3c06d49ca787140 100644 (file)
@@ -51,7 +51,7 @@ footer:
     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
index c2492b0f40e282e96d68f50274b8e8402100deb7..4b7699c5cd95f6fb8df6407655b59bcb0b3a97d3 100644 (file)
@@ -5,7 +5,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>; Bob Finch; Anth
 :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
@@ -21,6 +21,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>; Bob Finch; Anth
 :easymega: 1
 :telegps: 1
 :easytimer: 1
+:easymotor: 1
 :application: AltosUI
 :pdf-stylesdir: .
 :pdf-style: altusmetrum
index 9dd0058e63062aa1116a28525f447e7b4c75dba8..dad66c2883449211272e75569f03be3b74a4bdbf 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.7.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.6.adoc[]
 
index 448f8060c9019c4e92fb0b8b7937499ed068749d..d18b3ee5219955f43b279067d561635db6b318d3 100644 (file)
@@ -3,7 +3,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :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:
index 6bc616f5e9a9b139b27568fbbdebb4bcf6edba43..cb80360bf7c6bd0eb2a1419e4e1f999f8fe22d26 100644 (file)
@@ -7,7 +7,7 @@ endif::[]
 [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]
 
index 18a5afdf7b54673d756c0a51aee2ca447f472c32..afb935d750b9ad2f69164da91f9323b8a06cde01 100644 (file)
@@ -2,7 +2,7 @@
 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:
diff --git a/doc/motortest-configuration.inc b/doc/motortest-configuration.inc
new file mode 100644 (file)
index 0000000..e75cebb
--- /dev/null
@@ -0,0 +1,18 @@
+== 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.
+
diff --git a/doc/motortest-installation.inc b/doc/motortest-installation.inc
new file mode 100644 (file)
index 0000000..290c7b8
--- /dev/null
@@ -0,0 +1,51 @@
+== 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.
diff --git a/doc/motortest-intro.inc b/doc/motortest-intro.inc
new file mode 100644 (file)
index 0000000..fa30cb9
--- /dev/null
@@ -0,0 +1,27 @@
+== 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.
+
diff --git a/doc/motortest-operation.inc b/doc/motortest-operation.inc
new file mode 100644 (file)
index 0000000..e38fbe3
--- /dev/null
@@ -0,0 +1,14 @@
+== 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.
diff --git a/doc/release-notes-1.9.7.inc b/doc/release-notes-1.9.7.inc
new file mode 100644 (file)
index 0000000..61aee84
--- /dev/null
@@ -0,0 +1,29 @@
+= 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
index 2348c4ac8a7a496fbc3a92cfa07df07292b4ad9b..7a4c3348451221608b3c40ca75b8e6c907a0296f 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.7.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.6.adoc[]
 
index 5c2c83cc626e5eccaf68b77b8883d931cdc52693..639a2b9a85d38420ff5de6e85d83064beaca9143 100644 (file)
        |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[]
+
        |===
index 03d3ed76a1955c79e2669538ddf254a2c3042da4..c87b10e45205a3aa52b2f59310bf5d40b4e6a730 100644 (file)
@@ -1,5 +1,9 @@
 [appendix]
 == Release Notes
+       :leveloffset: 2
+       include::release-notes-1.9.7.adoc[]
+
+       <<<<
        :leveloffset: 2
        include::release-notes-1.9.6.adoc[]
 
index b8911a8a51197d4370734f5844f4ae1aba5694f3..6db284768df230cc75e36e00c27b2d425dceef92 100644 (file)
@@ -3,7 +3,7 @@ Keith Packard <keithp@keithp.com>; Bdale Garbee <bdale@gag.com>
 :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:
index 0317613cae8340033759a4d5c9554bad10f52241..a24776331a09818cece9e6cdfb77b7e78a1a0ec0 100644 (file)
@@ -59,7 +59,7 @@
                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
index 7522b9837da8f58b3241cf84818b402e322e5f0a..89f98dd451eca44d437c311e25ae45108da4f3c1 100644 (file)
@@ -5,8 +5,7 @@ Bdale Garbee <bdale@gag.com>
 :revdate: 01 Jan 1970
 :icons:
 :icontype: svg
-:revremark: initial draft
-:copyright: Bdale Garbee 2020
+:copyright: Bdale Garbee 2021
 :doctype: book
 :numbered:
 :stylesheet: am.css
index 4152de33a78f97ddcbd1fe6c38b0c59ae1426422..0df96abdd2b199efd72bd4937f460f7fff728ccb 100644 (file)
@@ -2,7 +2,7 @@
 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
index b9216a2235288d58a8c50705d18b40a6b9b2101c..e2e1388a74c4bcda591cb6985c75e1ae5d0aa4b9 100644 (file)
        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.
@@ -28,7 +37,7 @@
 
        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[]
@@ -44,7 +53,8 @@
                  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.
 
index ed0e4772f9a59dc10bb8d7e5cbf2199dbde4e864..3a3aca0e3092399dd5b29c645554eb8689712882 100755 (executable)
Binary files a/libaltos/altos.dll and b/libaltos/altos.dll differ
index e0edc1df3d21ec04d101454baa94874f038e3170..c83c59e9293cf09f4365a583cdb320f8b65fa1a6 100755 (executable)
Binary files a/libaltos/altos64.dll and b/libaltos/altos64.dll differ
index b92df70857fb03b1bbf61220cd91608eb307051d..43e347c40cb70ce96bb0bbf9dd19875a6e551881 100644 (file)
@@ -135,6 +135,7 @@ static struct {
        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 },
index 816dd4b3414ede0dae87e02efb540f57fd07f208..9a0328d7a2e2fbf44e5dc3e1260fcda3619cc904 100644 (file)
@@ -315,7 +315,8 @@ $(MACOSX_DIST): $(MACOSX_FILES)
        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
index 145a260ba76c07cfcb7dffb469b6191049f4d556..aa96638f5cf9822d1ca1fda58eccc6579903a4f2 100755 (executable)
@@ -1,16 +1,4 @@
 #!/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                                                   #
@@ -23,14 +11,14 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 # @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  #
@@ -52,7 +40,18 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 ##################################################################################
 
-
+#
+# 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()'
 #
@@ -178,6 +177,8 @@ if [ $exitcode -eq 0 ]; then
        JavaFolder="${AppleJavaFolder}"
        ResourcesFolder="${AppleResourcesFolder}"
 
+       # set expandable variables
+       APP_ROOT="${AppPackageFolder}"
        APP_PACKAGE="${AppPackageFolder}"
        JAVAROOT="${AppleJavaFolder}"
        USER_HOME="$HOME"
@@ -192,7 +193,7 @@ if [ $exitcode -eq 0 ]; then
                # 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}")
 
 
@@ -215,7 +216,7 @@ if [ $exitcode -eq 0 ]; then
        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
@@ -225,6 +226,8 @@ if [ $exitcode -eq 0 ]; then
        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')
@@ -232,9 +235,14 @@ if [ $exitcode -eq 0 ]; then
                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...
 
@@ -252,7 +260,11 @@ else
        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')"
@@ -270,12 +282,12 @@ else
        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
@@ -284,8 +296,11 @@ 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'
@@ -299,6 +314,18 @@ else
 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
@@ -309,14 +336,14 @@ fi
 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
@@ -327,14 +354,37 @@ done
 # 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)"
@@ -342,10 +392,12 @@ if [[ $LANG == fr* ]] ; then
        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."
@@ -356,10 +408,12 @@ elif [[ $LANG == de* ]] ; then
        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请联系该应用的开发者。"
@@ -370,10 +424,28 @@ elif [[ $LANG == zh* ]] ; then
        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."
@@ -384,8 +456,10 @@ else
        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
 
 
 
@@ -468,7 +542,7 @@ function get_comparable_java_version() {
 ################################################################################
 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
@@ -501,20 +575,28 @@ if [ -n "$JAVA_HOME" ] ; 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=""
@@ -525,7 +607,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -535,7 +617,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -543,15 +625,41 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
 
        # 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
@@ -571,6 +679,9 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; 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=()
@@ -655,7 +766,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
        # debug output
        for i in "${matchingJVMs[@]}"
        do
-               stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+               stub_logger "[JavaSearch] ... matches all requirements: $i"
        done
 
 
@@ -692,7 +803,13 @@ fi
 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
 
@@ -712,9 +829,10 @@ 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
 
@@ -722,9 +840,10 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -739,7 +858,7 @@ if [ -z "${JVMMainClass}" ]; then
        # 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
@@ -773,11 +892,11 @@ stub_logger "[WorkingDirectory] ${WorkingDirectory}"
 # - 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[@]}" }\
index 7d3e20d8d1a4f308692ebe9158582a370aa408be..c34e874aaaa05b2708a17ef2f9c20ae2b65f143c 100644 (file)
@@ -61,6 +61,10 @@ public class MicroPeak extends MicroFrame implements ActionListener, ItemListene
        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) {
@@ -255,7 +259,7 @@ public class MicroPeak extends MicroFrame implements ActionListener, ItemListene
        }
 
 
-       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) {
@@ -267,7 +271,6 @@ public class MicroPeak extends MicroFrame implements ActionListener, ItemListene
                        add_menu(menu, items[i][0], items[i][1]);
                }
                menu_bar.add(menu);
-               return menu;
        }
 
        public MicroPeak() {
@@ -286,12 +289,16 @@ public class MicroPeak extends MicroFrame implements ActionListener, ItemListene
                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() {
@@ -320,6 +327,11 @@ public class MicroPeak extends MicroFrame implements ActionListener, ItemListene
                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);
        }
index 3443ec8ac394cbd5f630f7d678e23f5dc9239189..0b6a7e278b020c45cf4fa1f105d2947a527b411b 100644 (file)
@@ -19,6 +19,7 @@ include Makedefs
 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 \
index 1d970e1927df605d35743c0539f237ee743422b5..097ecb4551fad8c432d0c9b0b5d34555bf073fd6 100644 (file)
@@ -53,7 +53,7 @@
 #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
index 8224ee06daa4aa2b1796e574c11ac59ed193f1b7..55dd4db0f62f7f6ae694b2260acee1e67c160dee 100644 (file)
@@ -18,7 +18,7 @@
 
 #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>
index 24eb79d9aaeba53b2fe2db62d93929569e5633d3..bfc0325e00d9406c59c51cc2d7fd9a23c8682310 100644 (file)
@@ -190,7 +190,7 @@ _ao_config_get(void)
                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;
@@ -395,7 +395,7 @@ ao_config_accel_calibrate_show(void)
 {
        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,
@@ -406,7 +406,7 @@ ao_config_accel_calibrate_show(void)
 #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;
@@ -418,7 +418,7 @@ ao_config_accel_calibrate_auto(char *orientation)
        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;
@@ -436,7 +436,7 @@ ao_config_accel_calibrate_auto(char *orientation)
                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]);
@@ -445,7 +445,7 @@ ao_config_accel_calibrate_auto(char *orientation)
                        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;
@@ -457,35 +457,31 @@ static void
 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",
@@ -495,11 +491,25 @@ ao_config_accel_calibrate_set(void)
        _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();
index 3cfd0d0731574c71599c5946e012a6f0d6c642bc..8a367733ee00e523ef0eb115767af64073a0cd6a 100644 (file)
@@ -92,7 +92,7 @@ struct ao_config {
 #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 */
index e4e4843e1cb8383ba1597699988fc0c67a57bf6f..d197239a991e60db1ce9d90b38b8611335e4b184 100644 (file)
@@ -75,7 +75,7 @@ ao_igniter_status(enum ao_igniter igniter)
 #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;
@@ -93,7 +93,8 @@ ao_igniter_fire(enum ao_igniter igniter)
                        break;
                }
                ao_ignition[igniter].firing = 0;
-               ao_delay(AO_IGNITER_CHARGE_TIME);
+               if (wait)
+                       ao_delay(AO_IGNITER_CHARGE_TIME);
        }
 }
 
@@ -111,27 +112,27 @@ ao_igniter(void)
                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;
                }
        }
@@ -149,12 +150,12 @@ ao_ignite_manual(void)
 #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
index f0816aeeeb07365836c29627ccd84ed10c275660..2167d145d2ecd7211832ce01879fcd68bcc3bb17 100644 (file)
@@ -292,28 +292,28 @@ ao_log_scan(void)
        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;
index bf326c1a7fb972b7e0d601adfaab767b806586a0..2b45f35e06ba11b07a35f3d4c84f791e6336606a 100644 (file)
@@ -81,18 +81,31 @@ ao_log_gps_tracking(uint16_t tick, struct ao_telemetry_satellite *gps_tracking_d
        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;
 }
index 213ec2d6872825cda21173ff15a13bfd8d6a5a6b..43abc43c1ec9e24396aff9a0c5dcc13dce44e386 100644 (file)
@@ -86,10 +86,6 @@ ao_storage_write(ao_pos_t pos, void *v_buf, uint16_t len)
        return 1;
 }
 
-#ifndef AO_STORAGE_ERASED_BYTE
-#define AO_STORAGE_ERASED_BYTE 0xff
-#endif
-
 uint8_t
 ao_storage_is_erased(uint32_t pos)
 {
index 026074b5d2db4c2bd30768a2e1fcd2de780bc636..ff8548eb150d71bb73a92ddf4457e7079a7a760f 100644 (file)
@@ -44,6 +44,10 @@ extern ao_pos_t      ao_storage_block;
 #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;
diff --git a/src/microtest/Makefile b/src/microtest/Makefile
new file mode 100644 (file)
index 0000000..04c31b0
--- /dev/null
@@ -0,0 +1,81 @@
+#
+# 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)
diff --git a/src/microtest/ao_microtest.c b/src/microtest/ao_microtest.c
new file mode 100644 (file)
index 0000000..168220c
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * 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();
+       }
+}
diff --git a/src/microtest/ao_pins.h b/src/microtest/ao_pins.h
new file mode 100644 (file)
index 0000000..2c02f3a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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_ */
index 145a260ba76c07cfcb7dffb469b6191049f4d556..aa96638f5cf9822d1ca1fda58eccc6579903a4f2 100755 (executable)
@@ -1,16 +1,4 @@
 #!/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                                                   #
@@ -23,14 +11,14 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 # @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  #
@@ -52,7 +40,18 @@ export FREETYPE_PROPERTIES=truetype:interpreter-version=35
 #                                                                                #
 ##################################################################################
 
-
+#
+# 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()'
 #
@@ -178,6 +177,8 @@ if [ $exitcode -eq 0 ]; then
        JavaFolder="${AppleJavaFolder}"
        ResourcesFolder="${AppleResourcesFolder}"
 
+       # set expandable variables
+       APP_ROOT="${AppPackageFolder}"
        APP_PACKAGE="${AppPackageFolder}"
        JAVAROOT="${AppleJavaFolder}"
        USER_HOME="$HOME"
@@ -192,7 +193,7 @@ if [ $exitcode -eq 0 ]; then
                # 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}")
 
 
@@ -215,7 +216,7 @@ if [ $exitcode -eq 0 ]; then
        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
@@ -225,6 +226,8 @@ if [ $exitcode -eq 0 ]; then
        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')
@@ -232,9 +235,14 @@ if [ $exitcode -eq 0 ]; then
                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...
 
@@ -252,7 +260,11 @@ else
        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')"
@@ -270,12 +282,12 @@ else
        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
@@ -284,8 +296,11 @@ 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'
@@ -299,6 +314,18 @@ else
 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
@@ -309,14 +336,14 @@ fi
 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
@@ -327,14 +354,37 @@ done
 # 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)"
@@ -342,10 +392,12 @@ if [[ $LANG == fr* ]] ; then
        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."
@@ -356,10 +408,12 @@ elif [[ $LANG == de* ]] ; then
        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请联系该应用的开发者。"
@@ -370,10 +424,28 @@ elif [[ $LANG == zh* ]] ; then
        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."
@@ -384,8 +456,10 @@ else
        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
 
 
 
@@ -468,7 +542,7 @@ function get_comparable_java_version() {
 ################################################################################
 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
@@ -501,20 +575,28 @@ if [ -n "$JAVA_HOME" ] ; 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=""
@@ -525,7 +607,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -535,7 +617,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -543,15 +625,41 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
 
        # 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
@@ -571,6 +679,9 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; 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=()
@@ -655,7 +766,7 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
        # debug output
        for i in "${matchingJVMs[@]}"
        do
-               stub_logger "[JavaSearch] ... ... matches all requirements: $i"
+               stub_logger "[JavaSearch] ... matches all requirements: $i"
        done
 
 
@@ -692,7 +803,13 @@ fi
 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
 
@@ -712,9 +829,10 @@ 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
 
@@ -722,9 +840,10 @@ if [ -z "${JAVACMD}" ] || [ ! -x "${JAVACMD}" ] ; then
                # 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
@@ -739,7 +858,7 @@ if [ -z "${JVMMainClass}" ]; then
        # 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
@@ -773,11 +892,11 @@ stub_logger "[WorkingDirectory] ${WorkingDirectory}"
 # - 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[@]}" }\