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