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>
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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;
+ }
+ }
+}
-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
AltosFlash.java \
AltosFlashListener.java \
AltosFlightReader.java \
+ AltosFlightStats.java \
AltosFrequency.java \
AltosGPS.java \
AltosGPSSat.java \
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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);
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * Copyright © 2011 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; 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);
- }
-}
+++ /dev/null
-/*
- * 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));
- }
-}
+++ /dev/null
-/*
- * 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)");
- }
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
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
AltosBTKnown.java
altosui_JAVA = \
- GrabNDrag.java \
AltosAscent.java \
AltosChannelMenu.java \
AltosCompanionInfo.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 \
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= \
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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);
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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)");
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
-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
altosuilibdir = $(datadir)/java
altosuilib_JAVA = \
+ GrabNDrag.java \
AltosDevice.java \
AltosDeviceDialog.java \
+ AltosFlightDisplay.java \
AltosFontListener.java \
AltosPositionListener.java \
AltosUIConfigure.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