altosui/altosuilib/altoslib: Move more stuff out of autosui. Reduce site map memory
authorKeith Packard <keithp@keithp.com>
Mon, 26 May 2014 03:55:11 +0000 (20:55 -0700)
committerKeith Packard <keithp@keithp.com>
Mon, 26 May 2014 03:56:48 +0000 (20:56 -0700)
Prepare to share with TeleGPS application.

This also has the changes to the site map tile which cache only a few
images and regenerate the flight path on the fly, saving piles of memory

Signed-off-by: Keith Packard <keithp@keithp.com>
23 files changed:
altoslib/AltosFlightStats.java [new file with mode: 0644]
altoslib/Makefile.am
altosui/AltosDisplayThread.java [deleted file]
altosui/AltosFlightDisplay.java [deleted file]
altosui/AltosFlightStats.java [deleted file]
altosui/AltosFreqList.java [deleted file]
altosui/AltosSiteMap.java [deleted file]
altosui/AltosSiteMapCache.java [deleted file]
altosui/AltosSiteMapPreload.java [deleted file]
altosui/AltosSiteMapTile.java [deleted file]
altosui/AltosVoice.java [deleted file]
altosui/GrabNDrag.java [deleted file]
altosui/Makefile.am
altosuilib/AltosDisplayThread.java [new file with mode: 0644]
altosuilib/AltosFlightDisplay.java [new file with mode: 0644]
altosuilib/AltosFreqList.java [new file with mode: 0644]
altosuilib/AltosSiteMap.java [new file with mode: 0644]
altosuilib/AltosSiteMapCache.java [new file with mode: 0644]
altosuilib/AltosSiteMapPreload.java [new file with mode: 0644]
altosuilib/AltosSiteMapTile.java [new file with mode: 0644]
altosuilib/AltosVoice.java [new file with mode: 0644]
altosuilib/GrabNDrag.java [new file with mode: 0644]
altosuilib/Makefile.am

diff --git a/altoslib/AltosFlightStats.java b/altoslib/AltosFlightStats.java
new file mode 100644 (file)
index 0000000..87e0429
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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; 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.altoslib_4;
+
+import java.io.*;
+
+public class AltosFlightStats {
+       public double           max_height;
+       public double           max_gps_height;
+       public double           max_speed;
+       public double           max_acceleration;
+       public double[] state_speed = new double[AltosLib.ao_flight_invalid + 1];
+       public double[] state_accel = new double[AltosLib.ao_flight_invalid + 1];
+       public int[]            state_count = new int[AltosLib.ao_flight_invalid + 1];
+       public double[] state_start = new double[AltosLib.ao_flight_invalid + 1];
+       public double[] state_end = new double[AltosLib.ao_flight_invalid + 1];
+       public int              serial;
+       public int              flight;
+       public int              year, month, day;
+       public int              hour, minute, second;
+       public double           lat, lon;
+       public double           pad_lat, pad_lon;
+       public boolean          has_gps;
+       public boolean          has_other_adc;
+       public boolean          has_rssi;
+       public boolean          has_imu;
+       public boolean          has_mag;
+       public boolean          has_orient;
+       public int              num_ignitor;
+
+       double landed_time(AltosStateIterable states) {
+               AltosState state = null;
+
+               for (AltosState s : states) {
+                       state = s;
+                       if (state.state == AltosLib.ao_flight_landed)
+                               break;
+               }
+
+               if (state == null)
+                       return 0;
+
+               double  landed_height = state.height();
+
+               state = null;
+
+               boolean above = true;
+
+               double  landed_time = -1000;
+
+               for (AltosState s : states) {
+                       state = s;
+
+                       if (state.height() > landed_height + 10) {
+                               above = true;
+                       } else {
+                               if (above && state.height() < landed_height + 2) {
+                                       above = false;
+                                       landed_time = state.time;
+                               }
+                       }
+               }
+               if (landed_time == -1000)
+                       landed_time = state.time;
+               return landed_time;
+       }
+
+       double boost_time(AltosStateIterable states) {
+               double boost_time = AltosLib.MISSING;
+               AltosState      state = null;
+
+               for (AltosState s : states) {
+                       state = s;
+                       if (state.acceleration() < 1)
+                               boost_time = state.time;
+                       if (state.state >= AltosLib.ao_flight_boost && state.state <= AltosLib.ao_flight_landed)
+                               break;
+               }
+               if (state == null)
+                       return 0;
+
+               if (boost_time == AltosLib.MISSING)
+                       boost_time = state.time;
+               return boost_time;
+       }
+
+
+       public AltosFlightStats(AltosStateIterable states) throws InterruptedException, IOException {
+               double          boost_time = boost_time(states);
+               double          end_time = 0;
+               double          landed_time = landed_time(states);
+
+               year = month = day = AltosLib.MISSING;
+               hour = minute = second = AltosLib.MISSING;
+               serial = flight = AltosLib.MISSING;
+               lat = lon = AltosLib.MISSING;
+               has_gps = false;
+               has_other_adc = false;
+               has_rssi = false;
+               has_imu = false;
+               has_mag = false;
+               has_orient = false;
+               for (AltosState state : states) {
+                       if (serial == AltosLib.MISSING && state.serial != AltosLib.MISSING)
+                               serial = state.serial;
+                       if (flight == AltosLib.MISSING && state.flight != AltosLib.MISSING)
+                               flight = state.flight;
+                       if (state.battery_voltage != AltosLib.MISSING)
+                               has_other_adc = true;
+                       if (state.rssi != AltosLib.MISSING)
+                               has_rssi = true;
+                       end_time = state.time;
+
+                       int state_id = state.state;
+                       if (state.time >= boost_time && state_id < AltosLib.ao_flight_boost)
+                               state_id = AltosLib.ao_flight_boost;
+                       if (state.time >= landed_time && state_id < AltosLib.ao_flight_landed)
+                               state_id = AltosLib.ao_flight_landed;
+                       if (state.gps != null && state.gps.locked) {
+                               year = state.gps.year;
+                               month = state.gps.month;
+                               day = state.gps.day;
+                               hour = state.gps.hour;
+                               minute = state.gps.minute;
+                               second = state.gps.second;
+                       }
+                       if (0 <= state_id && state_id < AltosLib.ao_flight_invalid) {
+                               double acceleration = state.acceleration();
+                               double speed = state.speed();
+                               if (acceleration != AltosLib.MISSING && speed != AltosLib.MISSING) {
+                                       state_accel[state_id] += acceleration;
+                                       state_speed[state_id] += speed;
+                                       state_count[state_id]++;
+                               }
+                               if (state_start[state_id] == 0.0)
+                                       state_start[state_id] = state.time;
+                               if (state_end[state_id] < state.time)
+                                       state_end[state_id] = state.time;
+                               max_height = state.max_height();
+                               max_speed = state.max_speed();
+                               max_acceleration = state.max_acceleration();
+                               max_gps_height = state.max_gps_height();
+                       }
+                       if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
+                               if (state_id <= AltosLib.ao_flight_pad) {
+                                       pad_lat = state.gps.lat;
+                                       pad_lon = state.gps.lon;
+                               }
+                               lat = state.gps.lat;
+                               lon = state.gps.lon;
+                               has_gps = true;
+                       }
+                       if (state.imu != null)
+                               has_imu = true;
+                       if (state.mag != null)
+                               has_mag = true;
+                       if (state.orient() != AltosLib.MISSING)
+                               has_orient = true;
+                       if (state.ignitor_voltage != null && state.ignitor_voltage.length > num_ignitor)
+                               num_ignitor = state.ignitor_voltage.length;
+               }
+               for (int s = AltosLib.ao_flight_startup; s <= AltosLib.ao_flight_landed; s++) {
+                       if (state_count[s] > 0) {
+                               state_speed[s] /= state_count[s];
+                               state_accel[s] /= state_count[s];
+                       }
+                       if (state_start[s] == 0)
+                               state_start[s] = end_time;
+                       if (state_end[s] == 0)
+                               state_end[s] = end_time;
+               }
+       }
+}
index 67cc38ffb23bfab7c5b7a440498ce9fd79fd26cf..bff09704ae2279ce14111788129b398b1f12de94 100644 (file)
@@ -1,4 +1,4 @@
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
 
 JAVAROOT=bin
 
@@ -51,6 +51,7 @@ altoslib_JAVA = \
        AltosFlash.java \
        AltosFlashListener.java \
        AltosFlightReader.java \
+       AltosFlightStats.java \
        AltosFrequency.java \
        AltosGPS.java \
        AltosGPSSat.java \
