From ae1174317fc476e39077f7dc257ec08709c6b301 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 10 Jun 2014 10:11:03 -0700 Subject: [PATCH] altoslib/altosui/telegps: Change log size configuration * Use new log-space value provided by firmware when available. * Divide that up into 1-8 flights and offer those sizes as options to the user instead of a fixed set of sizes. * Show how many flights each selection will store * This also checks values provided by the user Signed-off-by: Keith Packard --- altoslib/AltosConfigData.java | 38 +++--- altosui/AltosConfig.java | 24 ++-- altosui/AltosConfigUI.java | 72 ++++++---- telegps/Makefile.am | 3 +- telegps/TeleGPS.java | 4 +- telegps/TeleGPSConfig.java | 24 ++-- telegps/TeleGPSConfigUI.java | 64 +++++---- telegps/TeleGPSDisplayThread.java | 209 ++++++++++++++++++++++++++++++ 8 files changed, 345 insertions(+), 93 deletions(-) create mode 100644 telegps/TeleGPSDisplayThread.java diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java index 9292a5a2..9462ae6f 100644 --- a/altoslib/AltosConfigData.java +++ b/altoslib/AltosConfigData.java @@ -29,6 +29,7 @@ public class AltosConfigData implements Iterable { public int serial; public int flight; public int log_format; + public int log_space; public String version; /* Strings returned */ @@ -124,6 +125,22 @@ public class AltosConfigData implements Iterable { return lines.iterator(); } + public int log_space() { + if (log_space > 0) + return log_space; + + if (storage_size > 0) { + int space = storage_size; + + if (storage_erase_unit > 0 && use_flash_for_config()) + space -= storage_erase_unit; + + if (space > 0) + return space; + } + return 0; + } + public int log_available() { switch (log_format) { case AltosLib.AO_LOG_FORMAT_TINY: @@ -137,7 +154,7 @@ public class AltosConfigData implements Iterable { if (flight_log_max <= 0) return 1; int log_max = flight_log_max * 1024; - int log_space = storage_size - storage_erase_unit; + int log_space = log_space(); int log_used; if (stored_flight <= 0) @@ -202,6 +219,7 @@ public class AltosConfigData implements Iterable { serial = 0; flight = 0; log_format = AltosLib.AO_LOG_FORMAT_UNKNOWN; + log_space = -1; version = "unknown"; main_deploy = -1; @@ -247,6 +265,7 @@ public class AltosConfigData implements Iterable { try { serial = get_int(line, "serial-number"); } catch (Exception e) {} try { flight = get_int(line, "current-flight"); } catch (Exception e) {} try { log_format = get_int(line, "log-format"); } catch (Exception e) {} + try { log_space = get_int(line, "log-space"); } catch (Exception e) {} try { version = get_string(line, "software-version"); } catch (Exception e) {} /* Version also contains MS5607 info, which we ignore here */ @@ -390,19 +409,6 @@ public class AltosConfigData implements Iterable { } - public int log_limit() { - if (storage_size > 0) { - int log_limit = storage_size; - - if (storage_erase_unit > 0 && use_flash_for_config()) - log_limit -= storage_erase_unit; - - if (log_limit > 0) - return log_limit / 1024; - } - return 1024; - } - public void get_values(AltosConfigValues source) throws AltosConfigDataException { /* HAS_FLIGHT */ @@ -462,7 +468,7 @@ public class AltosConfigData implements Iterable { dest.set_radio_frequency(frequency()); boolean max_enabled = true; - if (log_limit() == 0) + if (log_space() == 0) max_enabled = false; switch (log_format) { @@ -477,7 +483,7 @@ public class AltosConfigData implements Iterable { dest.set_flight_log_max_enabled(max_enabled); dest.set_radio_enable(radio_enable); - dest.set_flight_log_max_limit(log_limit()); + dest.set_flight_log_max_limit(log_space() / 1024); dest.set_flight_log_max(flight_log_max); dest.set_ignite_mode(ignite_mode); dest.set_pad_orientation(pad_orientation); diff --git a/altosui/AltosConfig.java b/altosui/AltosConfig.java index 2cf69525..6eb7d40c 100644 --- a/altosui/AltosConfig.java +++ b/altosui/AltosConfig.java @@ -229,20 +229,20 @@ public class AltosConfig implements ActionListener { void save_data() { - /* bounds check stuff */ - if (config_ui.flight_log_max() > data.log_limit()) { - JOptionPane.showMessageDialog(owner, - String.format("Requested flight log, %dk, is larger than the available space, %dk.\n", - config_ui.flight_log_max(), - data.log_limit()), - "Maximum Flight Log Too Large", - JOptionPane.ERROR_MESSAGE); - return; - } + try { + /* bounds check stuff */ + if (config_ui.flight_log_max() > data.log_space() / 1024) { + JOptionPane.showMessageDialog(owner, + String.format("Requested flight log, %dk, is larger than the available space, %dk.\n", + config_ui.flight_log_max(), + data.log_space() / 1024), + "Maximum Flight Log Too Large", + JOptionPane.ERROR_MESSAGE); + return; + } - /* Pull data out of the UI and stuff back into our local data record */ + /* Pull data out of the UI and stuff back into our local data record */ - try { data.get_values(config_ui); run_serial_thread(serial_mode_save); } catch (AltosConfigDataException ae) { diff --git a/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java index bcb3e12c..f936d92c 100644 --- a/altosui/AltosConfigUI.java +++ b/altosui/AltosConfigUI.java @@ -99,12 +99,6 @@ public class AltosConfigUI "0", "5", "10", "15", "20" }; - static String[] flight_log_max_values = { - "64", "128", "192", "256", "320", - "384", "448", "512", "576", "640", - "704", "768", "832", "896", "960", - }; - static String[] ignite_mode_values = { "Dual Deploy", "Redundant Apogee", @@ -546,7 +540,7 @@ public class AltosConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = il; c.ipady = 5; - flight_log_max_label = new JLabel("Maximum Flight Log Size:"); + flight_log_max_label = new JLabel("Maximum Flight Log Size (kB):"); pane.add(flight_log_max_label, c); c = new GridBagConstraints(); @@ -557,7 +551,7 @@ public class AltosConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - flight_log_max_value = new JComboBox(flight_log_max_values); + flight_log_max_value = new JComboBox(); flight_log_max_value.setEditable(true); flight_log_max_value.addItemListener(this); pane.add(flight_log_max_value, c); @@ -918,8 +912,19 @@ public class AltosConfigUI apogee_delay_value.setEnabled(new_apogee_delay >= 0); } - public int apogee_delay() { - return Integer.parseInt(apogee_delay_value.getSelectedItem().toString()); + private int parse_int(String name, String s, boolean split) throws AltosConfigDataException { + String v = s; + if (split) + v = s.split("\\s+")[0]; + try { + return Integer.parseInt(v); + } catch (NumberFormatException ne) { + throw new AltosConfigDataException("Invalid %s \"%s\"", name, s); + } + } + + public int apogee_delay() throws AltosConfigDataException { + return parse_int("apogee delay", apogee_delay_value.getSelectedItem().toString(), false); } public void set_apogee_lockout(int new_apogee_lockout) { @@ -927,8 +932,8 @@ public class AltosConfigUI apogee_lockout_value.setEnabled(new_apogee_lockout >= 0); } - public int apogee_lockout() { - return Integer.parseInt(apogee_lockout_value.getSelectedItem().toString()); + public int apogee_lockout() throws AltosConfigDataException { + return parse_int("apogee lockout", apogee_lockout_value.getSelectedItem().toString(), false); } public void set_radio_frequency(double new_radio_frequency) { @@ -947,8 +952,8 @@ public class AltosConfigUI radio_calibration_value.setText(String.format("%d", new_radio_calibration)); } - public int radio_calibration() { - return Integer.parseInt(radio_calibration_value.getText()); + public int radio_calibration() throws AltosConfigDataException { + return parse_int("radio calibration", radio_calibration_value.getText(), false); } public void set_radio_enable(int new_radio_enable) { @@ -979,8 +984,22 @@ public class AltosConfigUI return callsign_value.getText(); } + int flight_log_max_limit; + int flight_log_max; + + public String flight_log_max_label(int flight_log_max) { + if (flight_log_max_limit != 0) { + int nflight = flight_log_max_limit / flight_log_max; + String plural = nflight > 1 ? "s" : ""; + + return String.format("%d (%d flight%s)", flight_log_max, nflight, plural); + } + return String.format("%d", flight_log_max); + } + public void set_flight_log_max(int new_flight_log_max) { - flight_log_max_value.setSelectedItem(Integer.toString(new_flight_log_max)); + flight_log_max_value.setSelectedItem(flight_log_max_label(new_flight_log_max)); + flight_log_max = new_flight_log_max; set_flight_log_max_tool_tip(); } @@ -989,20 +1008,19 @@ public class AltosConfigUI set_flight_log_max_tool_tip(); } - public int flight_log_max() { - return Integer.parseInt(flight_log_max_value.getSelectedItem().toString()); + public int flight_log_max() throws AltosConfigDataException { + return parse_int("flight log max", flight_log_max_value.getSelectedItem().toString(), true); } - public void set_flight_log_max_limit(int flight_log_max_limit) { - //boolean any_added = false; + public void set_flight_log_max_limit(int new_flight_log_max_limit) { + flight_log_max_limit = new_flight_log_max_limit; flight_log_max_value.removeAllItems(); - for (int i = 0; i < flight_log_max_values.length; i++) { - if (Integer.parseInt(flight_log_max_values[i]) < flight_log_max_limit){ - flight_log_max_value.addItem(flight_log_max_values[i]); - //any_added = true; - } + for (int i = 8; i >= 1; i--) { + int size = flight_log_max_limit / i; + flight_log_max_value.addItem(String.format("%d (%d flights)", size, i)); } - flight_log_max_value.addItem(String.format("%d", flight_log_max_limit)); + if (flight_log_max != 0) + set_flight_log_max(flight_log_max); } public void set_ignite_mode(int new_ignite_mode) { @@ -1165,11 +1183,11 @@ public class AltosConfigUI set_aprs_interval_tool_tip(); } - public int aprs_interval() { + public int aprs_interval() throws AltosConfigDataException { String s = aprs_interval_value.getSelectedItem().toString(); if (s.equals("Disabled")) return 0; - return Integer.parseInt(s); + return parse_int("aprs interval", s, false); } } diff --git a/telegps/Makefile.am b/telegps/Makefile.am index 7e17b331..e0d596e7 100644 --- a/telegps/Makefile.am +++ b/telegps/Makefile.am @@ -19,7 +19,8 @@ telegps_JAVA= \ TeleGPSConfig.java \ TeleGPSConfigUI.java \ TeleGPSPreferences.java \ - TeleGPSGraphUI.java + TeleGPSGraphUI.java \ + TeleGPSDisplayThread.java JFREECHART_CLASS= \ jfreechart.jar diff --git a/telegps/TeleGPS.java b/telegps/TeleGPS.java index 71174436..c61b245e 100644 --- a/telegps/TeleGPS.java +++ b/telegps/TeleGPS.java @@ -51,7 +51,7 @@ public class TeleGPS } AltosFlightReader reader; - AltosDisplayThread thread; + TeleGPSDisplayThread thread; JMenuBar menu_bar; @@ -349,7 +349,7 @@ public class TeleGPS public void set_reader(AltosFlightReader reader) { setTitle(String.format("TeleGPS %s", reader.name)); - thread = new AltosDisplayThread(this, voice(), this, reader); + thread = new TeleGPSDisplayThread(this, voice(), this, reader); thread.start(); } diff --git a/telegps/TeleGPSConfig.java b/telegps/TeleGPSConfig.java index 22e6a3ac..3505b0bb 100644 --- a/telegps/TeleGPSConfig.java +++ b/telegps/TeleGPSConfig.java @@ -221,20 +221,20 @@ public class TeleGPSConfig implements ActionListener { void save_data() { - /* bounds check stuff */ - if (config_ui.flight_log_max() > data.log_limit()) { - JOptionPane.showMessageDialog(owner, - String.format("Requested flight log, %dk, is larger than the available space, %dk.\n", - config_ui.flight_log_max(), - data.log_limit()), - "Maximum Flight Log Too Large", - JOptionPane.ERROR_MESSAGE); - return; - } + try { + /* bounds check stuff */ + if (config_ui.flight_log_max() > data.log_space()/1024) { + JOptionPane.showMessageDialog(owner, + String.format("Requested flight log, %dk, is larger than the available space, %dk.\n", + config_ui.flight_log_max(), + data.log_space()/1024), + "Maximum Flight Log Too Large", + JOptionPane.ERROR_MESSAGE); + return; + } - /* Pull data out of the UI and stuff back into our local data record */ + /* Pull data out of the UI and stuff back into our local data record */ - try { data.get_values(config_ui); run_serial_thread(serial_mode_save); } catch (AltosConfigDataException ae) { diff --git a/telegps/TeleGPSConfigUI.java b/telegps/TeleGPSConfigUI.java index 863d61bb..03666036 100644 --- a/telegps/TeleGPSConfigUI.java +++ b/telegps/TeleGPSConfigUI.java @@ -65,12 +65,6 @@ public class TeleGPSConfigUI ActionListener listener; - static String[] flight_log_max_values = { - "64", "128", "192", "256", "320", - "384", "448", "512", "576", "640", - "704", "768", "832", "896", "960", - }; - static String[] aprs_interval_values = { "Disabled", "2", @@ -376,7 +370,7 @@ public class TeleGPSConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = il; c.ipady = 5; - flight_log_max_label = new JLabel("Maximum Flight Log Size:"); + flight_log_max_label = new JLabel("Maximum Log Size (kB):"); pane.add(flight_log_max_label, c); c = new GridBagConstraints(); @@ -387,7 +381,7 @@ public class TeleGPSConfigUI c.anchor = GridBagConstraints.LINE_START; c.insets = ir; c.ipady = 5; - flight_log_max_value = new JComboBox(flight_log_max_values); + flight_log_max_value = new JComboBox(); flight_log_max_value.setEditable(true); flight_log_max_value.addItemListener(this); pane.add(flight_log_max_value, c); @@ -636,8 +630,19 @@ public class TeleGPSConfigUI radio_calibration_value.setText(String.format("%d", new_radio_calibration)); } - public int radio_calibration() { - return Integer.parseInt(radio_calibration_value.getText()); + private int parse_int(String name, String s, boolean split) throws AltosConfigDataException { + String v = s; + if (split) + v = s.split("\\s+")[0]; + try { + return Integer.parseInt(v); + } catch (NumberFormatException ne) { + throw new AltosConfigDataException("Invalid %s \"%s\"", name, s); + } + } + + public int radio_calibration() throws AltosConfigDataException { + return parse_int("radio calibration", radio_calibration_value.getText(), false); } public void set_radio_enable(int new_radio_enable) { @@ -668,8 +673,22 @@ public class TeleGPSConfigUI return callsign_value.getText(); } + int flight_log_max_limit; + int flight_log_max; + + public String flight_log_max_label(int flight_log_max) { + if (flight_log_max_limit != 0) { + int nflight = flight_log_max_limit / flight_log_max; + String plural = nflight > 1 ? "s" : ""; + + return String.format("%d (%d flight%s)", flight_log_max, nflight, plural); + } + return String.format("%d", flight_log_max); + } + public void set_flight_log_max(int new_flight_log_max) { - flight_log_max_value.setSelectedItem(Integer.toString(new_flight_log_max)); + flight_log_max_value.setSelectedItem(flight_log_max_label(new_flight_log_max)); + flight_log_max = new_flight_log_max; set_flight_log_max_tool_tip(); } @@ -678,20 +697,19 @@ public class TeleGPSConfigUI set_flight_log_max_tool_tip(); } - public int flight_log_max() { - return Integer.parseInt(flight_log_max_value.getSelectedItem().toString()); + public int flight_log_max() throws AltosConfigDataException { + return parse_int("flight log max", flight_log_max_value.getSelectedItem().toString(), true); } - public void set_flight_log_max_limit(int flight_log_max_limit) { - //boolean any_added = false; + public void set_flight_log_max_limit(int new_flight_log_max_limit) { + flight_log_max_limit = new_flight_log_max_limit; flight_log_max_value.removeAllItems(); - for (int i = 0; i < flight_log_max_values.length; i++) { - if (Integer.parseInt(flight_log_max_values[i]) < flight_log_max_limit){ - flight_log_max_value.addItem(flight_log_max_values[i]); - //any_added = true; - } + for (int i = 8; i >= 1; i--) { + int size = flight_log_max_limit / i; + flight_log_max_value.addItem(String.format("%d (%d flights)", size, i)); } - flight_log_max_value.addItem(String.format("%d", flight_log_max_limit)); + if (flight_log_max != 0) + set_flight_log_max(flight_log_max); } public void set_ignite_mode(int new_ignite_mode) { } @@ -791,11 +809,11 @@ public class TeleGPSConfigUI set_aprs_interval_tool_tip(); } - public int aprs_interval() { + public int aprs_interval() throws AltosConfigDataException { String s = aprs_interval_value.getSelectedItem().toString(); if (s.equals("Disabled")) return 0; - return Integer.parseInt(s); + return parse_int("aprs interval", s, false); } } diff --git a/telegps/TeleGPSDisplayThread.java b/telegps/TeleGPSDisplayThread.java new file mode 100644 index 00000000..9de33098 --- /dev/null +++ b/telegps/TeleGPSDisplayThread.java @@ -0,0 +1,209 @@ +/* + * Copyright © 2010 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; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package org.altusmetrum.telegps; + +import java.awt.*; +import javax.swing.*; +import java.io.*; +import java.text.*; +import org.altusmetrum.altoslib_4.*; +import org.altusmetrum.altosuilib_2.*; + +public class TeleGPSDisplayThread extends Thread { + + Frame parent; + IdleThread idle_thread; + AltosVoice voice; + AltosFlightReader reader; + AltosState old_state, state; + AltosListenerState listener_state; + AltosFlightDisplay display; + + synchronized void show_safely() { + final AltosState my_state = state; + final AltosListenerState my_listener_state = listener_state; + Runnable r = new Runnable() { + public void run() { + try { + display.show(my_state, my_listener_state); + } catch (Exception ex) { + } + } + }; + SwingUtilities.invokeLater(r); + } + + void reading_error_internal() { + JOptionPane.showMessageDialog(parent, + String.format("Error reading from \"%s\"", reader.name), + "Telemetry Read Error", + JOptionPane.ERROR_MESSAGE); + } + + void reading_error_safely() { + Runnable r = new Runnable() { + public void run() { + try { + reading_error_internal(); + } catch (Exception ex) { + } + } + }; + SwingUtilities.invokeLater(r); + } + + class IdleThread extends Thread { + + boolean started; + int report_interval; + long report_time; + + public synchronized void report(boolean last) { + if (state == null) + return; + + if (state.height() != AltosLib.MISSING) { + if (state.from_pad != null) { + voice.speak("Height %s, bearing %s %d, elevation %d, range %s, .\n", + AltosConvert.height.say(state.gps_height()), + state.from_pad.bearing_words( + AltosGreatCircle.BEARING_VOICE), + (int) (state.from_pad.bearing + 0.5), + (int) (state.elevation + 0.5), + AltosConvert.distance.say(state.range)); + } else { + voice.speak("Height %s.\n", + AltosConvert.height.say(state.height())); + } + } else { + voice.speak("Height is unknown.\n"); + } + } + + long now () { + return System.currentTimeMillis(); + } + + void set_report_time() { + report_time = now() + report_interval; + } + + public void run () { + try { + for (;;) { + if (reader.has_monitor_battery()) { + listener_state.battery = reader.monitor_battery(); + show_safely(); + } + set_report_time(); + for (;;) { + voice.drain(); + synchronized (this) { + long sleep_time = report_time - now(); + if (sleep_time <= 0) + break; + wait(sleep_time); + } + } + + report(false); + } + } catch (InterruptedException ie) { + try { + voice.drain(); + } catch (InterruptedException iie) { } + } + } + + public synchronized void notice(boolean spoken) { + if (old_state != null && old_state.state != state.state) { + report_time = now(); + this.notify(); + } else if (spoken) + set_report_time(); + } + + public IdleThread() { + report_interval = 10000; + } + } + + synchronized boolean tell() { + boolean ret = false; + if (old_state == null || old_state.gps_ready != state.gps_ready) { + if (state.gps_ready) { + voice.speak("GPS ready"); + ret = true; + } + else if (old_state != null) { + voice.speak("GPS lost"); + ret = true; + } + } + old_state = state; + return ret; + } + + public void run() { + boolean interrupted = false; + boolean told; + + idle_thread = new IdleThread(); + idle_thread.start(); + + try { + for (;;) { + try { + state = reader.read(); + if (state == null) + break; + reader.update(state); + show_safely(); + told = tell(); + idle_thread.notice(told); + } catch (ParseException pp) { + System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage()); + } catch (AltosCRCException ce) { + ++listener_state.crc_errors; + show_safely(); + } + } + } catch (InterruptedException ee) { + interrupted = true; + } catch (IOException ie) { + reading_error_safely(); + } finally { + if (!interrupted) + idle_thread.report(true); + reader.close(interrupted); + idle_thread.interrupt(); + try { + idle_thread.join(); + } catch (InterruptedException ie) {} + } + } + + public TeleGPSDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) { + listener_state = new AltosListenerState(); + parent = in_parent; + voice = in_voice; + display = in_display; + reader = in_reader; + display.reset(); + } +} -- 2.30.2