From 558a143315f0345b79cc4f22aa4b643fedcded9c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 11 Aug 2017 23:42:53 -0400 Subject: altosui: Accel calibration UI Provides a GUI for re-calibrating accelerometers Signed-off-by: Keith Packard --- altoslib/AltosAccelCal.java | 217 ++++++++++++++++++++++++++++++++++++ altoslib/AltosAccelCalListener.java | 34 ++++++ altoslib/AltosConfigData.java | 10 +- altoslib/AltosConfigValues.java | 10 ++ altoslib/AltosLink.java | 19 ++++ altoslib/Makefile.am | 2 + 6 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 altoslib/AltosAccelCal.java create mode 100644 altoslib/AltosAccelCalListener.java (limited to 'altoslib') diff --git a/altoslib/AltosAccelCal.java b/altoslib/AltosAccelCal.java new file mode 100644 index 00000000..03d9fbf2 --- /dev/null +++ b/altoslib/AltosAccelCal.java @@ -0,0 +1,217 @@ +/* + * Copyright © 2017 Keith Packard + * + * 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 3 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.altusmetrum.altoslib_12; + +import java.io.*; +import java.util.concurrent.*; + +public class AltosAccelCal implements Runnable { + + AltosLink link; + AltosAccelCalListener listener; + + boolean remote; + boolean close_on_exit; + double frequency; + String callsign; + + Thread accel_thread; + + AltosConfigData config_data; + + public static final int phase_antenna_up = 0; + public static final int phase_antenna_down = 1; + + void start_link() throws InterruptedException, TimeoutException { + if (remote) { + link.set_radio_frequency(frequency); + link.set_callsign(callsign); + link.start_remote(); + } else + link.flush_input(); + } + + boolean stop_link() throws InterruptedException, TimeoutException { + if (remote) + link.stop_remote(); + return link.reply_abort; + } + + public void set_frequency(double in_frequency) { + frequency = in_frequency; + link.abort_reply(); + } + + public void set_callsign(String in_callsign) { + callsign = in_callsign; + link.abort_reply(); + } + + public void abort() throws InterruptedException { + while (accel_thread.isAlive()) { + accel_thread.interrupt(); + link.abort_reply(); + Thread.sleep(100); + } + accel_thread.join(); + } + + static private final String press_msg = "press a key..."; + + private Semaphore ui_signal_semaphore; + private boolean ui_signal_reply; + + public void signal(boolean reply) { + System.out.printf("Signal cal semaphore %b\n", reply); + ui_signal_reply = reply; + ui_signal_semaphore.release(); + } + + private boolean wait_signal() throws InterruptedException { + System.out.printf("\twait for cal signal...\n"); + ui_signal_semaphore.acquire(); + System.out.printf("\tgot cal signal %b\n", ui_signal_reply); + return ui_signal_reply; + } + + private boolean wait_press(int timeout) throws InterruptedException { + for (;;) { + String line = link.get_reply(timeout); + if (line == null) { + System.out.printf("get_reply timeout\n"); + return false; + } + System.out.printf("got line %s\n", line); + if (line.contains(press_msg)) + return true; + if (line.contains("Invalid")) + return false; + if (line.contains("Syntax")) + return false; + if (line.contains("Calibrating")) + listener.message(this, line); + } + } + + static final int cal_timeout = 20 * 1000; + + public void run() { + System.out.printf("start accel cal procedure\n"); + try { + AltosConfigData new_config = null; + + try { + start_link(); + config_data = link.config_data(); + + /* set back to antenna up for calibration */ + if (config_data.pad_orientation != 0) + link.printf("c o 0\n"); + + /* Start calibration */ + try { + System.out.printf("*** start cal\n"); + link.set_match(press_msg); + link.printf("c a 0\n"); + System.out.printf("*** wait press\n"); + if (!wait_press(cal_timeout)) + throw new TimeoutException("timeout"); + System.out.printf("*** set_phase antenna_up\n"); + listener.set_phase(this, phase_antenna_up); + System.out.printf("*** wait_signal\n"); + if (!wait_signal()) + throw new InterruptedException("aborted"); + link.set_match(press_msg); + System.out.printf("*** send newline\n"); + link.printf("\n"); + System.out.printf("*** wait press\n"); + if (!wait_press(cal_timeout)) + throw new TimeoutException("timeout"); + System.out.printf("***set_phase antenna_down\n"); + listener.set_phase(this, phase_antenna_down); + System.out.printf("*** wait_signal\n"); + if (!wait_signal()) + throw new InterruptedException("aborted"); + System.out.printf("*** send newline and version command\n"); + link.printf("\nv\n"); + } catch (TimeoutException e) { + throw e; + } catch (InterruptedException e) { + throw e; + } + link.set_match(null); + + boolean worked = true; + for (;;) { + String line = link.get_reply(cal_timeout); + if (line == null) + throw new TimeoutException(); + System.out.printf("*** waiting for finish: %s\n", line); + if (line.contains("Invalid")) + worked = false; + if (line.contains("software-version")) + break; + if (line.contains("Calibrating")) + listener.message(this, line); + } + System.out.printf("*** worked: %b\n", worked); + if (worked) + new_config = new AltosConfigData(link); + } finally { + System.out.printf("Restore orientation %d +g %d -g %d\n", + config_data.pad_orientation, + config_data.accel_cal_plus, + config_data.accel_cal_minus); + if (config_data.pad_orientation != AltosLib.MISSING && config_data.pad_orientation != 0) + link.printf("c o %d\n", config_data.pad_orientation); + if (config_data.accel_cal_plus != AltosLib.MISSING && config_data.accel_cal_minus != AltosLib.MISSING) + link.printf("c a %d %d\n", + config_data.accel_cal_plus, config_data.accel_cal_minus); + stop_link(); + } + if (new_config != null) { + System.out.printf("*** +1g %d -1g %d\n", + new_config.accel_cal_plus, + new_config.accel_cal_minus); + listener.cal_done(this, new_config.accel_cal_plus, new_config.accel_cal_minus); + if (!wait_signal()) + throw new InterruptedException("aborted"); + } else + listener.error(this, "Calibration failed"); + } catch (TimeoutException te) { + System.out.printf("timeout"); + listener.error(this, "timeout"); + } catch (InterruptedException ie) { + System.out.printf("interrupted\n"); + listener.error(this, "interrupted"); + } + } + + public void start() { + accel_thread = new Thread(this); + listener.set_thread(this, accel_thread); + accel_thread.start(); + } + + public AltosAccelCal(AltosLink link, AltosAccelCalListener listener) { + this.link = link; + this.listener = listener; + ui_signal_semaphore = new Semaphore(0); + } +} diff --git a/altoslib/AltosAccelCalListener.java b/altoslib/AltosAccelCalListener.java new file mode 100644 index 00000000..17359245 --- /dev/null +++ b/altoslib/AltosAccelCalListener.java @@ -0,0 +1,34 @@ +/* + * Copyright © 2017 Keith Packard + * + * 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 3 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., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.altusmetrum.altoslib_12; + +import java.io.*; +import java.util.concurrent.*; + +public interface AltosAccelCalListener { + public void set_thread(AltosAccelCal cal, Thread thread); + + public void set_phase(AltosAccelCal cal, int phase); + + public void cal_done(AltosAccelCal cal, int plus, int minus); + + public void error(AltosAccelCal cal, String msg); + + public void message(AltosAccelCal cal, String msg); +} diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index 63c34310..d85f735b 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -525,6 +525,12 @@ public class AltosConfigData { if (pad_orientation != AltosLib.MISSING) pad_orientation = source.pad_orientation(); + if (accel_cal_plus != AltosLib.MISSING) + accel_cal_plus = source.accel_cal_plus(); + + if (accel_cal_minus != AltosLib.MISSING) + accel_cal_minus = source.accel_cal_minus(); + /* HAS_LOG */ if (flight_log_max != AltosLib.MISSING) flight_log_max = source.flight_log_max(); @@ -592,6 +598,7 @@ public class AltosConfigData { dest.set_flight_log_max(flight_log_max); dest.set_ignite_mode(ignite_mode); dest.set_pad_orientation(pad_orientation); + dest.set_accel_cal(accel_cal_plus, accel_cal_minus); dest.set_callsign(callsign); if (npyro != AltosLib.MISSING) dest.set_pyros(pyros); @@ -669,9 +676,10 @@ public class AltosConfigData { link.printf("c e %d\n", radio_enable); /* HAS_ACCEL */ - /* UI doesn't support accel cal */ if (pad_orientation != AltosLib.MISSING) link.printf("c o %d\n", pad_orientation); + if (accel_cal_plus != AltosLib.MISSING && accel_cal_minus != AltosLib.MISSING) + link.printf("c a %d %d\n", accel_cal_plus, accel_cal_minus); /* HAS_LOG */ if (flight_log_max != 0) diff --git a/altoslib/AltosConfigValues.java b/altoslib/AltosConfigValues.java index 170b1112..10478fd4 100644 --- a/altoslib/AltosConfigValues.java +++ b/altoslib/AltosConfigValues.java @@ -74,6 +74,16 @@ public interface AltosConfigValues { public abstract int pad_orientation(); + public abstract void set_accel_cal(int accel_cal_plus, int accel_cal_minus); + + public abstract int accel_cal_plus(); + + public abstract int accel_cal_minus(); + + public abstract void set_dirty(); + + public abstract void set_clean(); + public abstract void set_pyros(AltosPyro[] new_pyros); public abstract AltosPyro[] pyros() throws AltosConfigDataException; diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java index 5a802ef1..5413de9d 100644 --- a/altoslib/AltosLink.java +++ b/altoslib/AltosLink.java @@ -43,6 +43,8 @@ public abstract class AltosLink implements Runnable { public LinkedBlockingQueue reply_queue = new LinkedBlockingQueue(); public LinkedBlockingQueue binary_queue = new LinkedBlockingQueue(); + private String match_string = null; + public synchronized void add_monitor(LinkedBlockingQueue q) { set_monitor(true); monitors.add(q); @@ -112,6 +114,15 @@ public abstract class AltosLink implements Runnable { private int len_read = 0; + private boolean match_bytes(byte[] bytes, int byte_count, String match) { + if (byte_count < match.length()) + return false; + String line = new String(bytes, 0, byte_count, AltosLib.unicode_set); + if (line == null) + return false; + return line.indexOf(match) >= 0; + } + public void run () { int c; byte[] line_bytes = null; @@ -159,6 +170,11 @@ public abstract class AltosLink implements Runnable { line_count = 0; len_read = 0; } + if (match_string != null && match_bytes(line_bytes, line_count, match_string)) { + match_string = null; + add_bytes(line_bytes, line_count); + line_count = 0; + } } } } @@ -166,6 +182,9 @@ public abstract class AltosLink implements Runnable { } } + public void set_match(String match) { + match_string = match; + } public String get_reply(int timeout) throws InterruptedException { boolean can_cancel = can_cancel_reply(); diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am index 11b5d562..08af9496 100644 --- a/altoslib/Makefile.am +++ b/altoslib/Makefile.am @@ -26,6 +26,8 @@ record_files = \ altoslib_JAVA = \ AltosLib.java \ + AltosAccelCal.java \ + AltosAccelCalListener.java \ AltosCalData.java \ AltosCompanion.java \ AltosConfigData.java \ -- cgit v1.2.3 From b48966c3121e1b5d4a659bebcad595d6f1ec5ee5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 12 Sep 2017 13:33:13 -0700 Subject: altoslib: Correctly parse IMU cal data Was trying to match 'IMU call along' instead of 'IMU cal along', causing the line to not match and losing the IMU accel cal values. Signed-off-by: Keith Packard --- altoslib/AltosConfigData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'altoslib') diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index d85f735b..dc036867 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -405,7 +405,7 @@ public class AltosConfigData { /* HAS_GYRO */ try { - if (line.startsWith("IMU call along")) { + if (line.startsWith("IMU cal along")) { String[] bits = line.split("\\s+"); if (bits.length >= 8) { accel_zero_along = Integer.parseInt(bits[3]); -- cgit v1.2.3 From d84f5e576e83a2b965d0e9752a9bbef100954815 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 12 Sep 2017 13:34:23 -0700 Subject: altoslib: Remove debug stack dump from AltosState An empty AltosState is allocated when parsing JSON data; it's not a mistake. Signed-off-by: Keith Packard --- altoslib/AltosState.java | 1 - 1 file changed, 1 deletion(-) (limited to 'altoslib') diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java index 9ee3d57d..39ab10da 100644 --- a/altoslib/AltosState.java +++ b/altoslib/AltosState.java @@ -1060,7 +1060,6 @@ public class AltosState extends AltosDataListener { } public AltosState() { - Thread.dumpStack(); init(); } -- cgit v1.2.3 From 4151c30e1294c0dda3aa02e7ac23b9616f25a4d0 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 12 Sep 2017 13:35:25 -0700 Subject: altoslib: Make receive frequency available in telem data This lets AltosDroid record which frequency each station was heard on. Signed-off-by: Keith Packard --- altoslib/AltosDataListener.java | 5 +++++ altoslib/AltosTelemetry.java | 9 +++++++++ 2 files changed, 14 insertions(+) (limited to 'altoslib') diff --git a/altoslib/AltosDataListener.java b/altoslib/AltosDataListener.java index 5f89b3e4..be6d840f 100644 --- a/altoslib/AltosDataListener.java +++ b/altoslib/AltosDataListener.java @@ -20,6 +20,7 @@ public abstract class AltosDataListener { public double time = AltosLib.MISSING; public int state = AltosLib.MISSING; + public double frequency = AltosLib.MISSING; public void set_tick(int tick) { cal_data.set_tick(tick); @@ -55,6 +56,10 @@ public abstract class AltosDataListener { cal_data().set_flight(flight); } + public void set_frequency(double frequency) { + this.frequency = frequency; + } + /* Called after all records are captured */ public void finish() { } diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java index 7d576942..f17e1171 100644 --- a/altoslib/AltosTelemetry.java +++ b/altoslib/AltosTelemetry.java @@ -38,6 +38,9 @@ public abstract class AltosTelemetry implements AltosDataProvider { /* Mark when we received the packet */ long received_time; + /* Mark frequency packet was received on */ + public double frequency = AltosLib.MISSING; + static boolean cksum(int[] bytes) { int sum = 0x5a; for (int i = 1; i < bytes.length - 1; i++) @@ -50,6 +53,8 @@ public abstract class AltosTelemetry implements AltosDataProvider { listener.set_serial(serial()); if (listener.state == AltosLib.ao_flight_invalid) listener.set_state(AltosLib.ao_flight_startup); + if (frequency != AltosLib.MISSING) + listener.set_frequency(frequency); listener.set_tick(tick()); listener.set_rssi(rssi(), status()); listener.set_received_time(received_time); @@ -108,6 +113,10 @@ public abstract class AltosTelemetry implements AltosDataProvider { return telem; } + public void set_frequency(double frequency) { + this.frequency = frequency; + } + public AltosTelemetry() { this.received_time = System.currentTimeMillis(); } -- cgit v1.2.3 From 4f2cbe0c537c9f417aae310cc3b89f84e0915103 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 18 Sep 2017 09:57:41 -0700 Subject: altoslib: Avoid crashing when computing stats for empty flight logs Signed-off-by: Keith Packard --- altoslib/AltosFlightStats.java | 20 ++++++++++++-------- altoslib/AltosTimeSeries.java | 8 ++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'altoslib') diff --git a/altoslib/AltosFlightStats.java b/altoslib/AltosFlightStats.java index c2e4e2a3..ea1a9675 100644 --- a/altoslib/AltosFlightStats.java +++ b/altoslib/AltosFlightStats.java @@ -212,8 +212,11 @@ public class AltosFlightStats { add_times(series, (int) prev.value, prev.time, state.time); prev = state; } - if (prev != null) - add_times(series, (int) prev.value, prev.time, series.accel_series.last().time); + if (prev != null) { + AltosTimeValue last_accel = series.accel_series.last(); + if (last_accel != null) + add_times(series, (int) prev.value, prev.time, last_accel.time); + } } for (int s = 0; s <= AltosLib.ao_flight_invalid; s++) { @@ -245,14 +248,15 @@ public class AltosFlightStats { has_gps = true; lat = pad_lat = gps.lat; lon = pad_lon = gps.lon; - for (AltosGPSTimeValue gtv : series.gps_series) { - gps = gtv.gps; - if (gps.locked && gps.nsat >= 4) { - lat = gps.lat; - lon = gps.lon; + if (series.gps_series != null) { + for (AltosGPSTimeValue gtv : series.gps_series) { + gps = gtv.gps; + if (gps.locked && gps.nsat >= 4) { + lat = gps.lat; + lon = gps.lon; + } } } - } max_height = AltosLib.MISSING; diff --git a/altoslib/AltosTimeSeries.java b/altoslib/AltosTimeSeries.java index b3c432fc..9f3b4d80 100644 --- a/altoslib/AltosTimeSeries.java +++ b/altoslib/AltosTimeSeries.java @@ -151,11 +151,15 @@ public class AltosTimeSeries implements Iterable, Comparable 0) + return values.get(0); + return null; } public AltosTimeValue last() { - return values.get(values.size() - 1); + if (values.size() > 0) + return values.get(values.size() - 1); + return null; } public double average() { -- cgit v1.2.3