diff --git a/altosui/AltosDisplayThread.java b/altosui/AltosDisplayThread.java
deleted file mode 100644 (file)
index ab85607..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * 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 altosui;
-
-import java.awt.*;
-import javax.swing.*;
-import java.io.*;
-import java.text.*;
-import org.altusmetrum.altoslib_3.*;
-
-public class AltosDisplayThread 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     reported_landing;
-               int     report_interval;
-               long    report_time;
-
-               public synchronized void report(boolean last) {
-                       if (state == null)
-                               return;
-
-                       /* reset the landing count once we hear about a new flight */
-                       if (state.state < Altos.ao_flight_drogue)
-                               reported_landing = 0;
-
-                       /* Shut up once the rocket is on the ground */
-                       if (reported_landing > 2) {
-                               return;
-                       }
-
-                       /* If the rocket isn't on the pad, then report height */
-                       if (Altos.ao_flight_drogue <= state.state &&
-                           state.state < Altos.ao_flight_landed &&
-                           state.from_pad != null &&
-                           state.range >= 0)
-                       {
-                               voice.speak("Height %s, bearing %s %d, elevation %d, range %s.\n",
-                                           AltosConvert.height.say(state.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 if (state.state > Altos.ao_flight_pad) {
-                               voice.speak(AltosConvert.height.say_units(state.height()));
-                       } else {
-                               reported_landing = 0;
-                       }
-
-                       /* If the rocket is coming down, check to see if it has landed;
-                        * either we've got a landed report or we haven't heard from it in
-                        * a long time
-                        */
-                       if (state.state >= Altos.ao_flight_drogue &&
-                           (last ||
-                            System.currentTimeMillis() - state.received_time >= 15000 ||
-                            state.state == Altos.ao_flight_landed))
-                       {
-                               if (Math.abs(state.speed()) < 20 && state.height() < 100)
-                                       voice.speak("rocket landed safely");
-                               else
-                                       voice.speak("rocket may have crashed");
-                               if (state.from_pad != null)
-                                       voice.speak("Bearing %d degrees, range %s.",
-                                                   (int) (state.from_pad.bearing + 0.5),
-                                                   AltosConvert.distance.say_units(state.from_pad.distance));
-                               ++reported_landing;
-                               if (state.state != Altos.ao_flight_landed) {
-                                       state.state = Altos.ao_flight_landed;
-                                       show_safely();
-                               }
-                       }
-               }
-
-               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() {
-                       reported_landing = 0;
-                       report_interval = 10000;
-               }
-       }
-
-       synchronized boolean tell() {
-               boolean ret = false;
-               if (old_state == null || old_state.state != state.state) {
-                       voice.speak(state.state_name());
-                       if ((old_state == null || old_state.state <= Altos.ao_flight_boost) &&
-                           state.state > Altos.ao_flight_boost) {
-                               voice.speak("max speed: %s.",
-                                           AltosConvert.speed.say_units(state.max_speed() + 0.5));
-                               ret = true;
-                       } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) &&
-                                  state.state >= Altos.ao_flight_drogue) {
-                               voice.speak("max height: %s.",
-                                           AltosConvert.height.say_units(state.max_height() + 0.5));
-                               ret = true;
-                       }
-               }
-               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 AltosDisplayThread(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();
-       }
-}
diff --git a/altosui/AltosFlightDisplay.java b/altosui/AltosFlightDisplay.java
deleted file mode 100644 (file)
index c126425..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 altosui;
-
-import org.altusmetrum.altoslib_3.*;
-
-public interface AltosFlightDisplay {
-       void reset();
-
-       void show(AltosState state, AltosListenerState listener_state);
-
-       void set_font();
-}
diff --git a/altosui/AltosFlightStats.java b/altosui/AltosFlightStats.java
deleted file mode 100644 (file)
index d02a518..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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; 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 altosui;
-
-import java.io.*;
-import org.altusmetrum.altoslib_3.*;
-
-public class AltosFlightStats {
-       double          max_height;
-       double          max_gps_height;
-       double          max_speed;
-       double          max_acceleration;
-       double[]        state_speed = new double[Altos.ao_flight_invalid + 1];
-       double[]        state_accel = new double[Altos.ao_flight_invalid + 1];
-       int[]           state_count = new int[Altos.ao_flight_invalid + 1];
-       double[]        state_start = new double[Altos.ao_flight_invalid + 1];
-       double[]        state_end = new double[Altos.ao_flight_invalid + 1];
-       int             serial;
-       int             flight;
-       int             year, month, day;
-       int             hour, minute, second;
-       double          lat, lon;
-       double          pad_lat, pad_lon;
-       boolean         has_gps;
-       boolean         has_other_adc;
-       boolean         has_rssi;
-       boolean         has_imu;
-       boolean         has_mag;
-       boolean         has_orient;
-       int             num_ignitor;
-
-       double landed_time(AltosStateIterable states) {
-               AltosState state = null;
-
-               for (AltosState s : states) {
-                       state = s;
-                       if (state.state == Altos.ao_flight_landed)
-                               break;
-               }
-
-               if (state == null)
-                       return 0;
-
-               double  landed_height = state.height();
-
-               state = null;
-
-               boolean above = true;
-
-               double  landed_time = -1000;
-
-               for (AltosState s : states) {
-                       state = s;
-
-                       if (state.height() > landed_height + 10) {
-                               above = true;
-                       } else {
-                               if (above && state.height() < landed_height + 2) {
-                                       above = false;
-                                       landed_time = state.time;
-                               }
-                       }
-               }
-               if (landed_time == -1000)
-                       landed_time = state.time;
-               return landed_time;
-       }
-
-       double boost_time(AltosStateIterable states) {
-               double boost_time = AltosLib.MISSING;
-               AltosState      state = null;
-
-               for (AltosState s : states) {
-                       state = s;
-                       if (state.acceleration() < 1)
-                               boost_time = state.time;
-                       if (state.state >= AltosLib.ao_flight_boost && state.state <= AltosLib.ao_flight_landed)
-                               break;
-               }
-               if (state == null)
-                       return 0;
-
-               if (boost_time == AltosLib.MISSING)
-                       boost_time = state.time;
-               return boost_time;
-       }
-
-
-       public AltosFlightStats(AltosStateIterable states) throws InterruptedException, IOException {
-               double          boost_time = boost_time(states);
-               double          end_time = 0;
-               double          landed_time = landed_time(states);
-
-               year = month = day = AltosLib.MISSING;
-               hour = minute = second = AltosLib.MISSING;
-               serial = flight = AltosLib.MISSING;
-               lat = lon = AltosLib.MISSING;
-               has_gps = false;
-               has_other_adc = false;
-               has_rssi = false;
-               has_imu = false;
-               has_mag = false;
-               has_orient = false;
-               for (AltosState state : states) {
-                       if (serial == AltosLib.MISSING && state.serial != AltosLib.MISSING)
-                               serial = state.serial;
-                       if (flight == AltosLib.MISSING && state.flight != AltosLib.MISSING)
-                               flight = state.flight;
-                       if (state.battery_voltage != AltosLib.MISSING)
-                               has_other_adc = true;
-                       if (state.rssi != AltosLib.MISSING)
-                               has_rssi = true;
-                       end_time = state.time;
-
-                       int state_id = state.state;
-                       if (state.time >= boost_time && state_id < Altos.ao_flight_boost)
-                               state_id = Altos.ao_flight_boost;
-                       if (state.time >= landed_time && state_id < Altos.ao_flight_landed)
-                               state_id = Altos.ao_flight_landed;
-                       if (state.gps != null && state.gps.locked) {
-                               year = state.gps.year;
-                               month = state.gps.month;
-                               day = state.gps.day;
-                               hour = state.gps.hour;
-                               minute = state.gps.minute;
-                               second = state.gps.second;
-                       }
-                       if (0 <= state_id && state_id < Altos.ao_flight_invalid) {
-                               double acceleration = state.acceleration();
-                               double speed = state.speed();
-                               if (acceleration != AltosLib.MISSING && speed != AltosLib.MISSING) {
-                                       state_accel[state_id] += acceleration;
-                                       state_speed[state_id] += speed;
-                                       state_count[state_id]++;
-                               }
-                               if (state_start[state_id] == 0.0)
-                                       state_start[state_id] = state.time;
-                               if (state_end[state_id] < state.time)
-                                       state_end[state_id] = state.time;
-                               max_height = state.max_height();
-                               max_speed = state.max_speed();
-                               max_acceleration = state.max_acceleration();
-                               max_gps_height = state.max_gps_height();
-                       }
-                       if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
-                               if (state_id <= Altos.ao_flight_pad) {
-                                       pad_lat = state.gps.lat;
-                                       pad_lon = state.gps.lon;
-                               }
-                               lat = state.gps.lat;
-                               lon = state.gps.lon;
-                               has_gps = true;
-                       }
-                       if (state.imu != null)
-                               has_imu = true;
-                       if (state.mag != null)
-                               has_mag = true;
-                       if (state.orient() != AltosLib.MISSING)
-                               has_orient = true;
-                       if (state.ignitor_voltage != null && state.ignitor_voltage.length > num_ignitor)
-                               num_ignitor = state.ignitor_voltage.length;
-               }
-               for (int s = Altos.ao_flight_startup; s <= Altos.ao_flight_landed; s++) {
-                       if (state_count[s] > 0) {
-                               state_speed[s] /= state_count[s];
-                               state_accel[s] /= state_count[s];
-                       }
-                       if (state_start[s] == 0)
-                               state_start[s] = end_time;
-                       if (state_end[s] == 0)
-                               state_end[s] = end_time;
-               }
-       }
-}
diff --git a/altosui/AltosFreqList.java b/altosui/AltosFreqList.java
deleted file mode 100644 (file)
index 525e5ce..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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; 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 altosui;
-
-import javax.swing.*;
-import org.altusmetrum.altoslib_3.*;
-import org.altusmetrum.altosuilib_1.*;
-
-public class AltosFreqList extends JComboBox {
-
-       String  product;
-       int     serial;
-       int     calibrate;
-
-       public void set_frequency(double new_frequency) {
-               int i;
-
-               if (new_frequency < 0) {
-                       setVisible(false);
-                       return;
-               }
-
-               for (i = 0; i < getItemCount(); i++) {
-                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
-
-                       if (f.close(new_frequency)) {
-                               setSelectedIndex(i);
-                               return;
-                       }
-               }
-               for (i = 0; i < getItemCount(); i++) {
-                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
-
-                       if (new_frequency < f.frequency)
-                               break;
-               }
-               String  description = String.format("%s serial %d", product, serial);
-               AltosFrequency  frequency = new AltosFrequency(new_frequency, description);
-               AltosUIPreferences.add_common_frequency(frequency);
-               insertItemAt(frequency, i);
-               setMaximumRowCount(getItemCount());
-       }
-
-       public void set_product(String new_product) {
-               product = new_product;
-       }
-
-       public void set_serial(int new_serial) {
-               serial = new_serial;
-       }
-
-       public double frequency() {
-               AltosFrequency  f = (AltosFrequency) getSelectedItem();
-               if (f != null)
-                       return f.frequency;
-               return 434.550;
-       }
-
-       public AltosFreqList () {
-               super(AltosUIPreferences.common_frequencies());
-               setMaximumRowCount(getItemCount());
-               setEditable(false);
-               product = "Unknown";
-               serial = 0;
-       }
-
-       public AltosFreqList(double in_frequency) {
-               this();
-               set_frequency(in_frequency);
-       }
-}
diff --git a/altosui/AltosSiteMap.java b/altosui/AltosSiteMap.java
deleted file mode 100644 (file)
index 643417b..0000000
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * 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 altosui;
-
-import java.awt.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.Point2D;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_3.*;
-import org.altusmetrum.altosuilib_1.*;
-
-public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
-       // preferred vertical step in a tile in naut. miles
-       // will actually choose a step size between x and 2x, where this
-       // is 1.5x
-       static final double tile_size_nmi = 0.75;
-
-       static final int px_size = 512;
-
-       static final int MAX_TILE_DELTA = 100;
-
-       private static Point2D.Double translatePoint(Point2D.Double p,
-                       Point2D.Double d)
-       {
-               return new Point2D.Double(p.x + d.x, p.y + d.y);
-       }
-
-       static class LatLng {
-               public double lat, lng;
-               public LatLng(double lat, double lng) {
-                       this.lat = lat;
-                       this.lng = lng;
-               }
-       }
-
-       // based on google js
-       //  http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js
-       // search for fromLatLngToPoint and fromPointToLatLng
-       /*
-       private static Point2D.Double pt(LatLng latlng, int zoom) {
-               double scale_x = 256/360.0 * Math.pow(2, zoom);
-               double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
-               return pt(latlng, scale_x, scale_y);
-       }
-       */
-
-       private static Point2D.Double pt(LatLng latlng,
-                                        double scale_x, double scale_y)
-       {
-               Point2D.Double res = new Point2D.Double();
-               double e;
-
-               res.x = latlng.lng * scale_x;
-
-               e = Math.sin(Math.toRadians(latlng.lat));
-               e = Math.max(e,-(1-1.0E-15));
-               e = Math.min(e,  1-1.0E-15 );
-
-               res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
-               return res;
-       }
-
-       static private LatLng latlng(Point2D.Double pt,
-                                    double scale_x, double scale_y)
-       {
-               double lat, lng;
-               double rads;
-
-               lng = pt.x/scale_x;
-               rads = 2 * Math.atan(Math.exp(-pt.y/scale_y));
-               lat = Math.toDegrees(rads - Math.PI/2);
-
-               return new LatLng(lat,lng);
-       }
-
-       int zoom;
-       double scale_x, scale_y;
-
-       int radius;     /* half width/height of tiles to load */
-
-       private Point2D.Double pt(double lat, double lng) {
-               return pt(new LatLng(lat, lng), scale_x, scale_y);
-       }
-
-       private LatLng latlng(double x, double y) {
-               return latlng(new Point2D.Double(x,y), scale_x, scale_y);
-       }
-       /*
-       private LatLng latlng(Point2D.Double pt) {
-               return latlng(pt, scale_x, scale_y);
-       }
-       */
-
-       ConcurrentHashMap<Point,AltosSiteMapTile> mapTiles = new ConcurrentHashMap<Point,AltosSiteMapTile>();
-       Point2D.Double centre;
-
-       private Point2D.Double tileCoordOffset(Point p) {
-               return new Point2D.Double(centre.x - p.x*px_size,
-                                         centre.y - p.y * px_size);
-       }
-
-       private Point tileOffset(Point2D.Double p) {
-               return new Point((int)Math.floor((centre.x+p.x)/px_size),
-                                (int)Math.floor((centre.y+p.y)/px_size));
-       }
-
-       private Point2D.Double getBaseLocation(double lat, double lng) {
-               Point2D.Double locn, north_step;
-
-               zoom = 2;
-               // stupid loop structure to please Java's control flow analysis
-               do {
-                       zoom++;
-                       scale_x = 256/360.0 * Math.pow(2, zoom);
-                       scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
-                       locn = pt(lat, lng);
-                       north_step = pt(lat+tile_size_nmi*4/3/60.0, lng);
-                       if (locn.y - north_step.y > px_size)
-                               break;
-               } while (zoom < 22);
-               locn.x = -px_size * Math.floor(locn.x/px_size);
-               locn.y = -px_size * Math.floor(locn.y/px_size);
-               return locn;
-       }
-
-       public void reset() {
-               // nothing
-       }
-
-       public void set_font() {
-               // nothing
-       }
-
-       private void loadMap(final AltosSiteMapTile tile,
-                            File pngfile, String pngurl)
-       {
-               final ImageIcon res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl);
-               if (res != null) {
-                       SwingUtilities.invokeLater(new Runnable() {
-                                       public void run() {
-                                               tile.loadMap(res);
-                                       }
-                               });
-               } else {
-                       System.out.printf("# Failed to fetch file %s\n", pngfile);
-                       System.out.printf(" wget -O '%s' '%s'\n", pngfile, pngurl);
-               }
-       }
-
-       File pngfile;
-       String pngurl;
-
-       public int prefetchMap(int x, int y) {
-               LatLng map_latlng = latlng(
-                       -centre.x + x*px_size + px_size/2,
-                       -centre.y + y*px_size + px_size/2);
-               pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
-               pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
-               if (pngfile.exists()) {
-                       return 1;
-               } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) {
-                       return 0;
-               } else {
-                       return -1;
-               }
-       }
-
-       public static void prefetchMaps(double lat, double lng) {
-               int w = AltosSiteMapPreload.width;
-               int h = AltosSiteMapPreload.height;
-               AltosSiteMap asm = new AltosSiteMap(true);
-               asm.centre = asm.getBaseLocation(lat, lng);
-
-               //Point2D.Double p = new Point2D.Double();
-               //Point2D.Double p2;
-               int dx = -w/2, dy = -h/2;
-               for (int y = dy; y < h+dy; y++) {
-                       for (int x = dx; x < w+dx; x++) {
-                               int r = asm.prefetchMap(x, y);
-                               switch (r) {
-                               case 1:
-                                       System.out.printf("Already have %s\n", asm.pngfile);
-                                       break;
-                               case 0:
-                                       System.out.printf("Fetched map %s\n", asm.pngfile);
-                                       break;
-                               case -1:
-                                       System.out.printf("# Failed to fetch file %s\n", asm.pngfile);
-                                       System.out.printf(" wget -O '%s' ''\n", asm.pngfile, asm.pngurl);
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       public String initMap(Point offset) {
-               AltosSiteMapTile tile = mapTiles.get(offset);
-               Point2D.Double coord = tileCoordOffset(offset);
-
-               LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y);
-
-               File pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
-               String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
-               loadMap(tile, pngfile, pngurl);
-               return pngfile.toString();
-       }
-
-       public void initAndFinishMapAsync (final AltosSiteMapTile tile, final Point offset) {
-               Thread thread = new Thread() {
-                               public void run() {
-                                       initMap(offset);
-                                       finishTileLater(tile, offset);
-                               }
-                       };
-               thread.start();
-       }
-
-       public void setBaseLocation(double lat, double lng) {
-               for (Point k : mapTiles.keySet()) {
-                       AltosSiteMapTile tile = mapTiles.get(k);
-                       tile.clearMap();
-               }
-
-               centre = getBaseLocation(lat, lng);
-               scrollRocketToVisible(pt(lat,lng));
-       }
-
-       private void initMaps(double lat, double lng) {
-               setBaseLocation(lat, lng);
-
-               Thread thread = new Thread() {
-                               public void run() {
-                                       for (Point k : mapTiles.keySet())
-                                               initMap(k);
-                               }
-                       };
-               thread.start();
-       }
-
-       private static File MapFile(double lat, double lng, int zoom) {
-               char chlat = lat < 0 ? 'S' : 'N';
-               char chlng = lng < 0 ? 'W' : 'E';
-               if (lat < 0) lat = -lat;
-               if (lng < 0) lng = -lng;
-               return new File(AltosUIPreferences.mapdir(),
-                               String.format("map-%c%.6f,%c%.6f-%d.png",
-                                             chlat, lat, chlng, lng, zoom));
-       }
-
-       private static String MapURL(double lat, double lng, int zoom) {
-               return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size);
-       }
-
-       boolean initialised = false;
-       Point2D.Double last_pt = null;
-       int last_state = -1;
-
-       public void show(double lat, double lon) {
-               System.out.printf ("show %g %g\n", lat, lon);
-               return;
-//             initMaps(lat, lon);
-//             scrollRocketToVisible(pt(lat, lon));
-       }
-       public void show(final AltosState state, final AltosListenerState listener_state) {
-               // if insufficient gps data, nothing to update
-               AltosGPS        gps = state.gps;
-
-               if (gps == null)
-                       return;
-
-               if (!gps.locked && gps.nsat < 4)
-                       return;
-
-               if (!initialised) {
-                       if (state.pad_lat != AltosLib.MISSING && state.pad_lon != AltosLib.MISSING) {
-                               initMaps(state.pad_lat, state.pad_lon);
-                               initialised = true;
-                       } else if (gps.lat != AltosLib.MISSING && gps.lon != AltosLib.MISSING) {
-                               initMaps(gps.lat, gps.lon);
-                               initialised = true;
-                       } else {
-                               return;
-                       }
-               }
-
-               final Point2D.Double pt = pt(gps.lat, gps.lon);
-               if (last_pt == pt && last_state == state.state)
-                       return;
-
-               if (last_pt == null) {
-                       last_pt = pt;
-               }
-               boolean in_any = false;
-               for (Point offset : mapTiles.keySet()) {
-                       AltosSiteMapTile tile = mapTiles.get(offset);
-                       Point2D.Double ref, lref;
-                       ref = translatePoint(pt, tileCoordOffset(offset));
-                       lref = translatePoint(last_pt, tileCoordOffset(offset));
-                       tile.show(state, listener_state, lref, ref);
-                       if (0 <= ref.x && ref.x < px_size)
-                               if (0 <= ref.y && ref.y < px_size)
-                                       in_any = true;
-               }
-
-               Point offset = tileOffset(pt);
-               if (!in_any) {
-                       Point2D.Double ref, lref;
-                       ref = translatePoint(pt, tileCoordOffset(offset));
-                       lref = translatePoint(last_pt, tileCoordOffset(offset));
-
-                       AltosSiteMapTile tile = createTile(offset);
-                       tile.show(state, listener_state, lref, ref);
-                       initAndFinishMapAsync(tile, offset);
-               }
-
-               scrollRocketToVisible(pt);
-
-               if (offset != tileOffset(last_pt)) {
-                       ensureTilesAround(offset);
-               }
-
-               last_pt = pt;
-               last_state = state.state;
-       }
-
-       public void centre(Point2D.Double pt) {
-               Rectangle r = comp.getVisibleRect();
-               Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft));
-               int dx = (int)copt.x - r.width/2 - r.x;
-               int dy = (int)copt.y - r.height/2 - r.y;
-               r.x += dx;
-               r.y += dy;
-               comp.scrollRectToVisible(r);
-       }
-
-       public void centre(AltosState state) {
-               if (!state.gps.locked && state.gps.nsat < 4)
-                       return;
-               centre(pt(state.gps.lat, state.gps.lon));
-       }
-
-       public void draw_circle(double lat, double lon) {
-               final Point2D.Double pt = pt(lat, lon);
-
-               for (Point offset : mapTiles.keySet()) {
-                       AltosSiteMapTile tile = mapTiles.get(offset);
-                       Point2D.Double ref = translatePoint(pt, tileCoordOffset(offset));
-                       tile.draw_circle(ref);
-               }
-       }
-
-       private AltosSiteMapTile createTile(Point offset) {
-               AltosSiteMapTile tile = new AltosSiteMapTile(px_size);
-               mapTiles.put(offset, tile);
-               return tile;
-       }
-       private void finishTileLater(final AltosSiteMapTile tile,
-                                    final Point offset)
-       {
-               SwingUtilities.invokeLater( new Runnable() {
-                       public void run() {
-                               addTileAt(tile, offset);
-                       }
-               } );
-       }
-
-       private void ensureTilesAround(Point base_offset) {
-               for (int x = -radius; x <= radius; x++) {
-                       for (int y = -radius; y <= radius; y++) {
-                               Point offset = new Point(base_offset.x + x, base_offset.y + y);
-                               if (mapTiles.containsKey(offset))
-                                       continue;
-                               AltosSiteMapTile tile = createTile(offset);
-                               initAndFinishMapAsync(tile, offset);
-                       }
-               }
-       }
-
-       private Point topleft = new Point(0,0);
-       private void scrollRocketToVisible(Point2D.Double pt) {
-               Rectangle r = comp.getVisibleRect();
-               Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft));
-               int dx = (int)copt.x - r.width/2 - r.x;
-               int dy = (int)copt.y - r.height/2 - r.y;
-               if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) {
-                       r.x += dx;
-                       r.y += dy;
-                       comp.scrollRectToVisible(r);
-               }
-       }
-
-       private void addTileAt(AltosSiteMapTile tile, Point offset) {
-               if (Math.abs(offset.x) >= MAX_TILE_DELTA ||
-                               Math.abs(offset.y) >= MAX_TILE_DELTA)
-               {
-                       System.out.printf("Rocket too far away from pad (tile %d,%d)\n",
-                                         offset.x, offset.y);
-                       return;
-               }
-
-               boolean review = false;
-               Rectangle r = comp.getVisibleRect();
-               if (offset.x < topleft.x) {
-                       r.x += (topleft.x - offset.x) * px_size;
-                       topleft.x = offset.x;
-                       review = true;
-               }
-               if (offset.y < topleft.y) {
-                       r.y += (topleft.y - offset.y) * px_size;
-                       topleft.y = offset.y;
-                       review = true;
-               }
-               GridBagConstraints c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.BOTH;
-               // put some space between the map tiles, debugging only
-               // c.insets = new Insets(5, 5, 5, 5);
-
-               c.gridx = offset.x + MAX_TILE_DELTA;
-               c.gridy = offset.y + MAX_TILE_DELTA;
-               layout.setConstraints(tile, c);
-
-               comp.add(tile);
-               if (review) {
-                       comp.scrollRectToVisible(r);
-               }
-       }
-
-       private AltosSiteMap(boolean knowWhatYouAreDoing) {
-               if (!knowWhatYouAreDoing) {
-                       throw new RuntimeException("Arggh.");
-               }
-       }
-
-       JComponent comp = new JComponent() { };
-       private GridBagLayout layout = new GridBagLayout();
-
-       public AltosSiteMap(int in_radius) {
-               radius = in_radius;
-
-               GrabNDrag scroller = new GrabNDrag(comp);
-
-               comp.setLayout(layout);
-
-               for (int x = -radius; x <= radius; x++) {
-                       for (int y = -radius; y <= radius; y++) {
-                               Point offset = new Point(x, y);
-                               AltosSiteMapTile t = createTile(offset);
-                               addTileAt(t, offset);
-                       }
-               }
-               setViewportView(comp);
-               setPreferredSize(new Dimension(500,500));
-       }
-
-       public AltosSiteMap() {
-               this(1);
-       }
-}
diff --git a/altosui/AltosSiteMapCache.java b/altosui/AltosSiteMapCache.java
deleted file mode 100644 (file)
index 03dc3cf..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * 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 altosui;
-
-import javax.swing.*;
-import javax.imageio.ImageIO;
-import java.awt.image.*;
-import java.io.*;
-import java.net.URL;
-import java.net.URLConnection;
-
-public class AltosSiteMapCache extends JLabel {
-       static final long google_maps_ratelimit_ms = 1200;
-       // Google limits static map queries to 50 per minute per IP, so
-       // each query should take at least 1.2 seconds.
-
-       public static boolean fetchMap(File file, String url) {
-               URL u;
-               long startTime = System.nanoTime();
-
-               try {
-                       u = new URL(url);
-               } catch (java.net.MalformedURLException e) {
-                       return false;
-               }
-
-               byte[] data;
-               try {
-                       URLConnection uc = u.openConnection();
-                       int contentLength = uc.getContentLength();
-                       InputStream in = new BufferedInputStream(uc.getInputStream());
-                       int bytesRead = 0;
-                       int offset = 0;
-                       data = new byte[contentLength];
-                       while (offset < contentLength) {
-                               bytesRead = in.read(data, offset, data.length - offset);
-                               if (bytesRead == -1)
-                                       break;
-                               offset += bytesRead;
-                       }
-                       in.close();
-
-                       if (offset != contentLength) {
-                               return false;
-                       }
-               } catch (IOException e) {
-                       return false;
-               }
-
-               try {
-                       FileOutputStream out = new FileOutputStream(file);
-                       out.write(data);
-                       out.flush();
-                       out.close();
-               } catch (FileNotFoundException e) {
-                       return false;
-               } catch (IOException e) {
-                       if (file.exists()) {
-                               file.delete();
-                       }
-                       return false;
-               }
-
-               long duration_ms = (System.nanoTime() - startTime) / 1000000;
-               if (duration_ms < google_maps_ratelimit_ms) {
-                       try {
-                               Thread.sleep(google_maps_ratelimit_ms - duration_ms);
-                       } catch (InterruptedException e) {
-                               Thread.currentThread().interrupt();
-                       }
-               }
-
-               return true;
-       }
-
-       public static ImageIcon fetchAndLoadMap(File pngfile, String url) {
-               if (!pngfile.exists()) {
-                       if (!fetchMap(pngfile, url)) {
-                               return null;
-                       }
-               }
-               return loadMap(pngfile, url);
-       }
-
-       public static ImageIcon loadMap(File pngfile, String url) {
-               if (!pngfile.exists()) {
-                       return null;
-               }
-
-               try {
-                       BufferedImage   img;
-
-                       img = ImageIO.read(pngfile);
-                       if (img == null) {
-                               System.out.printf("# Can't read pngfile %s\n", pngfile);
-                               return null;
-                       }
-                       return new ImageIcon(img);
-               } catch (IOException e) {
-                       System.out.printf("# IO error trying to load %s\n", pngfile);
-                       return null;
-               }
-       }
-}
diff --git a/altosui/AltosSiteMapPreload.java b/altosui/AltosSiteMapPreload.java
deleted file mode 100644 (file)
index 8380b96..0000000
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * 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; 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 altosui;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.util.*;
-import java.text.*;
-import java.lang.Math;
-import java.net.URL;
-import java.net.URLConnection;
-import org.altusmetrum.altosuilib_1.*;
-
-class AltosMapPos extends Box {
-       AltosUI         owner;
-       JLabel          label;
-       JComboBox       hemi;
-       JTextField      deg;
-       JLabel          deg_label;
-       JTextField      min;
-       JLabel          min_label;
-
-       public void set_value(double new_value) {
-               double  d, m;
-               int     h;
-
-               h = 0;
-               if (new_value < 0) {
-                       h = 1;
-                       new_value = -new_value;
-               }
-               d = Math.floor(new_value);
-               deg.setText(String.format("%3.0f", d));
-               m = (new_value - d) * 60.0;
-               min.setText(String.format("%7.4f", m));
-               hemi.setSelectedIndex(h);
-       }
-
-       public double get_value() throws NumberFormatException {
-               int     h = hemi.getSelectedIndex();
-               String  d_t = deg.getText();
-               String  m_t = min.getText();
-               double  d, m, v;
-               try {
-                       d = Double.parseDouble(d_t);
-               } catch (NumberFormatException ne) {
-                       JOptionPane.showMessageDialog(owner,
-                                                     String.format("Invalid degrees \"%s\"",
-                                                                   d_t),
-                                                     "Invalid number",
-                                                     JOptionPane.ERROR_MESSAGE);
-                       throw ne;
-               }
-               try {
-                       if (m_t.equals(""))
-                               m = 0;
-                       else
-                               m = Double.parseDouble(m_t);
-               } catch (NumberFormatException ne) {
-                       JOptionPane.showMessageDialog(owner,
-                                                     String.format("Invalid minutes \"%s\"",
-                                                                   m_t),
-                                                     "Invalid number",
-                                                     JOptionPane.ERROR_MESSAGE);
-                       throw ne;
-               }
-               v = d + m/60.0;
-               if (h == 1)
-                       v = -v;
-               return v;
-       }
-
-       public AltosMapPos(AltosUI in_owner,
-                          String label_value,
-                          String[] hemi_names,
-                          double default_value) {
-               super(BoxLayout.X_AXIS);
-               owner = in_owner;
-               label = new JLabel(label_value);
-               hemi = new JComboBox(hemi_names);
-               hemi.setEditable(false);
-               deg = new JTextField(5);
-               deg.setMinimumSize(deg.getPreferredSize());
-               deg.setHorizontalAlignment(JTextField.RIGHT);
-               deg_label = new JLabel("°");
-               min = new JTextField(9);
-               min.setMinimumSize(min.getPreferredSize());
-               min_label = new JLabel("'");
-               set_value(default_value);
-               add(label);
-               add(Box.createRigidArea(new Dimension(5, 0)));
-               add(hemi);
-               add(Box.createRigidArea(new Dimension(5, 0)));
-               add(deg);
-               add(Box.createRigidArea(new Dimension(5, 0)));
-               add(deg_label);
-               add(Box.createRigidArea(new Dimension(5, 0)));
-               add(min);
-               add(Box.createRigidArea(new Dimension(5, 0)));
-               add(min_label);
-       }
-}
-
-class AltosSite {
-       String  name;
-       double  latitude;
-       double  longitude;
-
-       public String toString() {
-               return name;
-       }
-
-       public AltosSite(String in_name, double in_latitude, double in_longitude) {
-               name = in_name;
-               latitude = in_latitude;
-               longitude = in_longitude;
-       }
-
-       public AltosSite(String line) throws ParseException {
-               String[]        elements = line.split(":");
-
-               if (elements.length < 3)
-                       throw new ParseException(String.format("Invalid site line %s", line), 0);
-
-               name = elements[0];
-
-               try {
-                       latitude = Double.parseDouble(elements[1]);
-                       longitude = Double.parseDouble(elements[2]);
-               } catch (NumberFormatException ne) {
-                       throw new ParseException(String.format("Invalid site line %s", line), 0);
-               }
-       }
-}
-
-class AltosSites extends Thread {
-       AltosSiteMapPreload     preload;
-       URL                     url;
-       LinkedList<AltosSite>   sites;
-
-       void notify_complete() {
-               SwingUtilities.invokeLater(new Runnable() {
-                               public void run() {
-                                       preload.set_sites();
-                               }
-                       });
-       }
-
-       void add(AltosSite site) {
-               sites.add(site);
-       }
-
-       void add(String line) {
-               try {
-                       add(new AltosSite(line));
-               } catch (ParseException pe) {
-               }
-       }
-
-       public void run() {
-               try {
-                       URLConnection uc = url.openConnection();
-                       //int length = uc.getContentLength();
-
-                       InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), Altos.unicode_set);
-                       BufferedReader in = new BufferedReader(in_stream);
-
-                       for (;;) {
-                               String line = in.readLine();
-                               if (line == null)
-                                       break;
-                               add(line);
-                       }
-               } catch (IOException e) {
-               } finally {
-                       notify_complete();
-               }
-       }
-
-       public AltosSites(AltosSiteMapPreload in_preload) {
-               sites = new LinkedList<AltosSite>();
-               preload = in_preload;
-               try {
-                       url = new URL(Altos.launch_sites_url);
-               } catch (java.net.MalformedURLException e) {
-                       notify_complete();
-               }
-               start();
-       }
-}
-
-public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener, ItemListener {
-       AltosUI         owner;
-       AltosSiteMap    map;
-
-       AltosMapPos     lat;
-       AltosMapPos     lon;
-
-       final static int        radius = 5;
-       final static int        width = (radius * 2 + 1);
-       final static int        height = (radius * 2 + 1);
-
-       JProgressBar    pbar;
-
-       AltosSites      sites;
-       JLabel          site_list_label;
-       JComboBox       site_list;
-
-       JToggleButton   load_button;
-       boolean         loading;
-       JButton         close_button;
-
-       static final String[]   lat_hemi_names = { "N", "S" };
-       static final String[]   lon_hemi_names = { "E", "W" };
-
-       class updatePbar implements Runnable {
-               int             n;
-               String          s;
-
-               public updatePbar(int x, int y, String in_s) {
-                       n = (x + radius) + (y + radius) * width + 1;
-                       s = in_s;
-               }
-
-               public void run() {
-                       pbar.setValue(n);
-                       pbar.setString(s);
-                       if (n < width * height) {
-                               pbar.setValue(n);
-                               pbar.setString(s);
-                       } else {
-                               pbar.setValue(0);
-                               pbar.setString("");
-                               load_button.setSelected(false);
-                               loading = false;
-                       }
-               }
-       }
-
-       class bgLoad extends Thread {
-
-               AltosSiteMap    map;
-
-               public bgLoad(AltosSiteMap in_map) {
-                       map = in_map;
-               }
-
-               public void run() {
-                       for (int y = -map.radius; y <= map.radius; y++) {
-                               for (int x = -map.radius; x <= map.radius; x++) {
-                                       String  pngfile;
-                                       pngfile = map.initMap(new Point(x,y));
-                                       SwingUtilities.invokeLater(new updatePbar(x, y, pngfile));
-                               }
-                       }
-               }
-       }
-
-       public void set_sites() {
-               int     i = 1;
-               for (AltosSite site : sites.sites) {
-                       site_list.insertItemAt(site, i);
-                       i++;
-               }
-       }
-
-       public void itemStateChanged(ItemEvent e) {
-               int             state = e.getStateChange();
-
-               if (state == ItemEvent.SELECTED) {
-                       Object  o = e.getItem();
-                       if (o instanceof AltosSite) {
-                               AltosSite       site = (AltosSite) o;
-                               lat.set_value(site.latitude);
-                               lon.set_value(site.longitude);
-                       }
-               }
-       }
-
-       public void actionPerformed(ActionEvent e) {
-               String  cmd = e.getActionCommand();
-
-               if (cmd.equals("close"))
-                       setVisible(false);
-
-               if (cmd.equals("load")) {
-                       if (!loading) {
-                               try {
-                                       final double    latitude = lat.get_value();
-                                       final double    longitude = lon.get_value();
-                                       map.setBaseLocation(latitude,longitude);
-                                       map.draw_circle(latitude,longitude);
-                                       loading = true;
-                                       bgLoad thread = new bgLoad(map);
-                                       thread.start();
-                               } catch (NumberFormatException ne) {
-                                       load_button.setSelected(false);
-                               }
-                       }
-               }
-       }
-
-       public AltosSiteMapPreload(AltosUI in_owner) {
-               owner = in_owner;
-
-               Container               pane = getContentPane();
-               GridBagConstraints      c = new GridBagConstraints();
-               Insets                  i = new Insets(4,4,4,4);
-
-               pane.setLayout(new GridBagLayout());
-
-               map = new AltosSiteMap(radius);
-
-               c.fill = GridBagConstraints.BOTH;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 1;
-
-               c.gridx = 0;
-               c.gridy = 0;
-               c.gridwidth = 2;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(map, c);
-
-               pbar = new JProgressBar();
-               pbar.setMinimum(0);
-               pbar.setMaximum(width * height);
-               pbar.setValue(0);
-               pbar.setString("");
-               pbar.setStringPainted(true);
-
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 0;
-
-               c.gridx = 0;
-               c.gridy = 1;
-               c.gridwidth = 2;
-
-               pane.add(pbar, c);
-
-               site_list_label = new JLabel ("Known Launch Sites:");
-
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 0;
-
-               c.gridx = 0;
-               c.gridy = 2;
-               c.gridwidth = 1;
-
-               pane.add(site_list_label, c);
-
-               site_list = new JComboBox(new String[] { "Site List" });
-               site_list.addItemListener(this);
-
-               sites = new AltosSites(this);
-
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 0;
-
-               c.gridx = 1;
-               c.gridy = 2;
-               c.gridwidth = 1;
-
-               pane.add(site_list, c);
-
-               lat = new AltosMapPos(owner,
-                                     "Latitude:",
-                                     lat_hemi_names,
-                                     37.167833333);
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 0;
-               c.weighty = 0;
-
-               c.gridx = 0;
-               c.gridy = 3;
-               c.gridwidth = 1;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(lat, c);
-
-               lon = new AltosMapPos(owner,
-                                     "Longitude:",
-                                     lon_hemi_names,
-                                     -97.73975);
-
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 0;
-               c.weighty = 0;
-
-               c.gridx = 1;
-               c.gridy = 3;
-               c.gridwidth = 1;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(lon, c);
-
-               load_button = new JToggleButton("Load Map");
-               load_button.addActionListener(this);
-               load_button.setActionCommand("load");
-
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 0;
-
-               c.gridx = 0;
-               c.gridy = 4;
-               c.gridwidth = 1;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(load_button, c);
-
-               close_button = new JButton("Close");
-               close_button.addActionListener(this);
-               close_button.setActionCommand("close");
-
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.CENTER;
-               c.insets = i;
-               c.weightx = 1;
-               c.weighty = 0;
-
-               c.gridx = 1;
-               c.gridy = 4;
-               c.gridwidth = 1;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(close_button, c);
-
-               pack();
-               setLocationRelativeTo(owner);
-               setVisible(true);
-       }
-}
diff --git a/altosui/AltosSiteMapTile.java b/altosui/AltosSiteMapTile.java
deleted file mode 100644 (file)
index 7d5b65e..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * 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 altosui;
-
-import java.awt.*;
-import java.awt.image.*;
-import javax.swing.*;
-import java.awt.geom.Point2D;
-import java.awt.geom.Line2D;
-import org.altusmetrum.altoslib_3.*;
-
-public class AltosSiteMapTile extends JLayeredPane {
-       JLabel mapLabel;
-       JLabel draw;
-       Graphics2D g2d;
-       int px_size;
-
-       public void loadMap(ImageIcon icn) {
-               mapLabel.setIcon(icn);
-       }
-
-       public void clearMap() {
-               fillLabel(mapLabel, Color.GRAY, px_size);
-               g2d = fillLabel(draw, new Color(127,127,127,0), px_size);
-               g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                                    RenderingHints.VALUE_ANTIALIAS_ON);
-               g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-       }
-
-       static Color stateColors[] = {
-               Color.WHITE,  // startup
-               Color.WHITE,  // idle
-               Color.WHITE,  // pad
-               Color.RED,    // boost
-               Color.PINK,   // fast
-               Color.YELLOW, // coast
-               Color.CYAN,   // drogue
-               Color.BLUE,   // main
-               Color.BLACK   // landed
-       };
-
-       private boolean drawn_landed_circle = false;
-       private boolean drawn_boost_circle = false;
-       public synchronized void show(AltosState state, AltosListenerState listener_state,
-                                     Point2D.Double last_pt, Point2D.Double pt)
-       {
-               if (0 <= state.state && state.state < stateColors.length) {
-                       g2d.setColor(stateColors[state.state]);
-               }
-               g2d.draw(new Line2D.Double(last_pt, pt));
-
-               if (state.state == 3 && !drawn_boost_circle) {
-                       drawn_boost_circle = true;
-                       g2d.setColor(Color.RED);
-                       g2d.drawOval((int)last_pt.x-5, (int)last_pt.y-5, 10, 10);
-                       g2d.drawOval((int)last_pt.x-20, (int)last_pt.y-20, 40, 40);
-                       g2d.drawOval((int)last_pt.x-35, (int)last_pt.y-35, 70, 70);
-               }
-               if (state.state == 8 && !drawn_landed_circle) {
-                       drawn_landed_circle = true;
-                       g2d.setColor(Color.BLACK);
-                       g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
-                       g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
-                       g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
-               }
-
-               repaint();
-       }
-
-       public void draw_circle(Point2D.Double pt) {
-               g2d.setColor(Color.RED);
-               g2d.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
-               g2d.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
-               g2d.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
-       }
-
-       public static Graphics2D fillLabel(JLabel l, Color c, int px_size) {
-               BufferedImage img = new BufferedImage(px_size, px_size,
-                                                     BufferedImage.TYPE_INT_ARGB);
-               Graphics2D g = img.createGraphics();
-               g.setColor(c);
-               g.fillRect(0, 0, px_size, px_size);
-               l.setIcon(new ImageIcon(img));
-               return g;
-       }
-
-       public AltosSiteMapTile(int in_px_size) {
-               px_size = in_px_size;
-               setPreferredSize(new Dimension(px_size, px_size));
-
-               mapLabel = new JLabel();
-               fillLabel(mapLabel, Color.GRAY, px_size);
-               mapLabel.setOpaque(true);
-               mapLabel.setBounds(0, 0, px_size, px_size);
-               add(mapLabel, new Integer(0));
-
-               draw = new JLabel();
-               g2d = fillLabel(draw, new Color(127, 127, 127, 0), px_size);
-               g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                                    RenderingHints.VALUE_ANTIALIAS_ON);
-               g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-               draw.setBounds(0, 0, px_size, px_size);
-               draw.setOpaque(false);
-
-               add(draw, new Integer(1));
-       }
-}
diff --git a/altosui/AltosVoice.java b/altosui/AltosVoice.java
deleted file mode 100644 (file)
index 2ed6a8c..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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 altosui;
-
-import com.sun.speech.freetts.Voice;
-import com.sun.speech.freetts.VoiceManager;
-import java.util.concurrent.LinkedBlockingQueue;
-import org.altusmetrum.altosuilib_1.*;
-
-public class AltosVoice implements Runnable {
-       VoiceManager                    voice_manager;
-       Voice                           voice;
-       LinkedBlockingQueue<String>     phrases;
-       Thread                          thread;
-       boolean                         busy;
-
-       final static String voice_name = "kevin16";
-
-       public void run() {
-               try {
-                       for (;;) {
-                               String s = phrases.take();
-                               voice.speak(s);
-                               synchronized(this) {
-                                       if (phrases.isEmpty()) {
-                                               busy = false;
-                                               notifyAll();
-                                       }
-                               }
-                       }
-               } catch (InterruptedException e) {
-               }
-       }
-
-       public synchronized void drain() throws InterruptedException {
-               while (busy)
-                       wait();
-       }
-
-       public void speak_always(String s) {
-               try {
-                       if (voice != null) {
-                               synchronized(this) {
-                                       busy = true;
-                                       phrases.put(s);
-                               }
-                       }
-               } catch (InterruptedException e) {
-               }
-       }
-
-       public void speak(String s) {
-               if (AltosUIPreferences.voice())
-                       speak_always(s);
-       }
-
-       public void speak(String format, Object... parameters) {
-               speak(String.format(format, parameters));
-       }
-
-       public AltosVoice () {
-               busy = false;
-               voice_manager = VoiceManager.getInstance();
-               voice = voice_manager.getVoice(voice_name);
-               if (voice != null) {
-                       voice.allocate();
-                       phrases = new LinkedBlockingQueue<String> ();
-                       thread = new Thread(this);
-                       thread.start();
-               } else {
-                       System.out.printf("Voice manager failed to open %s\n", voice_name);
-                       Voice[] voices = voice_manager.getVoices();
-                       System.out.printf("Available voices:\n");
-                       for (int i = 0; i < voices.length; i++) {
-                               System.out.println("    " + voices[i].getName()
-                                                  + " (" + voices[i].getDomain() + " domain)");
-                       }
-               }
-       }
-}
diff --git a/altosui/GrabNDrag.java b/altosui/GrabNDrag.java
deleted file mode 100644 (file)
index 2fd6c4d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
- *
- * 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 altosui;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import javax.swing.event.MouseInputAdapter;
-
-class GrabNDrag extends MouseInputAdapter {
-       private JComponent scroll;
-       private Point startPt = new Point();
-
-       public GrabNDrag(JComponent scroll) {
-               this.scroll = scroll;
-               scroll.addMouseMotionListener(this);
-               scroll.addMouseListener(this);
-               scroll.setAutoscrolls(true);
-       }
-
-       public void mousePressed(MouseEvent e) {
-               startPt.setLocation(e.getPoint());
-       }
-       public void mouseDragged(MouseEvent e) {
-               int xd = e.getX() - startPt.x;
-               int yd = e.getY() - startPt.y;
-
-               Rectangle r = scroll.getVisibleRect();
-               r.x -= xd;
-               r.y -= yd;
-               scroll.scrollRectToVisible(r);
-       }
-}
index 76fe9961fb15c170f5e4d6289b8ace43bf9f6bd2..0440b4afb479eae8a6ad55a591fa0359ac2095d8 100644 (file)
@@ -1,6 +1,6 @@
 
 JAVAROOT=classes
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
 
 man_MANS=altosui.1
 
