altoslib/altosui/telegps: Change log size configuration
authorKeith Packard <keithp@keithp.com>
Tue, 10 Jun 2014 17:11:03 +0000 (10:11 -0700)
committerKeith Packard <keithp@keithp.com>
Tue, 10 Jun 2014 17:11:03 +0000 (10:11 -0700)
* 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 <keithp@keithp.com>
altoslib/AltosConfigData.java
altosui/AltosConfig.java
altosui/AltosConfigUI.java
telegps/Makefile.am
telegps/TeleGPS.java
telegps/TeleGPSConfig.java
telegps/TeleGPSConfigUI.java
telegps/TeleGPSDisplayThread.java [new file with mode: 0644]

index 9292a5a2d5b26903e735d9280741fbdb9c91bd60..9462ae6f4d62f8b9eae3b01a442436d711e53ee3 100644 (file)
@@ -29,6 +29,7 @@ public class AltosConfigData implements Iterable<String> {
        public int      serial;
        public int      flight;
        public int      log_format;
        public int      serial;
        public int      flight;
        public int      log_format;
+       public int      log_space;
        public String   version;
 
        /* Strings returned */
        public String   version;
 
        /* Strings returned */
@@ -124,6 +125,22 @@ public class AltosConfigData implements Iterable<String> {
                return lines.iterator();
        }
 
                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:
        public int log_available() {
                switch (log_format) {
                case AltosLib.AO_LOG_FORMAT_TINY:
@@ -137,7 +154,7 @@ public class AltosConfigData implements Iterable<String> {
                        if (flight_log_max <= 0)
                                return 1;
                        int     log_max = flight_log_max * 1024;
                        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)
                        int     log_used;
 
                        if (stored_flight <= 0)
@@ -202,6 +219,7 @@ public class AltosConfigData implements Iterable<String> {
                serial = 0;
                flight = 0;
                log_format = AltosLib.AO_LOG_FORMAT_UNKNOWN;
                serial = 0;
                flight = 0;
                log_format = AltosLib.AO_LOG_FORMAT_UNKNOWN;
+               log_space = -1;
                version = "unknown";
 
                main_deploy = -1;
                version = "unknown";
 
                main_deploy = -1;
@@ -247,6 +265,7 @@ public class AltosConfigData implements Iterable<String> {
                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 { 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 */
                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<String> {
        }
 
 
        }
 
 
-       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 */
        public void get_values(AltosConfigValues source) throws AltosConfigDataException {
 
                /* HAS_FLIGHT */
@@ -462,7 +468,7 @@ public class AltosConfigData implements Iterable<String> {
                dest.set_radio_frequency(frequency());
                boolean max_enabled = true;
 
                dest.set_radio_frequency(frequency());
                boolean max_enabled = true;
 
-               if (log_limit() == 0)
+               if (log_space() == 0)
                        max_enabled = false;
 
                switch (log_format) {
                        max_enabled = false;
 
                switch (log_format) {
@@ -477,7 +483,7 @@ public class AltosConfigData implements Iterable<String> {
 
                dest.set_flight_log_max_enabled(max_enabled);
                dest.set_radio_enable(radio_enable);
 
                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);
                dest.set_flight_log_max(flight_log_max);
                dest.set_ignite_mode(ignite_mode);
                dest.set_pad_orientation(pad_orientation);
index 2cf69525fea1e468d5ab40a502b224d9c64530bd..6eb7d40cb34ea88e7a0d60469ed9a179094bbd6a 100644 (file)
@@ -229,20 +229,20 @@ public class AltosConfig implements ActionListener {
 
        void save_data() {
 
 
        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) {
                        data.get_values(config_ui);
                        run_serial_thread(serial_mode_save);
                } catch (AltosConfigDataException ae) {
index bcb3e12c5df2da4b1ac1ee4b5fffdb61b96ee90a..f936d92c19ee9dc487c671e29b4d886f782e6a53 100644 (file)
@@ -99,12 +99,6 @@ public class AltosConfigUI
                "0", "5", "10", "15", "20"
        };
 
                "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",
        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;
                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();
                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;
                c.anchor = GridBagConstraints.LINE_START;
                c.insets = ir;
                c.ipady = 5;
-               flight_log_max_value = new JComboBox<String>(flight_log_max_values);
+               flight_log_max_value = new JComboBox<String>();
                flight_log_max_value.setEditable(true);
                flight_log_max_value.addItemListener(this);
                pane.add(flight_log_max_value, c);
                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);
        }
 
                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) {
        }
 
        public void set_apogee_lockout(int new_apogee_lockout) {
@@ -927,8 +932,8 @@ public class AltosConfigUI
                apogee_lockout_value.setEnabled(new_apogee_lockout >= 0);
        }
 
                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) {
        }
 
        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));
        }
 
                        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) {
        }
 
        public void set_radio_enable(int new_radio_enable) {
@@ -979,8 +984,22 @@ public class AltosConfigUI
                return callsign_value.getText();
        }
 
                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) {
        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();
        }
 
                set_flight_log_max_tool_tip();
        }
 