@@ -17,7 +17,6 @@ altosui_BT = \
        AltosBTKnown.java
 
 altosui_JAVA = \
-       GrabNDrag.java \
        AltosAscent.java \
        AltosChannelMenu.java \
        AltosCompanionInfo.java \
@@ -31,20 +30,16 @@ altosui_JAVA = \
        AltosCSVUI.java \
        AltosDescent.java \
        AltosDeviceUIDialog.java \
-       AltosDisplayThread.java \
        AltosEepromDelete.java \
        AltosEepromManage.java \
        AltosEepromMonitorUI.java \
        AltosEepromSelect.java \
        AltosFlashUI.java \
-       AltosFlightDisplay.java \
        AltosFlightInfoTableModel.java \
-       AltosFlightStats.java \
        AltosFlightStatsTable.java \
        AltosFlightStatus.java \
        AltosFlightStatusUpdate.java \
        AltosFlightUI.java \
-       AltosFreqList.java \
        Altos.java \
        AltosIdleMonitorUI.java \
        AltosIgniteUI.java \
@@ -61,17 +56,12 @@ altosui_JAVA = \
        AltosScanUI.java \
        AltosSerial.java \
        AltosSerialInUseException.java \
-       AltosSiteMap.java \
-       AltosSiteMapPreload.java \
-       AltosSiteMapCache.java \
-       AltosSiteMapTile.java \
        AltosUI.java \
        AltosGraph.java \
        AltosGraphDataPoint.java \
        AltosGraphDataSet.java \
        AltosGraphUI.java \
        AltosDataChooser.java \
-       AltosVoice.java \
        $(altosui_BT)
 
 JFREECHART_CLASS= \
diff --git a/altosuilib/AltosDisplayThread.java b/altosuilib/AltosDisplayThread.java
new file mode 100644 (file)
index 0000000..e88a891
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import java.io.*;
+import java.text.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosDisplayThread 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     reported_landing;
+               int     report_interval;
+               long    report_time;
+
+               public synchronized void report(boolean last) {
+                       if (state == null)
+                               return;
+
+                       /* reset the landing count once we hear about a new flight */
+                       if (state.state < AltosLib.ao_flight_drogue)
+                               reported_landing = 0;
+
+                       /* Shut up once the rocket is on the ground */
+                       if (reported_landing > 2) {
+                               return;
+                       }
+
+                       /* If the rocket isn't on the pad, then report height */
+                       if (AltosLib.ao_flight_drogue <= state.state &&
+                           state.state < AltosLib.ao_flight_landed &&
+                           state.from_pad != null &&
+                           state.range >= 0)
+                       {
+                               voice.speak("Height %s, bearing %s %d, elevation %d, range %s.\n",
+                                           AltosConvert.height.say(state.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 if (state.state > AltosLib.ao_flight_pad) {
+                               voice.speak(AltosConvert.height.say_units(state.height()));
+                       } else {
+                               reported_landing = 0;
+                       }
+
+                       /* If the rocket is coming down, check to see if it has landed;
+                        * either we've got a landed report or we haven't heard from it in
+                        * a long time
+                        */
+                       if (state.state >= AltosLib.ao_flight_drogue &&
+                           (last ||
+                            System.currentTimeMillis() - state.received_time >= 15000 ||
+                            state.state == AltosLib.ao_flight_landed))
+                       {
+                               if (Math.abs(state.speed()) < 20 && state.height() < 100)
+                                       voice.speak("rocket landed safely");
+                               else
+                                       voice.speak("rocket may have crashed");
+                               if (state.from_pad != null)
+                                       voice.speak("Bearing %d degrees, range %s.",
+                                                   (int) (state.from_pad.bearing + 0.5),
+                                                   AltosConvert.distance.say_units(state.from_pad.distance));
+                               ++reported_landing;
+                               if (state.state != AltosLib.ao_flight_landed) {
+                                       state.state = AltosLib.ao_flight_landed;
+                                       show_safely();
+                               }
+                       }
+               }
+
+               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() {
+                       reported_landing = 0;
+                       report_interval = 10000;
+               }
+       }
+
+       synchronized boolean tell() {
+               boolean ret = false;
+               if (old_state == null || old_state.state != state.state) {
+                       voice.speak(state.state_name());
+                       if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
+                           state.state > AltosLib.ao_flight_boost) {
+                               voice.speak("max speed: %s.",
+                                           AltosConvert.speed.say_units(state.max_speed() + 0.5));
+                               ret = true;
+                       } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
+                                  state.state >= AltosLib.ao_flight_drogue) {
+                               voice.speak("max height: %s.",
+                                           AltosConvert.height.say_units(state.max_height() + 0.5));
+                               ret = true;
+                       }
+               }
+               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 AltosDisplayThread(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();
+       }
+}
diff --git a/altosuilib/AltosFlightDisplay.java b/altosuilib/AltosFlightDisplay.java
new file mode 100644 (file)
index 0000000..5695a01
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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.altosuilib_2;
+
+import org.altusmetrum.altoslib_4.*;
+
+public interface AltosFlightDisplay {
+       void reset();
+
+       void show(AltosState state, AltosListenerState listener_state);
+
+       void set_font();
+}
diff --git a/altosuilib/AltosFreqList.java b/altosuilib/AltosFreqList.java
new file mode 100644 (file)
index 0000000..e1299aa
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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; 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.altosuilib_2;
+
+import javax.swing.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosFreqList extends JComboBox<AltosFrequency> {
+
+       String  product;
+       int     serial;
+       int     calibrate;
+
+       public void set_frequency(double new_frequency) {
+               int i;
+
+               if (new_frequency < 0) {
+                       setVisible(false);
+                       return;
+               }
+
+               for (i = 0; i < getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
+
+                       if (f.close(new_frequency)) {
+                               setSelectedIndex(i);
+                               return;
+                       }
+               }
+               for (i = 0; i < getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
+
+                       if (new_frequency < f.frequency)
+                               break;
+               }
+               String  description = String.format("%s serial %d", product, serial);
+               AltosFrequency  frequency = new AltosFrequency(new_frequency, description);
+               AltosUIPreferences.add_common_frequency(frequency);
+               insertItemAt(frequency, i);
+               setMaximumRowCount(getItemCount());
+       }
+
+       public void set_product(String new_product) {
+               product = new_product;
+       }
+
+       public void set_serial(int new_serial) {
+               serial = new_serial;
+       }
+
+       public double frequency() {
+               AltosFrequency  f = (AltosFrequency) getSelectedItem();
+               if (f != null)
+                       return f.frequency;
+               return 434.550;
+       }
+
+       public AltosFreqList () {
+               super(AltosUIPreferences.common_frequencies());
+               setMaximumRowCount(getItemCount());
+               setEditable(false);
+               product = "Unknown";
+               serial = 0;
+       }
+
+       public AltosFreqList(double in_frequency) {
+               this();
+               set_frequency(in_frequency);
+       }
+}
diff --git a/altosuilib/AltosSiteMap.java b/altosuilib/AltosSiteMap.java
new file mode 100644 (file)
index 0000000..1cfbc8b
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altosuilib_2;
+
+import java.awt.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.Point2D;
+import java.util.concurrent.*;
+import org.altusmetrum.altoslib_4.*;
+
+public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay {
+       // preferred vertical step in a tile in naut. miles
+       // will actually choose a step size between x and 2x, where this
+       // is 1.5x
+       static final double tile_size_nmi = 0.75;
+
+       static final int px_size = 512;
+
+       static final int MAX_TILE_DELTA = 100;
+
+       private static Point2D.Double translatePoint(Point2D.Double p,
+                       Point2D.Double d)
+       {
+               return new Point2D.Double(p.x + d.x, p.y + d.y);
+       }
+
+       static class LatLng {
+               public double lat, lng;
+               public LatLng(double lat, double lng) {
+                       this.lat = lat;
+                       this.lng = lng;
+               }
+       }
+
+       // based on google js
+       //  http://maps.gstatic.com/intl/en_us/mapfiles/api-3/2/10/main.js
+       // search for fromLatLngToPoint and fromPointToLatLng
+       /*
+       private static Point2D.Double pt(LatLng latlng, int zoom) {
+               double scale_x = 256/360.0 * Math.pow(2, zoom);
+               double scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+               return pt(latlng, scale_x, scale_y);
+       }
+       */
+
+       private static Point2D.Double pt(LatLng latlng,
+                                        double scale_x, double scale_y)
+       {
+               Point2D.Double res = new Point2D.Double();
+               double e;
+
+               res.x = latlng.lng * scale_x;
+
+               e = Math.sin(Math.toRadians(latlng.lat));
+               e = Math.max(e,-(1-1.0E-15));
+               e = Math.min(e,  1-1.0E-15 );
+
+               res.y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
+               return res;
+       }
+
+       static private LatLng latlng(Point2D.Double pt,
+                                    double scale_x, double scale_y)
+       {
+               double lat, lng;
+               double rads;
+
+               lng = pt.x/scale_x;
+               rads = 2 * Math.atan(Math.exp(-pt.y/scale_y));
+               lat = Math.toDegrees(rads - Math.PI/2);
+
+               return new LatLng(lat,lng);
+       }
+
+       int zoom;
+       double scale_x, scale_y;
+
+       int radius;     /* half width/height of tiles to load */
+
+       private Point2D.Double pt(double lat, double lng) {
+               return pt(new LatLng(lat, lng), scale_x, scale_y);
+       }
+
+       private LatLng latlng(double x, double y) {
+               return latlng(new Point2D.Double(x,y), scale_x, scale_y);
+       }
+       /*
+       private LatLng latlng(Point2D.Double pt) {
+               return latlng(pt, scale_x, scale_y);
+       }
+       */
+
+       ConcurrentHashMap<Point,AltosSiteMapTile> mapTiles = new ConcurrentHashMap<Point,AltosSiteMapTile>();
+       Point2D.Double centre;
+
+       private Point2D.Double tileCoordOffset(Point p) {
+               return new Point2D.Double(centre.x - p.x*px_size,
+                                         centre.y - p.y * px_size);
+       }
+
+       private Point tileOffset(Point2D.Double p) {
+               return new Point((int)Math.floor((centre.x+p.x)/px_size),
+                                (int)Math.floor((centre.y+p.y)/px_size));
+       }
+
+       private Point2D.Double getBaseLocation(double lat, double lng) {
+               Point2D.Double locn, north_step;
+
+               zoom = 2;
+               // stupid loop structure to please Java's control flow analysis
+               do {
+                       zoom++;
+                       scale_x = 256/360.0 * Math.pow(2, zoom);
+                       scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+                       locn = pt(lat, lng);
+                       north_step = pt(lat+tile_size_nmi*4/3/60.0, lng);
+                       if (locn.y - north_step.y > px_size)
+                               break;
+               } while (zoom < 22);
+               locn.x = -px_size * Math.floor(locn.x/px_size);
+               locn.y = -px_size * Math.floor(locn.y/px_size);
+               return locn;
+       }
+
+       public void reset() {
+               // nothing
+       }
+
+       public void set_font() {
+               // nothing
+       }
+
+       private void loadMap(final AltosSiteMapTile tile,
+                            final File pngfile, String pngurl)
+       {
+               boolean loaded = AltosSiteMapCache.fetchMap(pngfile, pngurl);
+               if (loaded) {
+                       SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               tile.loadMap(pngfile);
+                                       }
+                               });
+               } else {
+                       System.out.printf("# Failed to fetch file %s\n", pngfile);
+                       System.out.printf(" wget -O '%s' '%s'\n", pngfile, pngurl);
+               }
+       }
+
+
+       class AltosSiteMapPrefetch {
+               int     x;
+               int     y;
+               int     result;
+               File    pngfile;
+               String  pngurl;
+       }
+
+       public AltosSiteMapPrefetch prefetchMap(int x, int y) {
+               AltosSiteMapPrefetch    prefetch = new AltosSiteMapPrefetch();
+               LatLng map_latlng = latlng(
+                       -centre.x + x*px_size + px_size/2,
+                       -centre.y + y*px_size + px_size/2);
+               prefetch.pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
+               prefetch.pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
+               if (prefetch.pngfile.exists()) {
+                       prefetch.result = 1;
+               } else if (AltosSiteMapCache.fetchMap(prefetch.pngfile, prefetch.pngurl)) {
+                       prefetch.result = 0;
+               } else {
+                       prefetch.result = -1;
+               }
+               return prefetch;
+       }
+
+       public static void prefetchMaps(double lat, double lng) {
+               int w = AltosSiteMapPreload.width;
+               int h = AltosSiteMapPreload.height;
+               AltosSiteMap asm = new AltosSiteMap(true);
+               asm.centre = asm.getBaseLocation(lat, lng);
+
+               //Point2D.Double p = new Point2D.Double();
+               //Point2D.Double p2;
+               int dx = -w/2, dy = -h/2;
+               for (int y = dy; y < h+dy; y++) {
+                       for (int x = dx; x < w+dx; x++) {
+                               AltosSiteMapPrefetch prefetch = asm.prefetchMap(x, y);
+                               switch (prefetch.result) {
+                               case 1:
+                                       System.out.printf("Already have %s\n", prefetch.pngfile);
+                                       break;
+                               case 0:
+                                       System.out.printf("Fetched map %s\n", prefetch.pngfile);
+                                       break;
+                               case -1:
+                                       System.out.printf("# Failed to fetch file %s\n", prefetch.pngfile);
+                                       System.out.printf(" wget -O '%s' ''\n",
+                                                         prefetch.pngfile, prefetch.pngurl);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       public String initMap(Point offset) {
+               AltosSiteMapTile tile = mapTiles.get(offset);
+               Point2D.Double coord = tileCoordOffset(offset);
+
+               LatLng map_latlng = latlng(px_size/2-coord.x, px_size/2-coord.y);
+
+               File pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
+               String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
+               loadMap(tile, pngfile, pngurl);
+               return pngfile.toString();
+       }
+
+       public void initAndFinishMapAsync (final AltosSiteMapTile tile, final Point offset) {
+               Thread thread = new Thread() {
+                               public void run() {
+                                       initMap(offset);
+                                       finishTileLater(tile, offset);
+                               }
+                       };
+               thread.start();
+       }
+
+       public void setBaseLocation(double lat, double lng) {
+               for (Point k : mapTiles.keySet()) {
+                       AltosSiteMapTile tile = mapTiles.get(k);
+                       tile.clearMap();
+               }
+
+               centre = getBaseLocation(lat, lng);
+               scrollRocketToVisible(pt(lat,lng));
+       }
+
+       private void initMaps(double lat, double lng) {
+               setBaseLocation(lat, lng);
+
+               Thread thread = new Thread() {
+                               public void run() {
+                                       for (Point k : mapTiles.keySet())
+                                               initMap(k);
+                               }
+                       };
+               thread.start();
+       }
+
+       private static File MapFile(double lat, double lng, int zoom) {
+               char chlat = lat < 0 ? 'S' : 'N';
+               char chlng = lng < 0 ? 'W' : 'E';
+               if (lat < 0) lat = -lat;
+               if (lng < 0) lng = -lng;
+               return new File(AltosUIPreferences.mapdir(),
+                               String.format("map-%c%.6f,%c%.6f-%d.png",
+                                             chlat, lat, chlng, lng, zoom));
+       }
+
+       private static String MapURL(double lat, double lng, int zoom) {
+               return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=hybrid&format=png32", lat, lng, zoom, px_size, px_size);
+       }
+
+       boolean initialised = false;
+       Point2D.Double last_pt = null;
+       int last_state = -1;
+
+       public void show(double lat, double lon) {
+               System.out.printf ("show %g %g\n", lat, lon);
+               return;
+//             initMaps(lat, lon);
+//             scrollRocketToVisible(pt(lat, lon));
+       }
+       public void show(final AltosState state, final AltosListenerState listener_state) {
+               // if insufficient gps data, nothing to update
+               AltosGPS        gps = state.gps;
+
+               if (gps == null)
+                       return;
+
+               if (!gps.locked && gps.nsat < 4)
+                       return;
+
+               if (!initialised) {
+                       if (state.pad_lat != AltosLib.MISSING && state.pad_lon != AltosLib.MISSING) {
+                               initMaps(state.pad_lat, state.pad_lon);
+                               initialised = true;
+                       } else if (gps.lat != AltosLib.MISSING && gps.lon != AltosLib.MISSING) {
+                               initMaps(gps.lat, gps.lon);
+                               initialised = true;
+                       } else {
+                               return;
+                       }
+               }
+
+               final Point2D.Double pt = pt(gps.lat, gps.lon);
+               if (last_pt == pt && last_state == state.state)
+                       return;
+
+               if (last_pt == null) {
+                       last_pt = pt;
+               }
+               boolean in_any = false;
+               for (Point offset : mapTiles.keySet()) {
+                       AltosSiteMapTile tile = mapTiles.get(offset);
+                       Point2D.Double ref, lref;
+                       ref = translatePoint(pt, tileCoordOffset(offset));
+                       lref = translatePoint(last_pt, tileCoordOffset(offset));
+                       tile.show(state, listener_state, lref, ref);
+                       if (0 <= ref.x && ref.x < px_size)
+                               if (0 <= ref.y && ref.y < px_size)
+                                       in_any = true;
+               }
+
+               Point offset = tileOffset(pt);
+               if (!in_any) {
+                       Point2D.Double ref, lref;
+                       ref = translatePoint(pt, tileCoordOffset(offset));
+                       lref = translatePoint(last_pt, tileCoordOffset(offset));
+
+                       AltosSiteMapTile tile = createTile(offset);
+                       tile.show(state, listener_state, lref, ref);
+                       initAndFinishMapAsync(tile, offset);
+               }
+
+               scrollRocketToVisible(pt);
+
+               if (offset != tileOffset(last_pt)) {
+                       ensureTilesAround(offset);
+               }
+
+               last_pt = pt;
+               last_state = state.state;
+       }
+
+       public void centre(Point2D.Double pt) {
+               Rectangle r = comp.getVisibleRect();
+               Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft));
+               int dx = (int)copt.x - r.width/2 - r.x;
+               int dy = (int)copt.y - r.height/2 - r.y;
+               r.x += dx;
+               r.y += dy;
+               comp.scrollRectToVisible(r);
+       }
+
+       public void centre(AltosState state) {
+               if (!state.gps.locked && state.gps.nsat < 4)
+                       return;
+               centre(pt(state.gps.lat, state.gps.lon));
+       }
+
+       public void draw_circle(double lat, double lon) {
+               final Point2D.Double pt = pt(lat, lon);
+
+               for (Point offset : mapTiles.keySet()) {
+                       AltosSiteMapTile tile = mapTiles.get(offset);
+                       Point2D.Double ref = translatePoint(pt, tileCoordOffset(offset));
+                       tile.set_boost(ref);
+               }
+       }
+
+       private AltosSiteMapTile createTile(Point offset) {
+               AltosSiteMapTile tile = new AltosSiteMapTile(px_size);
+               mapTiles.put(offset, tile);
+               return tile;
+       }
+       private void finishTileLater(final AltosSiteMapTile tile,
+                                    final Point offset)
+       {
+               SwingUtilities.invokeLater( new Runnable() {
+                       public void run() {
+                               addTileAt(tile, offset);
+                       }
+               } );
+       }
+
+       private void ensureTilesAround(Point base_offset) {
+               for (int x = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
+                               Point offset = new Point(base_offset.x + x, base_offset.y + y);
+                               if (mapTiles.containsKey(offset))
+                                       continue;
+                               AltosSiteMapTile tile = createTile(offset);
+                               initAndFinishMapAsync(tile, offset);
+                       }
+               }
+       }
+
+       private Point topleft = new Point(0,0);
+       private void scrollRocketToVisible(Point2D.Double pt) {
+               Rectangle r = comp.getVisibleRect();
+               Point2D.Double copt = translatePoint(pt, tileCoordOffset(topleft));
+               int dx = (int)copt.x - r.width/2 - r.x;
+               int dy = (int)copt.y - r.height/2 - r.y;
+               if (Math.abs(dx) > r.width/4 || Math.abs(dy) > r.height/4) {
+                       r.x += dx;
+                       r.y += dy;
+                       comp.scrollRectToVisible(r);
+               }
+       }
+
+       private void addTileAt(AltosSiteMapTile tile, Point offset) {
+               if (Math.abs(offset.x) >= MAX_TILE_DELTA ||
+                               Math.abs(offset.y) >= MAX_TILE_DELTA)
+               {
+                       System.out.printf("Rocket too far away from pad (tile %d,%d)\n",
+                                         offset.x, offset.y);
+                       return;
+               }
+
+               boolean review = false;
+               Rectangle r = comp.getVisibleRect();
+               if (offset.x < topleft.x) {
+                       r.x += (topleft.x - offset.x) * px_size;
+                       topleft.x = offset.x;
+                       review = true;
+               }
+               if (offset.y < topleft.y) {
+                       r.y += (topleft.y - offset.y) * px_size;
+                       topleft.y = offset.y;
+                       review = true;
+               }
+               GridBagConstraints c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.BOTH;
+               // put some space between the map tiles, debugging only
+               // c.insets = new Insets(5, 5, 5, 5);
+
+               c.gridx = offset.x + MAX_TILE_DELTA;
+               c.gridy = offset.y + MAX_TILE_DELTA;
+               layout.setConstraints(tile, c);
+
+               comp.add(tile);
+               if (review) {
+                       comp.scrollRectToVisible(r);
+               }
+       }
+
+       private AltosSiteMap(boolean knowWhatYouAreDoing) {
+               if (!knowWhatYouAreDoing) {
+                       throw new RuntimeException("Arggh.");
+               }
+       }
+
+       JComponent comp = new JComponent() { };
+       private GridBagLayout layout = new GridBagLayout();
+
+       public AltosSiteMap(int in_radius) {
+               radius = in_radius;
+
+               GrabNDrag scroller = new GrabNDrag(comp);
+
+               comp.setLayout(layout);
+
+               for (int x = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
+                               Point offset = new Point(x, y);
+                               AltosSiteMapTile t = createTile(offset);
+                               addTileAt(t, offset);
+                       }
+               }
+               setViewportView(comp);
+               setPreferredSize(new Dimension(500,500));
+       }
+
+       public AltosSiteMap() {
+               this(1);
+       }
+}
diff --git a/altosuilib/AltosSiteMapCache.java b/altosuilib/AltosSiteMapCache.java
new file mode 100644 (file)
index 0000000..cf93016
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altosuilib_2;
+
+import javax.swing.*;
+import javax.imageio.ImageIO;
+import java.awt.image.*;
+import java.awt.*;
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+
+
+class AltosCacheImage {
+       Component       component;
+       File            file;
+       VolatileImage   image;
+       int             width;
+       int             height;
+       long            used;
+
+       public void load_image() throws IOException {
+               BufferedImage   bimg = ImageIO.read(file);
+               Graphics2D      g = image.createGraphics();
+               g.drawImage(bimg, 0, 0, null);
+               bimg.flush();
+       }
+
+       public Image validate() {
+               int     returnCode;
+
+               if (image != null)
+                       returnCode = image.validate(component.getGraphicsConfiguration());
+               else
+                       returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
+               if (returnCode == VolatileImage.IMAGE_RESTORED) {
+                       try {
+                               load_image();
+                       } catch (IOException e) {
+                               return null;
+                       }
+               } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
+                       image = component.createVolatileImage(width, height);
+                       try {
+                               load_image();
+                       } catch (IOException e) {
+                               return null;
+                       }
+               }
+               return image;
+       }
+
+       public void flush() {
+               image.flush();
+       }
+
+       public AltosCacheImage(Component component, File file, int w, int h) throws IOException {
+               this.component = component;
+               this.file = file;
+               width = w;
+               height = h;
+               image = component.createVolatileImage(w, h);
+               used = 0;
+       }
+}
+
+public class AltosSiteMapCache extends JLabel {
+       static final long google_maps_ratelimit_ms = 1200;
+       // Google limits static map queries to 50 per minute per IP, so
+       // each query should take at least 1.2 seconds.
+
+       public static boolean fetchMap(File file, String url) {
+               if (file.exists())
+                       return true;
+
+               URL u;
+               long startTime = System.nanoTime();
+
+               try {
+                       u = new URL(url);
+               } catch (java.net.MalformedURLException e) {
+                       return false;
+               }
+
+               byte[] data;
+               try {
+                       URLConnection uc = u.openConnection();
+                       int contentLength = uc.getContentLength();
+                       InputStream in = new BufferedInputStream(uc.getInputStream());
+                       int bytesRead = 0;
+                       int offset = 0;
+                       data = new byte[contentLength];
+                       while (offset < contentLength) {
+                               bytesRead = in.read(data, offset, data.length - offset);
+                               if (bytesRead == -1)
+                                       break;
+                               offset += bytesRead;
+                       }
+                       in.close();
+
+                       if (offset != contentLength) {
+                               return false;
+                       }
+               } catch (IOException e) {
+                       return false;
+               }
+
+               try {
+                       FileOutputStream out = new FileOutputStream(file);
+                       out.write(data);
+                       out.flush();
+                       out.close();
+               } catch (FileNotFoundException e) {
+                       return false;
+               } catch (IOException e) {
+                       if (file.exists()) {
+                               file.delete();
+                       }
+                       return false;
+               }
+
+               long duration_ms = (System.nanoTime() - startTime) / 1000000;
+               if (duration_ms < google_maps_ratelimit_ms) {
+                       try {
+                               Thread.sleep(google_maps_ratelimit_ms - duration_ms);
+                       } catch (InterruptedException e) {
+                               Thread.currentThread().interrupt();
+                       }
+               }
+
+               return true;
+       }
+
+       static int                      cache_size = 9;
+
+       static AltosCacheImage[]        images;
+
+       static long                     used;
+
+       public static void set_cache_size(int cache_size) {
+               AltosSiteMapCache.cache_size = cache_size;
+               images = null;
+       }
+
+       public static Image get_image(Component component, File file, int width, int height) {
+               int             oldest = -1;
+               long            age = used;
+               AltosCacheImage image;
+               if (images == null)
+                       images = new AltosCacheImage[cache_size];
+               for (int i = 0; i < cache_size; i++) {
+                       image = images[i];
+
+                       if (image == null) {
+                               oldest = i;
+                               break;
+                       }
+                       if (image.component == component && file.equals(image.file)) {
+                               image.used = used++;
+                               return image.validate();
+                       }
+                       if (image.used < age) {
+                               oldest = i;
+                               age = image.used;
+                       }
+               }
+
+               try {
+                       image = new AltosCacheImage(component, file, width, height);
+                       image.used = used++;
+                       if (images[oldest] != null)
+                               images[oldest].flush();
+                       images[oldest] = image;
+                       return image.validate();
+               } catch (IOException e) {
+                       return null;
+               }
+       }
+}
diff --git a/altosuilib/AltosSiteMapPreload.java b/altosuilib/AltosSiteMapPreload.java
new file mode 100644 (file)
index 0000000..baa7fc3
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * 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; 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.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosMapPos extends Box {
+       AltosUIFrame    owner;
+       JLabel          label;
+       JComboBox       hemi;
+       JTextField      deg;
+       JLabel          deg_label;
+       JTextField      min;
+       JLabel          min_label;
+
+       public void set_value(double new_value) {
+               double  d, m;
+               int     h;
+
+               h = 0;
+               if (new_value < 0) {
+                       h = 1;
+                       new_value = -new_value;
+               }
+               d = Math.floor(new_value);
+               deg.setText(String.format("%3.0f", d));
+               m = (new_value - d) * 60.0;
+               min.setText(String.format("%7.4f", m));
+               hemi.setSelectedIndex(h);
+       }
+
+       public double get_value() throws NumberFormatException {
+               int     h = hemi.getSelectedIndex();
+               String  d_t = deg.getText();
+               String  m_t = min.getText();
+               double  d, m, v;
+               try {
+                       d = Double.parseDouble(d_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid degrees \"%s\"",
+                                                                   d_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               try {
+                       if (m_t.equals(""))
+                               m = 0;
+                       else
+                               m = Double.parseDouble(m_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid minutes \"%s\"",
+                                                                   m_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               v = d + m/60.0;
+               if (h == 1)
+                       v = -v;
+               return v;
+       }
+
+       public AltosMapPos(AltosUIFrame in_owner,
+                          String label_value,
+                          String[] hemi_names,
+                          double default_value) {
+               super(BoxLayout.X_AXIS);
+               owner = in_owner;
+               label = new JLabel(label_value);
+               hemi = new JComboBox<String>(hemi_names);
+               hemi.setEditable(false);
+               deg = new JTextField(5);
+               deg.setMinimumSize(deg.getPreferredSize());
+               deg.setHorizontalAlignment(JTextField.RIGHT);
+               deg_label = new JLabel("°");
+               min = new JTextField(9);
+               min.setMinimumSize(min.getPreferredSize());
+               min_label = new JLabel("'");
+               set_value(default_value);
+               add(label);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(hemi);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(deg);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(deg_label);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(min);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(min_label);
+       }
+}
+
+class AltosSite {
+       String  name;
+       double  latitude;
+       double  longitude;
+
+       public String toString() {
+               return name;
+       }
+
+       public AltosSite(String in_name, double in_latitude, double in_longitude) {
+               name = in_name;
+               latitude = in_latitude;
+               longitude = in_longitude;
+       }
+
+       public AltosSite(String line) throws ParseException {
+               String[]        elements = line.split(":");
+
+               if (elements.length < 3)
+                       throw new ParseException(String.format("Invalid site line %s", line), 0);
+
+               name = elements[0];
+
+               try {
+                       latitude = Double.parseDouble(elements[1]);
+                       longitude = Double.parseDouble(elements[2]);
+               } catch (NumberFormatException ne) {
+                       throw new ParseException(String.format("Invalid site line %s", line), 0);
+               }
+       }
+}
+
+class AltosSites extends Thread {
+       AltosSiteMapPreload     preload;
+       URL                     url;
+       LinkedList<AltosSite>   sites;
+
+       void notify_complete() {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       preload.set_sites();
+                               }
+                       });
+       }
+
+       void add(AltosSite site) {
+               sites.add(site);
+       }
+
+       void add(String line) {
+               try {
+                       add(new AltosSite(line));
+               } catch (ParseException pe) {
+               }
+       }
+
+       public void run() {
+               try {
+                       URLConnection uc = url.openConnection();
+                       //int length = uc.getContentLength();
+
+                       InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
+                       BufferedReader in = new BufferedReader(in_stream);
+
+                       for (;;) {
+                               String line = in.readLine();
+                               if (line == null)
+                                       break;
+                               add(line);
+                       }
+               } catch (IOException e) {
+               } finally {
+                       notify_complete();
+               }
+       }
+
+       public AltosSites(AltosSiteMapPreload in_preload) {
+               sites = new LinkedList<AltosSite>();
+               preload = in_preload;
+               try {
+                       url = new URL(AltosLib.launch_sites_url);
+               } catch (java.net.MalformedURLException e) {
+                       notify_complete();
+               }
+               start();
+       }
+}
+
+public class AltosSiteMapPreload extends AltosUIDialog implements ActionListener, ItemListener {
+       AltosUIFrame    owner;
+       AltosSiteMap    map;
+
+       AltosMapPos     lat;
+       AltosMapPos     lon;
+
+       final static int        radius = 5;
+       final static int        width = (radius * 2 + 1);
+       final static int        height = (radius * 2 + 1);
+
+       JProgressBar    pbar;
+
+       AltosSites      sites;
+       JLabel          site_list_label;
+       JComboBox<AltosSite>    site_list;
+
+       JToggleButton   load_button;
+       boolean         loading;
+       JButton         close_button;
+
+       static final String[]   lat_hemi_names = { "N", "S" };
+       static final String[]   lon_hemi_names = { "E", "W" };
+
+       class updatePbar implements Runnable {
+               int             n;
+               String          s;
+
+               public updatePbar(int x, int y, String in_s) {
+                       n = (x + radius) + (y + radius) * width + 1;
+                       s = in_s;
+               }
+
+               public void run() {
+                       pbar.setValue(n);
+                       pbar.setString(s);
+                       if (n < width * height) {
+                               pbar.setValue(n);
+                               pbar.setString(s);
+                       } else {
+                               pbar.setValue(0);
+                               pbar.setString("");
+                               load_button.setSelected(false);
+                               loading = false;
+                       }
+               }
+       }
+
+       class bgLoad extends Thread {
+
+               AltosSiteMap    map;
+
+               public bgLoad(AltosSiteMap in_map) {
+                       map = in_map;
+               }
+
+               public void run() {
+                       for (int y = -map.radius; y <= map.radius; y++) {
+                               for (int x = -map.radius; x <= map.radius; x++) {
+                                       String  pngfile;
+                                       pngfile = map.initMap(new Point(x,y));
+                                       SwingUtilities.invokeLater(new updatePbar(x, y, pngfile));
+                               }
+                       }
+               }
+       }
+
+       public void set_sites() {
+               int     i = 1;
+               for (AltosSite site : sites.sites) {
+                       site_list.insertItemAt(site, i);
+                       i++;
+               }
+       }
+
+       public void itemStateChanged(ItemEvent e) {
+               int             state = e.getStateChange();
+
+               if (state == ItemEvent.SELECTED) {
+                       Object  o = e.getItem();
+                       if (o instanceof AltosSite) {
+                               AltosSite       site = (AltosSite) o;
+                               lat.set_value(site.latitude);
+                               lon.set_value(site.longitude);
+                       }
+               }
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("close"))
+                       setVisible(false);
+
+               if (cmd.equals("load")) {
+                       if (!loading) {
+                               try {
+                                       final double    latitude = lat.get_value();
+                                       final double    longitude = lon.get_value();
+                                       map.setBaseLocation(latitude,longitude);
+                                       map.draw_circle(latitude,longitude);
+                                       loading = true;
+                                       bgLoad thread = new bgLoad(map);
+                                       thread.start();
+                               } catch (NumberFormatException ne) {
+                                       load_button.setSelected(false);
+                               }
+                       }
+               }
+       }
+
+       public AltosSiteMapPreload(AltosUIFrame in_owner) {
+               owner = in_owner;
+
+               Container               pane = getContentPane();
+               GridBagConstraints      c = new GridBagConstraints();
+               Insets                  i = new Insets(4,4,4,4);
+
+               pane.setLayout(new GridBagLayout());
+
+               map = new AltosSiteMap(radius);
+
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 2;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(map, c);
+
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum(width * height);
+               pbar.setValue(0);
+               pbar.setString("");
+               pbar.setStringPainted(true);
+
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 2;
+
+               pane.add(pbar, c);
+
+               site_list_label = new JLabel ("Known Launch Sites:");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 2;
+               c.gridwidth = 1;
+
+               pane.add(site_list_label, c);
+
+               site_list = new JComboBox<AltosSite>(new AltosSite[] { new AltosSite("Site List", 0, 0) });
+               site_list.addItemListener(this);
+
+               sites = new AltosSites(this);
+
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridwidth = 1;
+
+               pane.add(site_list, c);
+
+               lat = new AltosMapPos(owner,
+                                     "Latitude:",
+                                     lat_hemi_names,
+                                     37.167833333);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 0;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(lat, c);
+
+               lon = new AltosMapPos(owner,
+                                     "Longitude:",
+                                     lon_hemi_names,
+                                     -97.73975);
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 0;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(lon, c);
+
+               load_button = new JToggleButton("Load Map");
+               load_button.addActionListener(this);
+               load_button.setActionCommand("load");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(load_button, c);
+
+               close_button = new JButton("Close");
+               close_button.addActionListener(this);
+               close_button.setActionCommand("close");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(close_button, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+}
diff --git a/altosuilib/AltosSiteMapTile.java b/altosuilib/AltosSiteMapTile.java
new file mode 100644 (file)
index 0000000..1046d6b
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altosuilib_2;
+
+import java.awt.*;
+import java.awt.image.*;
+import javax.swing.*;
+import javax.imageio.*;
+import java.awt.geom.Point2D;
+import java.awt.geom.Line2D;
+import java.io.*;
+import java.util.*;
+import org.altusmetrum.altoslib_4.*;
+
+class AltosPoint {
+       Point2D.Double  pt;
+       int             state;
+
+       AltosPoint(Point2D.Double pt, int state) {
+               this.pt = pt;
+               this.state = state;
+       }
+}
+
+public class AltosSiteMapTile extends JComponent {
+       int px_size;
+       File file;
+
+       Point2D.Double  boost;
+       Point2D.Double  landed;
+
+       LinkedList<AltosPoint>  points;
+
+       public void loadMap(File pngFile) {
+               file = pngFile;
+               repaint();
+       }
+
+       public void clearMap() {
+               boost = null;
+               landed = null;
+               points = null;
+               file = null;
+       }
+
+       static Color stateColors[] = {
+               Color.WHITE,  // startup
+               Color.WHITE,  // idle
+               Color.WHITE,  // pad
+               Color.RED,    // boost
+               Color.PINK,   // fast
+               Color.YELLOW, // coast
+               Color.CYAN,   // drogue
+               Color.BLUE,   // main
+               Color.BLACK   // landed
+       };
+
+       private void draw_circle(Graphics g, Point2D.Double pt) {
+               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);
+       }
+
+       public void set_boost(Point2D.Double boost) {
+               this.boost = boost;
+               repaint();
+       }
+
+       public void paint(Graphics g) {
+               Graphics2D      g2d = (Graphics2D) g;
+               AltosPoint      prev = null;
+               Image           img = null;
+
+               if (file != null)
+                       img = AltosSiteMapCache.get_image(this, file, px_size, px_size);
+
+               if (img != null) {
+                       g2d.drawImage(img, 0, 0, null);
+               } else {
+                       g2d.setColor(Color.GRAY);
+                       g2d.fillRect(0, 0, getWidth(), getHeight());
+               }
+
+               g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                  RenderingHints.VALUE_ANTIALIAS_ON);
+               g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+               if (points != null) {
+                       for (AltosPoint point : points) {
+                               if (prev != null) {
+                                       if (0 <= point.state && point.state < stateColors.length)
+                                               g2d.setColor(stateColors[point.state]);
+                                       g2d.draw(new Line2D.Double(prev.pt, point.pt));
+                               }
+                               prev = point;
+                       }
+               }
+               if (boost != null) {
+                       g2d.setColor(Color.RED);
+                       draw_circle(g2d, boost);
+               }
+               if (landed != null) {
+                       g2d.setColor(Color.BLACK);
+                       draw_circle(g2d, landed);
+               }
+       }
+
+       public synchronized void show(AltosState state, AltosListenerState listener_state,
+                                     Point2D.Double last_pt, Point2D.Double pt)
+       {
+               if (points == null)
+                       points = new LinkedList<AltosPoint>();
+
+               points.add(new AltosPoint(pt, state.state));
+
+               if (state.state == AltosLib.ao_flight_boost && boost == null)
+                       boost = pt;
+               if (state.state == AltosLib.ao_flight_landed && landed == null)
+                       landed = pt;
+               repaint();
+       }
+
+       public AltosSiteMapTile(int in_px_size) {
+               px_size = in_px_size;
+               setPreferredSize(new Dimension(px_size, px_size));
+       }
+}
diff --git a/altosuilib/AltosVoice.java b/altosuilib/AltosVoice.java
new file mode 100644 (file)
index 0000000..a3995f6
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * 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.altosuilib_2;
+
+import com.sun.speech.freetts.Voice;
+import com.sun.speech.freetts.VoiceManager;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class AltosVoice implements Runnable {
+       VoiceManager                    voice_manager;
+       Voice                           voice;
+       LinkedBlockingQueue<String>     phrases;
+       Thread                          thread;
+       boolean                         busy;
+
+       final static String voice_name = "kevin16";
+
+       public void run() {
+               try {
+                       for (;;) {
+                               String s = phrases.take();
+                               voice.speak(s);
+                               synchronized(this) {
+                                       if (phrases.isEmpty()) {
+                                               busy = false;
+                                               notifyAll();
+                                       }
+                               }
+                       }
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public synchronized void drain() throws InterruptedException {
+               while (busy)
+                       wait();
+       }
+
+       public void speak_always(String s) {
+               try {
+                       if (voice != null) {
+                               synchronized(this) {
+                                       busy = true;
+                                       phrases.put(s);
+                               }
+                       }
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public void speak(String s) {
+               if (AltosUIPreferences.voice())
+                       speak_always(s);
+       }
+
+       public void speak(String format, Object... parameters) {
+               speak(String.format(format, parameters));
+       }
+
+       public AltosVoice () {
+               busy = false;
+               voice_manager = VoiceManager.getInstance();
+               voice = voice_manager.getVoice(voice_name);
+               if (voice != null) {
+                       voice.allocate();
+                       phrases = new LinkedBlockingQueue<String> ();
+                       thread = new Thread(this);
+                       thread.start();
+               } else {
+                       System.out.printf("Voice manager failed to open %s\n", voice_name);
+                       Voice[] voices = voice_manager.getVoices();
+                       System.out.printf("Available voices:\n");
+                       for (int i = 0; i < voices.length; i++) {
+                               System.out.println("    " + voices[i].getName()
+                                                  + " (" + voices[i].getDomain() + " domain)");
+                       }
+               }
+       }
+}
diff --git a/altosuilib/GrabNDrag.java b/altosuilib/GrabNDrag.java
new file mode 100644 (file)
index 0000000..5e5fdd5
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
+ *
+ * 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.altosuilib_2;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.MouseInputAdapter;
+
+class GrabNDrag extends MouseInputAdapter {
+       private JComponent scroll;
+       private Point startPt = new Point();
+
+       public GrabNDrag(JComponent scroll) {
+               this.scroll = scroll;
+               scroll.addMouseMotionListener(this);
+               scroll.addMouseListener(this);
+               scroll.setAutoscrolls(true);
+       }
+
+       public void mousePressed(MouseEvent e) {
+               startPt.setLocation(e.getPoint());
+       }
+       public void mouseDragged(MouseEvent e) {
+               int xd = e.getX() - startPt.x;
+               int yd = e.getY() - startPt.y;
+
+               Rectangle r = scroll.getVisibleRect();
+               r.x -= xd;
+               r.y -= yd;
+               scroll.scrollRectToVisible(r);
+       }
+}
index 4b22af1f8eda6fab2ed14cc710c7ce16db3cbfee..b7d624e2e4843b0c4778b0b683bbeb3fb498911a 100644 (file)
@@ -1,4 +1,4 @@
-AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6
+AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6
 
 JAVAROOT=bin
 
@@ -9,8 +9,10 @@ SRC=.
 altosuilibdir = $(datadir)/java
 
 altosuilib_JAVA = \
+       GrabNDrag.java \
        AltosDevice.java \
        AltosDeviceDialog.java \
+       AltosFlightDisplay.java \
        AltosFontListener.java \
        AltosPositionListener.java \
        AltosUIConfigure.java \
@@ -30,7 +32,14 @@ altosuilib_JAVA = \
        AltosUIPreferences.java \
        AltosUISeries.java \
        AltosUIVersion.java \
-       AltosUSBDevice.java
+       AltosUSBDevice.java \
+       AltosSiteMap.java \
+       AltosSiteMapCache.java \
+       AltosSiteMapPreload.java \
+       AltosSiteMapTile.java \
+       AltosVoice.java \
+       AltosDisplayThread.java \
+       AltosFreqList.java
 
 JAR=altosuilib_$(ALTOSUILIB_VERSION).jar