@@ -989,20 +1008,19 @@ public class AltosConfigUI
                set_flight_log_max_tool_tip();
        }
 
                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();
                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) {
        }
 
        public void set_ignite_mode(int new_ignite_mode) {
@@ -1165,11 +1183,11 @@ public class AltosConfigUI
                set_aprs_interval_tool_tip();
        }
 
                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;
                String  s = aprs_interval_value.getSelectedItem().toString();
 
                if (s.equals("Disabled"))
                        return 0;
-               return Integer.parseInt(s);
+               return parse_int("aprs interval", s, false);
        }
 }
        }
 }
index 7e17b3311752ac3be6edcee26a6d8f5e5128ba25..e0d596e798e42fdbfc00775e31abc12ddaaa5b13 100644 (file)
@@ -19,7 +19,8 @@ telegps_JAVA= \
        TeleGPSConfig.java \
        TeleGPSConfigUI.java \
        TeleGPSPreferences.java \
        TeleGPSConfig.java \
        TeleGPSConfigUI.java \
        TeleGPSPreferences.java \
-       TeleGPSGraphUI.java
+       TeleGPSGraphUI.java \
+       TeleGPSDisplayThread.java
 
 JFREECHART_CLASS= \
     jfreechart.jar
 
 JFREECHART_CLASS= \
     jfreechart.jar
index 7117443632dcfa9553a706be58dba74fefaef2a2..c61b245e006c80d7e16ae353df4cd093ccc19a63 100644 (file)
@@ -51,7 +51,7 @@ public class TeleGPS
        }
 
        AltosFlightReader       reader;
        }
 
        AltosFlightReader       reader;
-       AltosDisplayThread      thread;
+       TeleGPSDisplayThread    thread;
 
        JMenuBar                menu_bar;
 
 
        JMenuBar                menu_bar;
 
@@ -349,7 +349,7 @@ public class TeleGPS
 
        public void set_reader(AltosFlightReader reader) {
                setTitle(String.format("TeleGPS %s", reader.name));
 
        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();
        }
 
                thread.start();
        }
 
index 22e6a3ac134477dcdc571502b9300ec35e2347e9..3505b0bb643ba186cb51cdbdf9b568ccfa817970 100644 (file)
@@ -221,20 +221,20 @@ public class TeleGPSConfig implements ActionListener {
 
        void save_data() {
 
 
        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) {
                        data.get_values(config_ui);
                        run_serial_thread(serial_mode_save);
                } catch (AltosConfigDataException ae) {
index 863d61bbbd6ff7758bd67efe18856979001e4d1c..0366603614da04ce339afebdde82b94b11903186 100644 (file)
@@ -65,12 +65,6 @@ public class TeleGPSConfigUI
 
        ActionListener          listener;
 
 
        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",
        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;
                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();
                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;
                c.anchor = GridBagConstraints.LINE_START;
                c.insets = ir;
                c.ipady = 5;
-               flight_log_max_value = new JComboBox<String>(flight_log_max_values);
+               flight_log_max_value = new JComboBox<String>();
                flight_log_max_value.setEditable(true);
                flight_log_max_value.addItemListener(this);
                pane.add(flight_log_max_value, c);
                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));
        }
 
                        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) {
        }
 
        public void set_radio_enable(int new_radio_enable) {
@@ -668,8 +673,22 @@ public class TeleGPSConfigUI
                return callsign_value.getText();
        }
 
                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) {
        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();
        }
 
                set_flight_log_max_tool_tip();
        }
 
@@ -678,20 +697,19 @@ public class TeleGPSConfigUI
                set_flight_log_max_tool_tip();
        }
 
                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();
                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) { }
        }
 
        public void set_ignite_mode(int new_ignite_mode) { }
@@ -791,11 +809,11 @@ public class TeleGPSConfigUI
                set_aprs_interval_tool_tip();
        }
 
                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;
                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 (file)
index 0000000..9de3309
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2010 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; 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();
+       }
+}