Initial Commit - Copy from Altus Metrum AltOS

This commit is contained in:
2024-06-25 19:03:04 +02:00
commit 13fc49c923
2048 changed files with 1206748 additions and 0 deletions

3
altosuilib/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
bin
classaltosuilib.stamp
altosuilib*.jar

View File

@@ -0,0 +1,129 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import libaltosJNI.*;
import org.altusmetrum.altoslib_14.*;
public class AltosBTDevice extends altos_bt_device implements AltosDevice {
public String getProductName() {
String name = getName();
if (name == null)
return "Altus Metrum";
int dash = name.lastIndexOf("-");
if (dash < 0)
return name;
return name.substring(0,dash);
}
public int getProduct() {
if (AltosLib.bt_product_telebt.equals(getProductName()))
return AltosLib.product_telebt;
return 0;
}
public String getPath() {
return getAddr();
}
public String getErrorString() {
altos_error error = new altos_error();
libaltos.altos_get_last_error(error);
return String.format("%s (%d)", error.getString(), error.getCode());
}
public int getSerial() {
String name = getName();
if (name == null)
return 0;
int dash = name.lastIndexOf("-");
if (dash < 0 || dash >= name.length())
return 0;
String sn = name.substring(dash + 1, name.length());
try {
return Integer.parseInt(sn);
} catch (NumberFormatException ne) {
return 0;
}
}
public String toString() {
return String.format("%-20.20s %4d %s",
getProductName(), getSerial(), getAddr());
}
public String toShortString() {
return String.format("%s %d %s",
getProductName(), getSerial(), getAddr());
}
public SWIGTYPE_p_altos_file open() {
return libaltos.altos_bt_open(this);
}
/*
private boolean isAltusMetrum() {
if (getName().startsWith(Altos.bt_product_telebt))
return true;
return false;
}
*/
public boolean matchProduct(int want_product) {
// if (!isAltusMetrum())
// return false;
if (want_product == AltosLib.product_any)
return true;
if (want_product == AltosLib.product_basestation)
return matchProduct(AltosLib.product_telebt);
if (want_product == getProduct())
return true;
return false;
}
public int hashCode() {
return getName().hashCode() ^ getAddr().hashCode();
}
public boolean equals(Object o) {
if (o == null)
return false;
if (!(o instanceof AltosBTDevice))
return false;
AltosBTDevice other = (AltosBTDevice) o;
return getName().equals(other.getName()) && getAddr().equals(other.getAddr());
}
public AltosBTDevice(String name, String addr) {
AltosUILib.load_library();
libaltos.altos_bt_fill_in(name, addr,this);
}
public AltosBTDevice() {
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import libaltosJNI.*;
import org.altusmetrum.altoslib_14.*;
public class AltosBTDeviceIterator implements Iterator<AltosBTDevice> {
AltosBTDevice current;
boolean done;
SWIGTYPE_p_altos_bt_list list;
public boolean hasNext() {
if (list == null)
return false;
if (current != null)
return true;
if (done)
return false;
current = new AltosBTDevice();
while (libaltos.altos_bt_list_next(list, current) != 0) {
// if (current.matchProduct(product))
return true;
}
current = null;
done = true;
return false;
}
public AltosBTDevice next() {
if (hasNext()) {
AltosBTDevice next = current;
current = null;
return next;
}
return null;
}
public void remove() {
throw new UnsupportedOperationException();
}
public AltosBTDeviceIterator(int inquiry_time) {
done = false;
current = null;
list = libaltos.altos_bt_list_start(inquiry_time);
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import org.altusmetrum.altoslib_14.*;
public class AltosBTKnown implements Iterable<AltosBTDevice> {
LinkedList<AltosBTDevice> devices = new LinkedList<AltosBTDevice>();
AltosPreferencesBackend bt_pref = AltosUIPreferences.bt_devices();
private String get_address(String name) {
return bt_pref.getString(name, "");
}
private void set_address(String name, String addr) {
bt_pref.putString(name, addr);
}
private void remove(String name) {
bt_pref.remove(name);
}
private void load() {
try {
String[] names = bt_pref.keys();
for (int i = 0; i < names.length; i++) {
String name = names[i];
String addr = get_address(name);
devices.add(new AltosBTDevice(name, addr));
}
} catch (IllegalStateException ie) {
}
}
public Iterator<AltosBTDevice> iterator() {
return devices.iterator();
}
private void flush() {
AltosUIPreferences.flush_preferences();
}
public void set(Iterable<AltosBTDevice> new_devices) {
for (AltosBTDevice old : devices) {
boolean found = false;
for (AltosBTDevice new_device : new_devices) {
if (new_device.equals(old)) {
found = true;
break;
}
}
if (!found)
remove(old.getName());
}
devices = new LinkedList<AltosBTDevice>();
for (AltosBTDevice new_device : new_devices) {
devices.add(new_device);
set_address(new_device.getName(), new_device.getAddr());
}
flush();
}
public List<AltosDevice> list(int product) {
LinkedList<AltosDevice> list = new LinkedList<AltosDevice>();
for (AltosBTDevice device : devices) {
if (device.matchProduct(product))
list.add(device);
}
return list;
}
public AltosBTKnown() {
devices = new LinkedList<AltosBTDevice>();
bt_pref = AltosUIPreferences.bt_devices();
load();
}
static AltosBTKnown known;
static public AltosBTKnown bt_known() {
if (known == null)
known = new AltosBTKnown();
return known;
}
}

View File

@@ -0,0 +1,354 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import java.util.*;
import java.util.concurrent.*;
import org.altusmetrum.altoslib_14.*;
public class AltosBTManage extends AltosUIDialog implements ActionListener, Iterable<AltosBTDevice> {
LinkedBlockingQueue<AltosBTDevice> found_devices;
Frame frame;
LinkedList<ActionListener> listeners;
AltosBTKnown bt_known;
class DeviceList extends JList<AltosBTDevice> implements Iterable<AltosBTDevice> {
LinkedList<AltosBTDevice> devices;
DefaultListModel<AltosBTDevice> list_model;
public void add (AltosBTDevice device) {
if (!devices.contains(device)) {
devices.add(device);
list_model.addElement(device);
}
}
public void remove (AltosBTDevice device) {
if (devices.contains(device)) {
devices.remove(device);
list_model.removeElement(device);
}
}
public boolean contains(AltosBTDevice device) {
return devices.contains(device);
}
//Subclass JList to workaround bug 4832765, which can cause the
//scroll pane to not let the user easily scroll up to the beginning
//of the list. An alternative would be to set the unitIncrement
//of the JScrollBar to a fixed value. You wouldn't get the nice
//aligned scrolling, but it should work.
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
int row;
if (orientation == SwingConstants.VERTICAL &&
direction < 0 && (row = getFirstVisibleIndex()) != -1) {
Rectangle r = getCellBounds(row, row);
if ((r.y == visibleRect.y) && (row != 0)) {
Point loc = r.getLocation();
loc.y--;
int prevIndex = locationToIndex(loc);
Rectangle prevR = getCellBounds(prevIndex, prevIndex);
if (prevR == null || prevR.y >= r.y) {
return 0;
}
return prevR.height;
}
}
return super.getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
public Iterator<AltosBTDevice> iterator() {
return devices.iterator();
}
public java.util.List<AltosBTDevice> selected_list() throws InterruptedException {
return getSelectedValuesList();
}
public DeviceList() {
devices = new LinkedList<AltosBTDevice>();
list_model = new DefaultListModel<AltosBTDevice>();
setModel(list_model);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setLayoutOrientation(JList.HORIZONTAL_WRAP);
setVisibleRowCount(-1);
}
}
DeviceList visible_devices;
DeviceList known_devices;
Thread bt_thread;
public Iterator<AltosBTDevice> iterator() {
return known_devices.iterator();
}
public void commit() {
bt_known.set(this);
}
public void add_known() {
try {
for (AltosBTDevice device : visible_devices.selected_list()) {
known_devices.add(device);
visible_devices.remove(device);
}
} catch (InterruptedException ie) {
}
}
public void remove_known() {
try {
for (AltosBTDevice device : known_devices.selected_list()) {
known_devices.remove(device);
visible_devices.add(device);
}
} catch (InterruptedException ie) {
}
}
public void addActionListener(ActionListener l) {
listeners.add(l);
}
private void forwardAction(ActionEvent e) {
for (ActionListener l : listeners)
l.actionPerformed(e);
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if ("ok".equals(command)) {
bt_thread.interrupt();
commit();
setVisible(false);
forwardAction(e);
} else if ("cancel".equals(command)) {
bt_thread.interrupt();
setVisible(false);
forwardAction(e);
} else if ("select".equals(command)) {
add_known();
} else if ("deselect".equals(command)) {
remove_known();
}
}
public void got_visible_device() {
while (!found_devices.isEmpty()) {
AltosBTDevice device = found_devices.remove();
if (!known_devices.contains(device))
visible_devices.add(device);
}
}
class BTGetVisibleDevices implements Runnable {
public void run () {
for (;;)
for (int time = 1; time <= 8; time <<= 1) {
AltosBTDeviceIterator i = new AltosBTDeviceIterator(time);
AltosBTDevice device;
if (Thread.interrupted())
return;
try {
while ((device = i.next()) != null) {
Runnable r;
if (Thread.interrupted())
return;
found_devices.add(device);
r = new Runnable() {
public void run() {
got_visible_device();
}
};
SwingUtilities.invokeLater(r);
}
} catch (Exception e) {
System.out.printf("uh-oh, exception %s\n", e.toString());
}
}
}
}
public static void show(Component frameComp, AltosBTKnown known) {
Frame frame = JOptionPane.getFrameForComponent(frameComp);
AltosBTManage dialog;
dialog = new AltosBTManage(frame, known);
dialog.setVisible(true);
}
public AltosBTManage(Frame in_frame, AltosBTKnown in_known) {
super(in_frame, "Manage Bluetooth Devices", true);
frame = in_frame;
bt_known = in_known;
BTGetVisibleDevices get_visible_devices = new BTGetVisibleDevices();
bt_thread = new Thread(get_visible_devices);
bt_thread.start();
listeners = new LinkedList<ActionListener>();
found_devices = new LinkedBlockingQueue<AltosBTDevice>();
Container pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(4,4,4,4);
/*
* Known devices label and list
*/
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(new JLabel("Known Devices"), c);
known_devices = new DeviceList();
for (AltosBTDevice device : bt_known)
known_devices.add(device);
JScrollPane known_list_scroller = new JScrollPane(known_devices);
known_list_scroller.setPreferredSize(new Dimension(400, 80));
known_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 1;
c.gridheight = 2;
c.weightx = 1;
c.weighty = 1;
pane.add(known_list_scroller, c);
/*
* Visible devices label and list
*/
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 2;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(new JLabel("Visible Devices"), c);
visible_devices = new DeviceList();
JScrollPane visible_list_scroller = new JScrollPane(visible_devices);
visible_list_scroller.setPreferredSize(new Dimension(400, 80));
visible_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.WEST;
c.gridx = 2;
c.gridy = 1;
c.gridheight = 2;
c.gridwidth = 1;
c.weightx = 1;
c.weighty = 1;
pane.add(visible_list_scroller, c);
/*
* Arrows between the two lists
*/
BasicArrowButton select_arrow = new BasicArrowButton(SwingConstants.WEST);
select_arrow.setActionCommand("select");
select_arrow.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.SOUTH;
c.gridx = 1;
c.gridy = 1;
c.gridheight = 1;
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(select_arrow, c);
BasicArrowButton deselect_arrow = new BasicArrowButton(SwingConstants.EAST);
deselect_arrow.setActionCommand("deselect");
deselect_arrow.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.NORTH;
c.gridx = 1;
c.gridy = 2;
c.gridheight = 1;
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(deselect_arrow, c);
JButton cancel_button = new JButton("Cancel");
cancel_button.setActionCommand("cancel");
cancel_button.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0;
c.gridy = 3;
c.gridheight = 1;
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(cancel_button, c);
JButton ok_button = new JButton("OK");
ok_button.setActionCommand("ok");
ok_button.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 2;
c.gridy = 3;
c.gridheight = 1;
c.gridwidth = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(ok_button, c);
getRootPane().setDefaultButton(ok_button);
pack();
setLocationRelativeTo(frame);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
bt_thread.interrupt();
setVisible(false);
}
});
}
}

106
altosuilib/AltosCSVUI.java Normal file
View File

@@ -0,0 +1,106 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import org.altusmetrum.altoslib_14.*;
public class AltosCSVUI
extends AltosUIDialog
implements ActionListener
{
JFileChooser csv_chooser;
JPanel accessory;
JComboBox<String> combo_box;
AltosFlightSeries series;
AltosCalData cal_data;
AltosWriter writer;
static String[] combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" };
void set_default_file() {
File current = csv_chooser.getSelectedFile();
String current_name = current.getName();
String new_name = null;
String selected = (String) combo_box.getSelectedItem();
if (selected.contains("CSV"))
new_name = AltosLib.replace_extension(current_name, ".csv");
else if (selected.contains("KML"))
new_name = AltosLib.replace_extension(current_name, ".kml");
if (new_name != null)
csv_chooser.setSelectedFile(new File(new_name));
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("comboBoxChanged"))
set_default_file();
}
public AltosCSVUI(JFrame frame, AltosFlightSeries series, File source_file) {
this.series = series;
this.cal_data = series.cal_data();
csv_chooser = new JFileChooser(source_file);
accessory = new JPanel();
accessory.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.weightx = 1;
c.weighty = 0;
c.insets = new Insets (4, 4, 4, 4);
JLabel accessory_label = new JLabel("Export File Type");
c.gridx = 0;
c.gridy = 0;
accessory.add(accessory_label, c);
combo_box = new JComboBox<String>(combo_box_items);
combo_box.addActionListener(this);
c.gridx = 0;
c.gridy = 1;
accessory.add(combo_box, c);
csv_chooser.setAccessory(accessory);
csv_chooser.setSelectedFile(source_file);
set_default_file();
int ret = csv_chooser.showSaveDialog(frame);
if (ret == JFileChooser.APPROVE_OPTION) {
File file = csv_chooser.getSelectedFile();
String type = (String) combo_box.getSelectedItem();
try {
if (type.contains("CSV"))
writer = new AltosCSV(file);
else
writer = new AltosKML(file);
writer.write(series);
writer.close();
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(frame,
ee.getMessage(),
"Cannot open file",
JOptionPane.ERROR_MESSAGE);
}
}
}
}

View File

@@ -0,0 +1,413 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.text.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import org.altusmetrum.altoslib_14.*;
class AltosEditFreqUI extends AltosUIDialog implements ActionListener {
Frame frame;
JTextField frequency;
JTextField description;
JButton ok_button, cancel_button;
boolean got_ok;
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if ("ok".equals(cmd)) {
got_ok = true;
setVisible(false);
}
if ("cancel".equals(cmd)) {
got_ok = false;
setVisible(false);
}
}
public AltosFrequency get() {
if (!got_ok)
return null;
String f_s = frequency.getText();
String d_s = description.getText();
try {
double f_d = AltosParse.parse_double_locale(f_s);
return new AltosFrequency(f_d, d_s);
} catch (ParseException ne) {
}
return null;
}
public AltosEditFreqUI(Frame in_frame, AltosFrequency existing) {
super(in_frame, true);
got_ok = false;
frame = in_frame;
Container pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets (4,4,4,4);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(new JLabel("Frequency"), c);
frequency = new JTextField(12);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 1;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(frequency, c);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(new JLabel("Description"), c);
description = new JTextField(12);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 1;
c.gridy = 1;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(description, c);
ok_button = new JButton("OK");
ok_button.setActionCommand("ok");
ok_button.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 2;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(ok_button, c);
cancel_button = new JButton("Cancel");
cancel_button.setActionCommand("cancel");
cancel_button.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 1;
c.gridy = 2;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(cancel_button, c);
if (existing == null)
setTitle("Add New Frequency");
else {
setTitle("Edit Existing Frequency");
frequency.setText(String.format("%7.3f", existing.frequency));
description.setText(existing.description);
}
getRootPane().setDefaultButton(ok_button);
pack();
setLocationRelativeTo(frame);
}
public AltosEditFreqUI(Frame in_frame) {
this(in_frame, (AltosFrequency) null);
}
}
public class AltosConfigFreqUI extends AltosUIDialog implements ActionListener {
Frame frame;
LinkedList<ActionListener> listeners;
class FrequencyList extends JList<AltosFrequency> {
DefaultListModel<AltosFrequency> list_model;
public void add(AltosFrequency frequency) {
int i;
for (i = 0; i < list_model.size(); i++) {
AltosFrequency f = (AltosFrequency) list_model.get(i);
if (frequency.frequency == f.frequency)
return;
if (frequency.frequency < f.frequency)
break;
}
list_model.insertElementAt(frequency, i);
}
public void remove(AltosFrequency frequency) {
list_model.removeElement(frequency);
}
//Subclass JList to workaround bug 4832765, which can cause the
//scroll pane to not let the user easily scroll up to the beginning
//of the list. An alternative would be to set the unitIncrement
//of the JScrollBar to a fixed value. You wouldn't get the nice
//aligned scrolling, but it should work.
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
int row;
if (orientation == SwingConstants.VERTICAL &&
direction < 0 && (row = getFirstVisibleIndex()) != -1) {
Rectangle r = getCellBounds(row, row);
if ((r.y == visibleRect.y) && (row != 0)) {
Point loc = r.getLocation();
loc.y--;
int prevIndex = locationToIndex(loc);
Rectangle prevR = getCellBounds(prevIndex, prevIndex);
if (prevR == null || prevR.y >= r.y) {
return 0;
}
return prevR.height;
}
}
return super.getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
public AltosFrequency selected() {
AltosFrequency f = (AltosFrequency) getSelectedValue();
return f;
}
public AltosFrequency[] frequencies() {
AltosFrequency[] ret;
ret = new AltosFrequency[list_model.size()];
for (int i = 0; i < list_model.size(); i++)
ret[i] = (AltosFrequency) list_model.get(i);
return ret;
}
public FrequencyList(AltosFrequency[] in_frequencies) {
list_model = new DefaultListModel<AltosFrequency>();
setModel(list_model);
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
setLayoutOrientation(JList.HORIZONTAL_WRAP);
for (int i = 0; i < in_frequencies.length; i++) {
add(in_frequencies[i]);
}
setVisibleRowCount(in_frequencies.length);
}
}
FrequencyList frequencies;
void save_frequencies() {
AltosUIPreferences.set_common_frequencies(frequencies.frequencies());
}
JButton add, edit, remove;
JButton cancel, ok;
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if ("ok".equals(cmd)) {
save_frequencies();
setVisible(false);
} else if ("cancel".equals(cmd)) {
setVisible(false);
} else if ("add".equals(cmd)) {
AltosEditFreqUI ui = new AltosEditFreqUI(frame);
ui.setVisible(true);
AltosFrequency f = ui.get();
if (f != null)
frequencies.add(f);
} else if ("edit".equals(cmd)) {
AltosFrequency old_f = frequencies.selected();
if (old_f == null)
return;
AltosEditFreqUI ui = new AltosEditFreqUI(frame, old_f);
ui.setVisible(true);
AltosFrequency new_f = ui.get();
if (new_f != null) {
if (old_f != null)
frequencies.remove(old_f);
frequencies.add(new_f);
}
} else if ("remove".equals(cmd)) {
AltosFrequency old_f = frequencies.selected();
if (old_f == null)
return;
int ret = JOptionPane.showConfirmDialog(this,
String.format("Remove frequency \"%s\"?",
old_f.toShortString()),
"Remove Frequency",
JOptionPane.YES_NO_OPTION);
if (ret == JOptionPane.YES_OPTION)
frequencies.remove(old_f);
}
}
public AltosFrequency[] frequencies() {
return frequencies.frequencies();
}
public AltosConfigFreqUI(Frame in_frame,
AltosFrequency[] in_frequencies) {
super(in_frame, "Manage Frequencies", true);
frame = in_frame;
listeners = new LinkedList<ActionListener>();
frequencies = new FrequencyList(in_frequencies);
Container pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(4,4,4,4);
/*
* Frequencies label and list
*/
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(new JLabel("Frequencies"), c);
JScrollPane list_scroller = new JScrollPane(frequencies);
list_scroller.setAlignmentX(LEFT_ALIGNMENT);
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.WEST;
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 6;
c.gridheight = 2;
c.weightx = 1;
c.weighty = 1;
pane.add(list_scroller, c);
add = new JButton("Add");
add.setActionCommand("add");
add.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0;
c.gridy = 3;
c.gridwidth = 2;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(add, c);
edit = new JButton("Edit");
edit.setActionCommand("edit");
edit.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 2;
c.gridy = 3;
c.gridwidth = 2;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(edit, c);
remove = new JButton("Remove");
remove.setActionCommand("remove");
remove.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 4;
c.gridy = 3;
c.gridwidth = 2;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(remove, c);
ok = new JButton("OK");
ok.setActionCommand("ok");
ok.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0;
c.gridy = 4;
c.gridwidth = 3;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(ok, c);
cancel = new JButton("Cancel");
cancel.setActionCommand("cancel");
cancel.addActionListener(this);
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 3;
c.gridy = 4;
c.gridwidth = 3;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
pane.add(cancel, c);
pack();
setLocationRelativeTo(frame);
}
public static void show(Component frameComp) {
Frame frame = JOptionPane.getFrameForComponent(frameComp);
AltosConfigFreqUI dialog;
dialog = new AltosConfigFreqUI(frame, AltosUIPreferences.common_frequencies());
dialog.setVisible(true);
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
import org.altusmetrum.altoslib_14.*;
public class AltosDataChooser extends JFileChooser {
JFrame frame;
String filename;
File file;
public String filename() {
return filename;
}
public File file() {
return file;
}
public AltosRecordSet runDialog() {
int ret;
ret = showOpenDialog(frame);
if (ret == APPROVE_OPTION) {
file = getSelectedFile();
if (file == null)
return null;
try {
return AltosLib.record_set(file);
} catch (IOException ie) {
JOptionPane.showMessageDialog(frame,
ie.getMessage(),
"Error reading file",
JOptionPane.ERROR_MESSAGE);
}
}
return null;
}
public AltosDataChooser(JFrame in_frame) {
frame = in_frame;
setDialogTitle("Select Flight Record File");
setFileFilter(new FileNameExtensionFilter("On-board Log file",
"eeprom"));
setFileFilter(new FileNameExtensionFilter("Telemetry file",
"telem"));
setFileFilter(new FileNameExtensionFilter("Flight data file",
"telem", "eeprom"));
setCurrentDirectory(AltosUIPreferences.logdir());
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import libaltosJNI.*;
public interface AltosDevice {
public abstract String toString();
public abstract String toShortString();
public abstract int getSerial();
public abstract String getPath();
public abstract boolean matchProduct(int product);
public abstract String getErrorString();
public SWIGTYPE_p_altos_file open();
public abstract boolean equals(Object obj);
}

View File

@@ -0,0 +1,190 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public abstract class AltosDeviceDialog extends AltosUIDialog implements ActionListener {
private AltosDevice value;
private JList<AltosDevice> list;
private JButton cancel_button;
private JButton select_button;
public Frame frame;
public int product;
public JPanel buttonPane;
private Timer timer;
AltosDevice[] devices;
public AltosDevice getValue() {
return value;
}
public abstract AltosDevice[] devices();
public void update_devices() {
AltosDevice selected = list.getSelectedValue();
devices = devices();
list.setListData(devices);
for (AltosDevice d : devices) {
if (d.equals(selected)) {
list.setSelectedValue(d, true);
break;
}
}
select_button.setEnabled(devices.length > 0);
}
public void add_bluetooth() { }
public AltosDeviceDialog (Frame in_frame, Component location, int in_product) {
super(in_frame, "Device Selection", true);
product = in_product;
frame = in_frame;
value = null;
cancel_button = new JButton("Cancel");
cancel_button.setActionCommand("cancel");
cancel_button.addActionListener(this);
select_button = new JButton("Select");
select_button.setActionCommand("select");
select_button.addActionListener(this);
select_button.setEnabled(false);
getRootPane().setDefaultButton(select_button);
list = new JList<AltosDevice>(devices) {
//Subclass JList to workaround bug 4832765, which can cause the
//scroll pane to not let the user easily scroll up to the beginning
//of the list. An alternative would be to set the unitIncrement
//of the JScrollBar to a fixed value. You wouldn't get the nice
//aligned scrolling, but it should work.
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
int row;
if (orientation == SwingConstants.VERTICAL &&
direction < 0 && (row = getFirstVisibleIndex()) != -1) {
Rectangle r = getCellBounds(row, row);
if ((r.y == visibleRect.y) && (row != 0)) {
Point loc = r.getLocation();
loc.y--;
int prevIndex = locationToIndex(loc);
Rectangle prevR = getCellBounds(prevIndex, prevIndex);
if (prevR == null || prevR.y >= r.y) {
return 0;
}
return prevR.height;
}
}
return super.getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
};
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
list.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
select_button.doClick(); //emulate button click
}
}
});
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(400, 80));
listScroller.setAlignmentX(LEFT_ALIGNMENT);
//Create a container so that we can add a title around
//the scroll pane. Can't add a title directly to the
//scroll pane because its background would be white.
//Lay out the label and scroll pane from top to bottom.
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
JLabel label = new JLabel("Select Device");
label.setLabelFor(list);
listPane.add(label);
listPane.add(Box.createRigidArea(new Dimension(0,5)));
listPane.add(listScroller);
listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//Lay out the buttons from left to right.
buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(cancel_button);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
if (AltosUILib.has_bluetooth)
add_bluetooth();
buttonPane.add(select_button);
//Put everything together, using the content pane's BorderLayout.
Container contentPane = getContentPane();
contentPane.add(listPane, BorderLayout.CENTER);
contentPane.add(buttonPane, BorderLayout.PAGE_END);
//Initialize values.
update_devices();
if (devices != null && devices.length != 0)
list.setSelectedValue(devices[0], true);
pack();
setLocationRelativeTo(location);
timer = new Timer(1000, new ActionListener () {
public void actionPerformed(ActionEvent evt) {
update_devices();
}
});
addComponentListener(new ComponentListener() {
public void componentShown(ComponentEvent e) {
timer.start();
}
public void componentMoved(ComponentEvent e) {
}
public void componentResized(ComponentEvent e) {
}
public void componentHidden(ComponentEvent e) {
timer.stop();
}
});
}
//Handle clicks on the Set and Cancel buttons.
public void actionPerformed(ActionEvent e) {
if ("select".equals(e.getActionCommand())) {
value = (AltosDevice)(list.getSelectedValue());
setVisible(false);
}
if ("cancel".equals(e.getActionCommand()))
setVisible(false);
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class AltosDeviceUIDialog extends AltosDeviceDialog {
boolean include_bluetooth;
public AltosDevice[] devices() {
java.util.List<AltosDevice> usb_devices = AltosUSBDevice.list(product);
int num_devices = usb_devices.size();
java.util.List<AltosDevice> bt_devices = null;
if (include_bluetooth) {
bt_devices = AltosBTKnown.bt_known().list(product);
num_devices += bt_devices.size();
}
AltosDevice[] devices = new AltosDevice[num_devices];
for (int i = 0; i < usb_devices.size(); i++)
devices[i] = usb_devices.get(i);
if (include_bluetooth) {
int off = usb_devices.size();
for (int j = 0; j < bt_devices.size(); j++)
devices[off + j] = bt_devices.get(j);
}
return devices;
}
public void add_bluetooth() {
if (include_bluetooth) {
JButton manage_bluetooth_button = new JButton("Manage Bluetooth");
manage_bluetooth_button.setActionCommand("manage");
manage_bluetooth_button.addActionListener(this);
buttonPane.add(manage_bluetooth_button);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
}
}
public void actionPerformed(ActionEvent e) {
super.actionPerformed(e);
if ("manage".equals(e.getActionCommand())) {
AltosBTManage.show(frame, AltosBTKnown.bt_known());
update_devices();
}
}
public AltosDeviceUIDialog (Frame in_frame, Component location, int in_product, boolean include_bluetooth) {
super(in_frame, location, in_product);
this.include_bluetooth = include_bluetooth;
}
public AltosDeviceUIDialog (Frame in_frame, Component location, int in_product) {
this(in_frame, location, in_product, true);
}
public static AltosDevice show (Component frameComp, int product, boolean include_bluetooth) {
Frame frame = JOptionPane.getFrameForComponent(frameComp);
AltosDeviceUIDialog dialog;
dialog = new AltosDeviceUIDialog(frame, frameComp, product, include_bluetooth);
dialog.setVisible(true);
return dialog.getValue();
}
public static AltosDevice show (Component frameComp, int product) {
return show(frameComp, product, true);
}
public static AltosUSBDevice show_usb (Component frameComp, int product) {
return (AltosUSBDevice) show(frameComp, product, false);
}
}

View File

@@ -0,0 +1,261 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.text.*;
import org.altusmetrum.altoslib_14.*;
public class AltosDisplayThread extends Thread {
Frame parent;
IdleThread idle_thread;
AltosVoice voice;
AltosFlightReader reader;
AltosState state;
int old_state = AltosLib.ao_flight_invalid;
boolean old_gps_ready = false;
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, distance %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.distance));
} else if (state.state() > AltosLib.ao_flight_pad && state.height() != AltosLib.MISSING) {
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_stateless &&
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, distance %s.",
(int) (state.from_pad.bearing + 0.5),
AltosConvert.distance.say_units(state.from_pad.distance));
++reported_landing;
}
}
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) {
}
}
public synchronized void notice(boolean spoken) {
if (old_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 != state.state()) {
if (state.state() != AltosLib.ao_flight_stateless)
voice.speak(state.state_name());
if ((old_state == AltosLib.ao_flight_invalid || old_state <= AltosLib.ao_flight_boost) &&
state.state() > AltosLib.ao_flight_boost) {
if (state.max_speed() != AltosLib.MISSING)
voice.speak("max speed: %s.",
AltosConvert.speed.say_units(state.max_speed() + 0.5));
ret = true;
} else if ((old_state == AltosLib.ao_flight_invalid || old_state < AltosLib.ao_flight_drogue) &&
state.state() >= AltosLib.ao_flight_drogue) {
if (state.max_height() != AltosLib.MISSING)
voice.speak("max height: %s.",
AltosConvert.height.say_units(state.max_height() + 0.5));
ret = true;
}
}
if (old_gps_ready != state.gps_ready) {
if (state.gps_ready) {
voice.speak("GPS ready");
ret = true;
}
else if (old_gps_ready) {
voice.speak("GPS lost");
ret = true;
}
}
old_state = state.state();
old_gps_ready = state.gps_ready;
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) {
listener_state.running = false;
break;
}
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();
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
import org.altusmetrum.altoslib_14.*;
public class AltosEepromDelete implements Runnable {
AltosEepromList flights;
Thread eeprom_thread;
AltosSerial serial_line;
boolean remote;
JFrame frame;
ActionListener listener;
boolean success;
private void DeleteLog (AltosEepromLog log)
throws IOException, InterruptedException, TimeoutException {
if (flights.config_data.flight_log_max != 0 || flights.config_data.log_format != 0) {
/* Devices with newer firmware can erase the
* flash blocks containing each flight
*/
serial_line.flush_input();
serial_line.printf("d %d\n", log.flight);
for (;;) {
/* It can take a while to erase the flash... */
String line = serial_line.get_reply(200000);
if (line == null)
throw new TimeoutException();
if (line.equals("Erased"))
break;
if (line.equals("Failed to erase"))
throw new IOException(line);
if (line.startsWith("No such"))
throw new IOException(line);
}
}
}
private void show_error_internal(String message, String title) {
JOptionPane.showMessageDialog(frame,
message,
title,
JOptionPane.ERROR_MESSAGE);
}
private void show_error(String in_message, String in_title) {
final String message = in_message;
final String title = in_title;
Runnable r = new Runnable() {
public void run() {
try {
show_error_internal(message, title);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
public void run () {
success = false;
try {
if (remote)
serial_line.start_remote();
for (AltosEepromLog log : flights) {
if (log.delete_selected) {
DeleteLog(log);
}
}
success = true;
} catch (IOException ee) {
show_error (ee.getLocalizedMessage(),
serial_line.device.toShortString());
} catch (InterruptedException ie) {
} catch (TimeoutException te) {
show_error (String.format("Connection to \"%s\" failed",
serial_line.device.toShortString()),
"Connection Failed");
} finally {
try {
if (remote)
serial_line.stop_remote();
} catch (InterruptedException ie) {
} finally {
serial_line.flush_output();
serial_line.close();
}
}
if (listener != null) {
Runnable r = new Runnable() {
public void run() {
try {
listener.actionPerformed(new ActionEvent(this,
success ? 1 : 0,
"delete"));
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
}
public void start() {
eeprom_thread = new Thread(this);
eeprom_thread.start();
}
public void addActionListener(ActionListener l) {
listener = l;
}
public AltosEepromDelete(JFrame given_frame,
AltosSerial given_serial_line,
boolean given_remote,
AltosEepromList given_flights) {
frame = given_frame;
serial_line = given_serial_line;
remote = given_remote;
flights = given_flights;
success = false;
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright © 2017 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, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package org.altusmetrum.altosuilib_14;
import org.altusmetrum.altoslib_14.*;
public interface AltosEepromGrapher {
public void graph_flights(AltosEepromList list);
}

View File

@@ -0,0 +1,258 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.concurrent.*;
import org.altusmetrum.altoslib_14.*;
public class AltosEepromManage implements ActionListener {
JFrame frame;
boolean remote;
AltosDevice device;
AltosSerial serial_line;
AltosEepromList flights;
AltosEepromDownload download;
AltosEepromDelete delete;
AltosEepromGrapher grapher;
public void finish() {
if (serial_line != null) {
try {
serial_line.flush_input();
} catch (InterruptedException ie) {
}
serial_line.close();
serial_line = null;
}
}
private int countDeletedFlights() {
int count = 0;
for (AltosEepromLog flight : flights) {
if (flight.delete_selected)
count++;
}
return count;
}
private String showDeletedFlights() {
String result = "";
for (AltosEepromLog flight : flights) {
if (flight.delete_selected) {
if (result.equals(""))
result = String.format("%d", flight.flight);
else
result = String.format("%s, %d", result, flight.flight);
}
}
return result;
}
public boolean delete_start() {
boolean any_selected = false;
for (AltosEepromLog flight : flights)
any_selected = any_selected || flight.delete_selected;
if (any_selected) {
delete = new AltosEepromDelete(frame,
serial_line,
remote,
flights);
delete.addActionListener(this);
/*
* Start flight log delete
*/
delete.start();
return true;
}
return false;
}
public void graph_start() {
boolean any_selected = false;
for (AltosEepromLog flight : flights) {
if (!flight.download_selected)
flight.graph_selected = false;
any_selected = any_selected || flight.graph_selected;
}
if (any_selected && grapher != null)
grapher.graph_flights(flights);
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
boolean success = e.getID() != 0;
boolean running = false;
if (cmd.equals("download")) {
if (success) {
running = delete_start();
if (!running)
graph_start();
}
} else if (cmd.equals("delete")) {
if (success) {
JOptionPane.showMessageDialog(frame,
String.format("%d flights erased: %s",
countDeletedFlights(),
showDeletedFlights()),
serial_line.device.toShortString(),
JOptionPane.INFORMATION_MESSAGE);
graph_start();
}
}
if (!running)
finish();
}
public void got_flights(AltosEepromList in_flights) {
boolean running = false;;
flights = in_flights;
try {
if (flights.size() == 0) {
JOptionPane.showMessageDialog(frame,
String.format("No flights available on %d",
device.getSerial()),
serial_line.device.toShortString(),
JOptionPane.INFORMATION_MESSAGE);
} else {
AltosEepromSelect select = new AltosEepromSelect(frame, flights, grapher != null);
if (select.run()) {
boolean any_selected = false;
for (AltosEepromLog flight : flights)
any_selected = any_selected || flight.download_selected;
if (any_selected) {
AltosEepromMonitorUI monitor = new AltosEepromMonitorUI(frame);
monitor.addActionListener(this);
serial_line.set_frame(frame);
download = new AltosEepromDownload(monitor,
serial_line,
remote,
flights);
/*
* Start flight log download
*/
download.start();
running = true;
} else {
running = delete_start();
if (!running)
graph_start();
}
}
}
if (!running)
finish();
} catch (Exception e) {
got_exception(e);
}
}
public void got_exception(Exception e) {
if (e instanceof IOException) {
IOException ee = (IOException) e;
JOptionPane.showMessageDialog(frame,
device.toShortString(),
ee.getLocalizedMessage(),
JOptionPane.ERROR_MESSAGE);
} else if (e instanceof TimeoutException) {
//TimeoutException te = (TimeoutException) e;
JOptionPane.showMessageDialog(frame,
String.format("Communications failed with \"%s\"",
device.toShortString()),
"Cannot open target device",
JOptionPane.ERROR_MESSAGE);
}
finish();
}
class EepromGetList implements Runnable {
AltosEepromManage manage;
public void run () {
Runnable r;
try {
flights = new AltosEepromList(serial_line, remote);
r = new Runnable() {
public void run() {
got_flights(flights);
}
};
} catch (Exception e) {
final Exception f_e = e;
r = new Runnable() {
public void run() {
got_exception(f_e);
}
};
}
SwingUtilities.invokeLater(r);
}
public EepromGetList(AltosEepromManage in_manage) {
manage = in_manage;
}
}
public AltosEepromManage(JFrame given_frame, AltosEepromGrapher grapher, int product) {
//boolean running = false;
frame = given_frame;
this.grapher = grapher;
device = AltosDeviceUIDialog.show(frame, product);
remote = false;
if (device != null) {
try {
serial_line = new AltosSerial(device);
if (device.matchProduct(AltosLib.product_basestation))
remote = true;
serial_line.set_frame(frame);
EepromGetList get_list = new EepromGetList(this);
Thread t = new Thread(get_list);
t.start();
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(frame,
ee.getMessage(),
"Cannot open target device",
JOptionPane.ERROR_MESSAGE);
} catch (AltosSerialInUseException si) {
JOptionPane.showMessageDialog(frame,
String.format("Device \"%s\" already in use",
device.toShortString()),
"Device in use",
JOptionPane.ERROR_MESSAGE);
}
}
}
}

View File

@@ -0,0 +1,305 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
class result_holder {
static int result;
}
public class AltosEepromMonitorUI extends AltosUIDialog implements AltosEepromMonitor {
JFrame owner;
Container pane;
Box box;
JLabel serial_label;
JLabel flight_label;
JLabel file_label;
JLabel serial_value;
JLabel flight_value;
JButton cancel;
JProgressBar pbar;
ActionListener listener;
static final int progress_max = 10000;
public AltosEepromMonitorUI(JFrame owner) {
super (owner, "Download Flight Data", false);
setMinimumSize(new Dimension(600, 100));
this.owner = owner;
GridBagConstraints c;
Insets il = new Insets(4,4,4,4);
Insets ir = new Insets(4,4,4,4);
pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
serial_label = new JLabel("Serial:");
pane.add(serial_label, c);
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
serial_value = new JLabel("");
pane.add(serial_value, c);
c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.gridx = 0; c.gridy = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
flight_label = new JLabel("Flight:");
pane.add(flight_label, c);
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 1; c.gridy = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
flight_value = new JLabel("");
pane.add(flight_value, c);
pbar = new JProgressBar();
pbar.setMinimum(0);
pbar.setMaximum(progress_max);
pbar.setStringPainted(true);
set_block_internal(0);
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0; c.gridy = 3;
c.gridwidth = GridBagConstraints.REMAINDER;
Insets ib = new Insets(4,4,4,4);
c.insets = ib;
pane.add(pbar, c);
cancel = new JButton("Cancel");
c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0; c.gridy = 4;
c.gridwidth = GridBagConstraints.REMAINDER;
Insets ic = new Insets(4,4,4,4);
c.insets = ic;
pane.add(cancel, c);
pack();
setLocationRelativeTo(owner);
}
public void addActionListener(ActionListener l) {
listener = l;
}
public void set_thread(Thread in_eeprom_thread) {
final Thread eeprom_thread = in_eeprom_thread;
cancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (eeprom_thread != null) {
eeprom_thread.interrupt();
}
}
});
}
public void start() {
setVisible(true);
}
int max_block = 1;
public void set_block_internal(int block) {
double pos;
String s;
pos = (double) block / (double) max_block;
s = String.format("block %d of %d", block, max_block);
pbar.setString(s);
pbar.setStringPainted(true);
pbar.setValue((int) (pos * progress_max));
}
public void set_max(int max_block) {
this.max_block = max_block;
}
public void set_block(int in_block) {
final int block = in_block;
Runnable r = new Runnable() {
public void run() {
try {
set_block_internal(block);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
private void set_serial_internal(int serial) {
serial_value.setText(String.format("%d", serial));
}
public void set_serial(int in_serial) {
final int serial = in_serial;
Runnable r = new Runnable() {
public void run() {
try {
set_serial_internal(serial);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
private void set_flight_internal(int flight) {
flight_value.setText(String.format("%d", flight));
}
public void set_flight(int in_flight) {
final int flight = in_flight;
Runnable r = new Runnable() {
public void run() {
try {
set_flight_internal(flight);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
private void done_internal(boolean success) {
listener.actionPerformed(new ActionEvent(this,
success ? 1 : 0,
"download"));
setVisible(false);
dispose();
}
public void done(boolean in_success) {
final boolean success = in_success;
Runnable r = new Runnable() {
public void run() {
try {
done_internal(success);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
private void reset_internal() {
set_max(1);
set_block_internal(0);
set_flight_internal(0);
}
public void reset() {
Runnable r = new Runnable() {
public void run() {
try {
reset_internal();
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
private void show_message_internal(String message, String title, int message_type) {
int joption_message_type = JOptionPane.ERROR_MESSAGE;
switch (message_type) {
case INFO_MESSAGE:
joption_message_type = JOptionPane.INFORMATION_MESSAGE;
break;
case WARNING_MESSAGE:
joption_message_type = JOptionPane.WARNING_MESSAGE;
break;
case ERROR_MESSAGE:
joption_message_type = JOptionPane.ERROR_MESSAGE;
break;
}
JOptionPane.showMessageDialog(owner,
message,
title,
joption_message_type);
}
public Boolean check_overwrite(File in_file) {
final Semaphore check_overwrite_done = new Semaphore(0);
final File file = in_file;
final result_holder result = new result_holder();
Runnable r = new Runnable() {
public void run() {
result_holder.result = JOptionPane.showConfirmDialog(owner,
String.format("\"%s\" already exists, overwrite?",
file.toString()),
"Overwrite Existing File?",
JOptionPane.YES_NO_OPTION);
check_overwrite_done.release();
}
};
SwingUtilities.invokeLater(r);
try {
check_overwrite_done.acquire();
} catch (Exception e) {}
return result_holder.result == JOptionPane.YES_OPTION;
}
public void show_message(String in_message, String in_title, int in_message_type) {
final String message = in_message;
final String title = in_title;
final int message_type = in_message_type;
Runnable r = new Runnable() {
public void run() {
try {
show_message_internal(message, title, message_type);
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
}

View File

@@ -0,0 +1,234 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import org.altusmetrum.altoslib_14.*;
class AltosEepromItem implements ActionListener {
AltosEepromLog log;
JLabel label;
JCheckBox download;
JCheckBox delete;
JCheckBox graph;
public void actionPerformed(ActionEvent e) {
log.download_selected = download.isSelected();
log.delete_selected = delete.isSelected();
log.graph_selected = graph.isSelected();
}
public AltosEepromItem(AltosEepromLog in_log) {
log = in_log;
String text;
if (log.flight >= 0)
text = String.format("Flight #%02d", log.flight);
else
text = String.format("Corrupt #%02d", -log.flight);
label = new JLabel(text);
download = new JCheckBox("", log.download_selected);
download.addActionListener(this);
delete = new JCheckBox("", log.delete_selected);
delete.addActionListener(this);
graph = new JCheckBox("", log.graph_selected);
graph.addActionListener(this);
}
}
public class AltosEepromSelect extends AltosUIDialog implements ActionListener {
//private JList list;
private JFrame frame;
JButton ok;
JButton cancel;
boolean success;
/* Listen for events from our buttons */
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("ok"))
success = true;
setVisible(false);
}
public boolean run() {
success = false;
setLocationRelativeTo(frame);
setVisible(true);
return success;
}
public AltosEepromSelect (JFrame in_frame,
AltosEepromList flights,
boolean has_graph) {
super(in_frame, String.format("Flight list for serial %d", flights.config_data.serial), true);
frame = in_frame;
/* Create the container for the dialog */
Container contentPane = getContentPane();
/* First, we create a pane containing the dialog's header/title */
JLabel selectLabel = new JLabel(String.format ("Select flights"), SwingConstants.CENTER);
JPanel labelPane = new JPanel();
labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
labelPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
labelPane.add(Box.createHorizontalGlue());
labelPane.add(selectLabel);
labelPane.add(Box.createHorizontalGlue());
/* Add the header to the container. */
contentPane.add(labelPane, BorderLayout.PAGE_START);
/* Now we create the evilness that is a GridBag for the flight details */
GridBagConstraints c;
Insets i = new Insets(4,4,4,4);
JPanel flightPane = new JPanel();
flightPane.setLayout(new GridBagLayout());
flightPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
/* Flight Header */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
JLabel flightHeaderLabel = new JLabel("Flight");
flightPane.add(flightHeaderLabel, c);
/* Download Header */
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
JLabel downloadHeaderLabel = new JLabel("Download");
flightPane.add(downloadHeaderLabel, c);
/* Delete Header */
c = new GridBagConstraints();
c.gridx = 2; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
JLabel deleteHeaderLabel = new JLabel("Delete");
flightPane.add(deleteHeaderLabel, c);
if (has_graph) {
/* Graph Header */
c = new GridBagConstraints();
c.gridx = 3; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
JLabel graphHeaderLabel = new JLabel("Graph");
flightPane.add(graphHeaderLabel, c);
}
/* Add the flights to the GridBag */
AltosEepromItem item;
int itemNumber = 1;
for (AltosEepromLog flight : flights) {
/* Create a flight object with handlers and
* appropriate UI items
*/
item = new AltosEepromItem(flight);
/* Add a decriptive label for the flight */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = itemNumber;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
flightPane.add(item.label, c);
/* Add download checkbox for the flight */
c = new GridBagConstraints();
c.gridx = 1; c.gridy = itemNumber;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
flightPane.add(item.download, c);
/* Add delete checkbox for the flight */
c = new GridBagConstraints();
c.gridx = 2; c.gridy = itemNumber;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
flightPane.add(item.delete, c);
if (has_graph) {
/* Add graph checkbox for the flight */
c = new GridBagConstraints();
c.gridx = 3; c.gridy = itemNumber;
c.fill = GridBagConstraints.NONE;
c.weightx = 0.5;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
flightPane.add(item.graph, c);
}
itemNumber++;
}
/* Add the GridBag to the container */
contentPane.add(flightPane, BorderLayout.CENTER);
/* Create the dialog buttons */
ok = new JButton("OK");
ok.addActionListener(this);
ok.setActionCommand("ok");
cancel = new JButton("Cancel");
cancel.addActionListener(this);
cancel.setActionCommand("cancel");
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(cancel);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(ok);
/* Add the buttons to the container */
contentPane.add(buttonPane, BorderLayout.PAGE_END);
/* Pack the window! */
pack();
}
}

View File

@@ -0,0 +1,759 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
import java.util.concurrent.*;
import org.altusmetrum.altoslib_14.*;
public class AltosFlashUI
extends AltosUIDialog
implements ActionListener
{
Container pane;
Box box;
JLabel serial_label;
JLabel serial_value;
JLabel file_label;
JLabel file_value;
JProgressBar pbar;
JButton cancel;
AltosUIFrame frame;
// Hex file with rom image
File file;
// Debug connection
AltosUSBDevice device;
AltosLink link;
// Desired Rom configuration
AltosRomconfig rom_config;
// Flash controller
AltosProgrammer programmer;
private static final String[] pair_programmed_files = {
"teleballoon",
"telebt-v1",
"teledongle-v0",
"telefire-v0",
"telemetrum-v0",
"telemetrum-v1",
"telemini-v1",
"telenano",
"teleshield"
};
private static final String[] pair_programmed_devices = {
"TeleBalloon",
"TeleBT-v1",
"TeleDongle-v0",
"TeleFire-v0",
"TeleFire",
"TeleMetrum-v0",
"TeleMetrum-v1",
"TeleMini-v1",
"TeleNano",
"TeleShield"
};
private static final String[] log_erased_devices = {
"TeleGPS"
};
private boolean is_pair_programmed() {
if (file != null) {
String name = file.getName();
for (int i = 0; i < pair_programmed_files.length; i++) {
if (name.startsWith(pair_programmed_files[i]))
return true;
}
}
if (device != null) {
String name = device.toString();
for (int i = 0; i < pair_programmed_devices.length; i++) {
if (name.startsWith(pair_programmed_devices[i]))
return true;
}
}
return false;
}
private boolean is_log_erased() {
if (device != null) {
String name = device.toString();
for (int i = 0; i < log_erased_devices.length; i++) {
if (name.startsWith(log_erased_devices[i]))
return true;
}
}
return false;
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == cancel) {
if (programmer != null)
programmer.abort();
setVisible(false);
dispose();
} else {
String cmd = e.getActionCommand();
if (e.getID() == -1) {
JOptionPane.showMessageDialog(frame,
e.getActionCommand(),
file.toString(),
JOptionPane.ERROR_MESSAGE);
setVisible(false);
dispose();
} else if (cmd.equals(AltosFlashListener.flash_done)) {
setVisible(false);
dispose();
} else if (cmd.equals(AltosFlashListener.flash_start)) {
setVisible(true);
} else {
pbar.setValue(e.getID());
pbar.setString(cmd);
}
}
}
public void build_dialog() {
GridBagConstraints c;
Insets il = new Insets(4,4,4,4);
Insets ir = new Insets(4,4,4,4);
pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
serial_label = new JLabel("Serial:");
pane.add(serial_label, c);
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
serial_value = new JLabel("");
pane.add(serial_value, c);
c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.gridx = 0; c.gridy = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
file_label = new JLabel("File:");
pane.add(file_label, c);
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 1; c.gridy = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
file_value = new JLabel(file.toString());
pane.add(file_value, c);
pbar = new JProgressBar();
pbar.setMinimum(0);
pbar.setMaximum(100);
pbar.setValue(0);
pbar.setString("");
pbar.setStringPainted(true);
pbar.setPreferredSize(new Dimension(600, 20));
c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0; c.gridy = 2;
c.gridwidth = GridBagConstraints.REMAINDER;
Insets ib = new Insets(4,4,4,4);
c.insets = ib;
pane.add(pbar, c);
cancel = new JButton("Cancel");
c = new GridBagConstraints();
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridx = 0; c.gridy = 3;
c.gridwidth = GridBagConstraints.REMAINDER;
Insets ic = new Insets(4,4,4,4);
c.insets = ic;
pane.add(cancel, c);
cancel.addActionListener(this);
pack();
setLocationRelativeTo(frame);
}
void set_serial(int serial_number) {
serial_value.setText(String.format("%d", serial_number));
}
static class AltosHexfileFilter extends javax.swing.filechooser.FileFilter {
String head;
String description;
public AltosHexfileFilter(String usb_product) {
int l;
int dash;
/* Trim off any trailing variants (1.0a vs 1.0) */
for (dash = usb_product.length(); dash > 0; dash--) {
char c = usb_product.charAt(dash-1);
if (c == '-')
break;
}
if (dash == 0)
dash = usb_product.length();
for (l = usb_product.length(); l > dash; l--) {
char c = usb_product.charAt(l-1);
if (c < 'a' || 'z' < c)
break;
}
head = usb_product.substring(0, l).toLowerCase();
description = String.format("%s Image File", usb_product);
}
public boolean accept(File file) {
return !file.isFile() || (file.getName().startsWith(head) && file.getName().endsWith(".ihx"));
}
public String getDescription() {
return description;
}
}
boolean select_source_file() {
JFileChooser hexfile_chooser = new JFileChooser();
File firmwaredir = AltosUIPreferences.firmwaredir();
if (firmwaredir != null)
hexfile_chooser.setCurrentDirectory(firmwaredir);
hexfile_chooser.setDialogTitle("Select Flash Image");
javax.swing.filechooser.FileFilter ihx_filter = new FileNameExtensionFilter("Flash Image", "ihx");
hexfile_chooser.addChoosableFileFilter(ihx_filter);
hexfile_chooser.setFileFilter(ihx_filter);
if (!is_pair_programmed() && !device.matchProduct(AltosLib.product_altusmetrum)) {
AltosHexfileFilter filter = new AltosHexfileFilter(device.usb_product());
hexfile_chooser.addChoosableFileFilter(filter);
hexfile_chooser.setFileFilter(filter);
}
int returnVal = hexfile_chooser.showOpenDialog(frame);
if (returnVal != JFileChooser.APPROVE_OPTION)
return false;
file = hexfile_chooser.getSelectedFile();
if (file == null)
return false;
AltosUIPreferences.set_firmwaredir(file.getParentFile());
return true;
}
boolean select_device() {
int product = AltosLib.product_any;
device = AltosDeviceUIDialog.show_usb(frame, AltosLib.product_any);
if (device == null)
return false;
return true;
}
boolean rom_config_matches (AltosRomconfig a, AltosRomconfig b) {
if (a == null || b == null)
return (a == null && b == null);
if (!a.valid || !b.valid)
return false;
if (a.usb_id != null && b.usb_id != null &&
(a.usb_id.vid != b.usb_id.vid ||
a.usb_id.pid != b.usb_id.pid))
return false;
if (a.usb_product != null && b.usb_product != null &&
!a.usb_product.equals(b.usb_product))
return false;
return true;
}
boolean update_rom_config_info(AltosRomconfig existing_config, AltosRomconfig image_config) {
AltosRomconfig new_config;
if (!rom_config_matches(existing_config, image_config)) {
int ret;
if (existing_config == null || !existing_config.valid) {
ret = JOptionPane.showConfirmDialog(this,
String.format("Cannot determine target device type\nImage is %04x:%04x %s\nFlash anyways?",
image_config.usb_id.vid,
image_config.usb_id.pid,
image_config.usb_product),
"Unknown Target Device",
JOptionPane.YES_NO_OPTION);
} else {
ret = JOptionPane.showConfirmDialog(this,
String.format("Device is %04x:%04x %s\nImage is %04x:%04x %s\nFlash anyways?",
existing_config.usb_id.vid,
existing_config.usb_id.pid,
existing_config.usb_product,
image_config.usb_id.vid,
image_config.usb_id.pid,
image_config.usb_product),
"Image doesn't match Device",
JOptionPane.YES_NO_OPTION);
}
if (ret != JOptionPane.YES_OPTION)
return false;
}
if (existing_config != null && existing_config.radio_calibration_broken) {
int ret = JOptionPane.showConfirmDialog(this,
String.format("Radio calibration value %d may be incorrect\nFlash anyways?",
existing_config.radio_calibration),
"Radio Calibration Invalid",
JOptionPane.YES_NO_OPTION);
if (ret != JOptionPane.YES_OPTION)
return false;
}
new_config = AltosRomconfigUI.show(frame, existing_config);
if (new_config == null)
return false;
rom_config = new_config;
set_serial(rom_config.serial_number);
setVisible(true);
return true;
}
void exception (Exception e) {
if (e instanceof FileNotFoundException) {
JOptionPane.showMessageDialog(frame,
((FileNotFoundException) e).getMessage(),
"Cannot open file",
JOptionPane.ERROR_MESSAGE);
} else if (e instanceof AltosSerialInUseException) {
JOptionPane.showMessageDialog(frame,
String.format("Device \"%s\" already in use",
device.toShortString()),
"Device in use",
JOptionPane.ERROR_MESSAGE);
} else {
JOptionPane.showMessageDialog(frame,
e.getMessage(),
file.toString(),
JOptionPane.ERROR_MESSAGE);
}
}
class flash_task implements Runnable, AltosFlashListener {
AltosFlashUI ui;
Thread t;
AltosProgrammer programmer;
public void position(String in_s, int in_percent) {
final String s = in_s;
final int percent = in_percent;
Runnable r = new Runnable() {
public void run() {
try {
ui.actionPerformed(new ActionEvent(this,
percent,
s));
} catch (Exception ex) {
}
}
};
SwingUtilities.invokeLater(r);
}
public void run () {
try {
if (ui.is_pair_programmed())
programmer = new AltosFlash(ui.file, link, this);
else
programmer = new AltosSelfFlash(ui.file, link, this);
final AltosRomconfig current_config = programmer.target_romconfig(device.usb_id(), device.usb_product());
final AltosRomconfig image_config = programmer.image_romconfig();
System.out.printf("product %s current %s image %s\n", device.usb_product(), current_config, image_config);
final Semaphore await_rom_config = new Semaphore(0);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ui.programmer = programmer;
ui.update_rom_config_info(current_config, image_config);
await_rom_config.release();
}
});
await_rom_config.acquire();
if (ui.rom_config != null) {
programmer.set_romconfig(ui.rom_config);
programmer.flash();
}
} catch (InterruptedException ee) {
final Exception e = ee;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ui.exception(e);
}
});
} catch (IOException ee) {
final Exception e = ee;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ui.exception(e);
}
});
} finally {
if (programmer != null)
programmer.close();
}
}
public flash_task(AltosFlashUI in_ui) {
ui = in_ui;
t = new Thread(this);
t.start();
}
}
flash_task flasher;
boolean erase_answer;
class open_task implements Runnable {
AltosDevice device;
Thread t;
open_dialog dialog;
AltosLink link;
public void do_exception(final Exception e) {
if (link != null) {
try {
link.close();
} catch (Exception ex) {}
}
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
try { dialog.open_exception(e); } catch (Exception ex) { }
}
});
}
public void do_success(final AltosLink link) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
try { dialog.open_success(link); } catch (Exception ex) { }
}
});
}
public boolean do_notify_erase(final AltosConfigData config_data) {
erase_answer = false;
final Semaphore erase_answer_done = new Semaphore(0);
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
int ret = JOptionPane.showConfirmDialog(dialog.owner,
String.format("Updating %s from firmware %s will erase stored data, continue?",
config_data.product,
config_data.version),
"Erase Stored Data?",
JOptionPane.YES_NO_OPTION);
erase_answer = ret == JOptionPane.YES_OPTION;
erase_answer_done.release();
}
});
try {
erase_answer_done.acquire();
} catch (Exception ex) {
return false;
}
return erase_answer;
}
public void run () {
link = null;
try {
boolean new_device = false;
for (;;) {
System.out.printf("Attempting to open %s\n", device.toShortString());
for (int i = 0; i < 20; i++) {
link = new AltosSerial(device);
if (link != null)
break;
if (!new_device)
break;
System.out.printf("Waiting for device to become ready\n");
Thread.sleep(1000);
}
if (link == null)
throw new IOException(String.format("%s: open failed",
device.toShortString()));
System.out.printf("Checking device ready\n");
/* See if the link is usable already */
if (is_pair_programmed() || link.is_loader()) {
System.out.printf("Device ready for use\n");
do_success(link);
return;
}
System.out.printf("Checking log erased\n");
if (is_log_erased()) {
System.out.printf("Fetching config data\n");
AltosConfigData config_data = link.config_data();
System.out.printf("version %s\n", config_data.version);
/* Completely erase TeleGPS flash when firmware is old */
if (config_data.compare_version("1.9.7") < 0)
{
if (!do_notify_erase(config_data))
throw new IOException(String.format("%s: not erasing log",
device.toShortString()));
System.out.printf("Erasing log\n");
link.printf("Z DoIt\n");
link.synchronize(120 * 1000);
}
}
java.util.List<AltosDevice> prev_devices =
AltosUSBDevice.list(AltosLib.product_altusmetrum);
/* Nope, switch to loader and
* wait for it to re-appear
*/
System.out.printf("Switch to loader\n");
link.to_loader();
/* This is a bit fragile, but
* I'm not sure what else to
* do other than ask the user.
*
* Search for a device which
* wasn't there before we
* asked the target to switch
* to loader mode
*/
device = null;
for (;;) {
Thread.sleep(100);
java.util.List<AltosDevice> devices =
AltosUSBDevice.list(AltosLib.product_altusmetrum);
for (AltosDevice d : devices) {
boolean matched = false;
System.out.printf("\tfound device %s\n", d.toShortString());
for (AltosDevice p : prev_devices)
if (d.equals(p)) {
matched = true;
break;
}
if (!matched) {
System.out.printf("Identified new device %s\n", d.toShortString());
device = (AltosUSBDevice) d;
new_device = true;
break;
}
}
if (device != null)
break;
}
}
} catch (AltosSerialInUseException ee) {
do_exception(ee);
} catch (FileNotFoundException fe) {
do_exception(fe);
} catch (IOException ie) {
do_exception (ie);
} catch (TimeoutException te) {
do_exception (te);
} catch (InterruptedException ie) {
do_exception (ie);
}
}
public void cancel() {
t.interrupt();
}
public open_task(AltosDevice device, open_dialog dialog) {
this.device = device;
this.dialog = dialog;
t = new Thread(this);
t.start();
}
}
class open_dialog
extends AltosUIDialog
implements ActionListener
{
AltosUIFrame owner;
private JLabel opening_label;
private JButton cancel_button;
boolean done = false;
AltosLink link = null;
open_task open = null;
public void open_exception(Exception e) {
System.out.printf("open_exception\n");
setVisible(false);
exception(e);
done = true;
}
public void open_success(AltosLink link) {
System.out.printf("open_success\n");
setVisible(false);
this.link = link;
done = true;
}
public AltosLink do_open(open_task open) throws InterruptedException {
this.open = open;
setVisible(true);
return link;
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("cancel"))
if (open != null)
open.cancel();
done = true;
setVisible(false);
}
public open_dialog(AltosUIFrame in_owner) {
super(in_owner, "Open Flash Target Device", true);
owner = in_owner;
Container pane = getScrollablePane();
GridBagConstraints c = new GridBagConstraints();
Insets i = new Insets(4,4,4,4);
pane.setLayout(new GridBagLayout());
opening_label = new JLabel("Opening Device");
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LINE_START;
c.insets = i;
c.weightx = 0;
c.weighty = 0;
c.gridx = 0;
c.gridy = 0;
pane.add(opening_label, c);
cancel_button = new JButton("Cancel");
cancel_button.addActionListener(this);
cancel_button.setActionCommand("cancel");
c.gridy = 1;
pane.add(cancel_button, c);
pack();
setLocationRelativeTo(owner);
}
}
private boolean open_device() throws InterruptedException {
open_dialog dialog = new open_dialog(frame);
open_task open = new open_task(device, dialog);
link = dialog.do_open(open);
return link != null;
}
/*
* Execute the steps for flashing
* a device. Note that this returns immediately;
* this dialog is not modal
*/
void showDialog() {
if (!select_device())
return;
if (!select_source_file())
return;
try {
if (!open_device())
return;
} catch (InterruptedException ie) {
return;
}
build_dialog();
flash_task f = new flash_task(this);
}
public static void show(AltosUIFrame frame) {
AltosFlashUI ui = new AltosFlashUI(frame);
ui.showDialog();
}
public AltosFlashUI(AltosUIFrame in_frame) {
super(in_frame, "Program Altusmetrum Device", false);
frame = in_frame;
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.table.*;
public class AltosFlightInfoTableModel extends AbstractTableModel {
final static private String[] columnNames = {"Field", "Value"};
int rows;
int cols;
private String[][] data;
public int getColumnCount() { return cols; }
public int getRowCount() { return rows; }
public String getColumnName(int col) { return columnNames[col & 1]; }
public Object getValueAt(int row, int col) {
if (row >= rows || col >= cols)
return "";
return data[row][col];
}
int[] current_row;
public void reset() {
for (int i = 0; i < cols / 2; i++)
current_row[i] = 0;
}
public void clear() {
reset();
for (int c = 0; c < cols; c++)
for (int r = 0; r < rows; r++)
data[r][c] = "";
fireTableDataChanged();
}
public void addRow(int col, String name, String value) {
if (current_row[col] < rows) {
data[current_row[col]][col * 2] = name;
data[current_row[col]][col * 2 + 1] = value;
}
current_row[col]++;
}
public void finish() {
for (int c = 0; c < cols / 2; c++)
while (current_row[c] < rows)
addRow(c, "", "");
fireTableDataChanged();
}
public AltosFlightInfoTableModel (int in_rows, int in_cols) {
rows = in_rows;
cols = in_cols * 2;
data = new String[rows][cols];
current_row = new int[in_cols];
}
}

View File

@@ -0,0 +1,271 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import org.altusmetrum.altoslib_14.*;
public class AltosFlightStatsTable extends JComponent implements AltosFontListener {
GridBagLayout layout;
LinkedList<FlightStat> flight_stats = new LinkedList<FlightStat>();
class FlightStat implements AltosFontListener {
JLabel label;
JTextField[] value;
public void font_size_changed(int font_size) {
label.setFont(AltosUILib.label_font);
for (int i = 0; i < value.length; i++)
value[i].setFont(AltosUILib.value_font);
}
public void set(String ... values) {
for (int j = 0; j < values.length; j++)
value[j].setText(values[j]);
}
public FlightStat(GridBagLayout layout, int y, String label_text, String ... values) {
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad);
c.weighty = 1;
label = new JLabel(label_text);
label.setFont(AltosUILib.label_font);
label.setHorizontalAlignment(SwingConstants.LEFT);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0;
layout.setConstraints(label, c);
add(label);
value = new JTextField[values.length];
for (int j = 0; j < values.length; j++) {
value[j] = new JTextField(values[j]);
value[j].setEditable(false);
value[j].setFont(AltosUILib.value_font);
value[j].setHorizontalAlignment(SwingConstants.RIGHT);
c.gridx = j+1; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
layout.setConstraints(value[j], c);
add(value[j]);
}
flight_stats.add(this);
}
}
public void font_size_changed(int font_size) {
for (FlightStat f : flight_stats)
f.font_size_changed(font_size);
}
static String pos(double p, String pos, String neg) {
String h = pos;
if (p < 0) {
h = neg;
p = -p;
}
int deg = (int) Math.floor(p);
double min = (p - Math.floor(p)) * 60.0;
return String.format("%s %4d° %9.6f'", h, deg, min);
}
private FlightStat max_height_stat;
private FlightStat max_speed_stat;
private FlightStat max_accel_stat;
private FlightStat boost_accel_stat;
private FlightStat drogue_descent_stat;
private FlightStat main_descent_stat;
public void set_values(AltosFlightStats stats) {
if (max_height_stat != null && stats.max_height != AltosLib.MISSING) {
max_height_stat.set(String.format("%6.1f m", stats.max_height),
String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_height)));
}
if (max_speed_stat != null && stats.max_speed != AltosLib.MISSING) {
max_speed_stat.set(String.format("%6.1f m/s", stats.max_speed),
String.format("%5.0f fps", AltosConvert.mps_to_fps(stats.max_speed)),
String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
}
if (max_accel_stat != null && stats.max_acceleration != AltosLib.MISSING) {
max_accel_stat.set(String.format("%6.1f m/s²", stats.max_acceleration),
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
String.format("%6.2f G", AltosConvert.meters_to_g(stats.max_acceleration)));
}
if (boost_accel_stat != null && stats.state_accel[AltosLib.ao_flight_boost] != AltosLib.MISSING) {
boost_accel_stat.set(String.format("%6.1f m/s²", stats.state_accel[AltosLib.ao_flight_boost]),
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.state_accel[AltosLib.ao_flight_boost])),
String.format("%6.2f G", AltosConvert.meters_to_g(stats.state_accel[AltosLib.ao_flight_boost])));
}
if (drogue_descent_stat != null && stats.state_speed[AltosLib.ao_flight_drogue] != AltosLib.MISSING) {
drogue_descent_stat.set(String.format("%6.1f m/s", -stats.state_speed[AltosLib.ao_flight_drogue]),
String.format("%5.0f ft/s", -AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_drogue])));
}
if (main_descent_stat != null && stats.state_speed[AltosLib.ao_flight_main] != AltosLib.MISSING) {
main_descent_stat.set(String.format("%6.1f m/s", -stats.state_speed[AltosLib.ao_flight_main]),
String.format("%5.0f ft/s", -AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_main])));
}
}
public void set_stats(AltosFlightStats stats) {
int y = 0;
if (stats.serial != AltosLib.MISSING) {
if (stats.product != null && stats.firmware_version != null)
new FlightStat(layout, y++, "Device",
stats.product,
String.format("version %s", stats.firmware_version),
String.format("serial %d", stats.serial));
else
new FlightStat(layout, y++, "Serial", String.format("%d", stats.serial));
}
if (stats.flight != AltosLib.MISSING)
new FlightStat(layout, y++, "Flight", String.format("%d", stats.flight));
if (stats.year != AltosLib.MISSING && stats.hour != AltosLib.MISSING)
new FlightStat(layout, y++, "Date/Time",
String.format("%04d-%02d-%02d", stats.year, stats.month, stats.day),
String.format("%02d:%02d:%02d UTC", stats.hour, stats.minute, stats.second));
else {
if (stats.year != AltosLib.MISSING)
new FlightStat(layout, y++, "Date",
String.format("%04d-%02d-%02d", stats.year, stats.month, stats.day));
if (stats.hour != AltosLib.MISSING)
new FlightStat(layout, y++, "Time",
String.format("%02d:%02d:%02d UTC", stats.hour, stats.minute, stats.second));
}
if (stats.max_height != AltosLib.MISSING) {
max_height_stat = new FlightStat(layout, y++, "Maximum height",
String.format("%6.1f m", stats.max_height),
String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_height)));
}
if (stats.max_gps_height != AltosLib.MISSING) {
new FlightStat(layout, y++, "Maximum GPS height",
String.format("%6.1f m", stats.max_gps_height),
String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_gps_height)));
}
if (stats.max_speed != AltosLib.MISSING) {
max_speed_stat = new FlightStat(layout, y++, "Maximum speed",
String.format("%6.1f m/s", stats.max_speed),
String.format("%5.0f fps", AltosConvert.mps_to_fps(stats.max_speed)),
String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
}
if (stats.max_acceleration != AltosLib.MISSING)
max_accel_stat = new FlightStat(layout, y++, "Maximum boost acceleration",
String.format("%6.1f m/s²", stats.max_acceleration),
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
String.format("%6.2f G", AltosConvert.meters_to_g(stats.max_acceleration)));
if (stats.state_accel[AltosLib.ao_flight_boost] != AltosLib.MISSING)
boost_accel_stat = new FlightStat(layout, y++, "Average boost acceleration",
String.format("%6.1f m/s²", stats.state_accel[AltosLib.ao_flight_boost]),
String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.state_accel[AltosLib.ao_flight_boost])),
String.format("%6.2f G", AltosConvert.meters_to_g(stats.state_accel[AltosLib.ao_flight_boost])));
if (stats.state_time[AltosLib.ao_flight_boost] != 0 || stats.state_time[AltosLib.ao_flight_fast] != 0 || stats.state_time[AltosLib.ao_flight_coast] != 0) {
double boost_time = stats.state_time[AltosLib.ao_flight_boost];
double fast_time = stats.state_time[AltosLib.ao_flight_fast];
double coast_time = stats.state_time[AltosLib.ao_flight_coast];
if (fast_time > 0) {
new FlightStat(layout, y++, "Ascent time",
String.format("%6.1f s %s", boost_time,
AltosLib.state_name(AltosLib.ao_flight_boost)),
String.format("%6.1f s %s", fast_time,
AltosLib.state_name(AltosLib.ao_flight_fast)),
String.format("%6.1f s %s", coast_time,
AltosLib.state_name(AltosLib.ao_flight_coast)));
} else {
new FlightStat(layout, y++, "Ascent time",
String.format("%6.1f s %s", boost_time,
AltosLib.state_name(AltosLib.ao_flight_boost)),
String.format("%6.1f s %s", coast_time,
AltosLib.state_name(AltosLib.ao_flight_coast)));
}
}
if (stats.state_speed[AltosLib.ao_flight_drogue] != AltosLib.MISSING) {
String label;
if (stats.state_speed[AltosLib.ao_flight_main] == AltosLib.MISSING)
label = "Descent rate";
else
label = "Drogue descent rate";
drogue_descent_stat = new FlightStat(layout, y++, label,
String.format("%6.1f m/s", -stats.state_speed[AltosLib.ao_flight_drogue]),
String.format("%5.0f ft/s", -AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_drogue])));
}
if (stats.state_speed[AltosLib.ao_flight_main] != AltosLib.MISSING)
main_descent_stat = new FlightStat(layout, y++, "Main descent rate",
String.format("%6.1f m/s", -stats.state_speed[AltosLib.ao_flight_main]),
String.format("%5.0f ft/s", -AltosConvert.meters_to_feet(stats.state_speed[AltosLib.ao_flight_main])));
if (stats.state_time[AltosLib.ao_flight_drogue] != 0 || stats.state_time[AltosLib.ao_flight_main] != 0) {
double drogue_duration = stats.state_time[AltosLib.ao_flight_drogue];
double main_duration = stats.state_time[AltosLib.ao_flight_main];
double duration = drogue_duration + main_duration;
if (drogue_duration > 0 && main_duration > 0) {
new FlightStat(layout, y++, "Descent time",
String.format("%6.1f s %s", drogue_duration,
AltosLib.state_name(AltosLib.ao_flight_drogue)),
String.format("%6.1f s %s", main_duration,
AltosLib.state_name(AltosLib.ao_flight_main)));
} else if (duration > 0) {
new FlightStat(layout, y++, "Descent time",
String.format("%6.1f s", duration));
}
}
if (stats.landed_time > stats.boost_time)
new FlightStat(layout, y++, "Flight time",
String.format("%6.1f s", stats.landed_time - stats.boost_time));
if (stats.has_gps && stats.pad_lat != AltosLib.MISSING) {
new FlightStat(layout, y++, "Pad location",
pos(stats.pad_lat,"N","S"),
pos(stats.pad_lon,"E","W"));
}
if (stats.has_gps && stats.lat != AltosLib.MISSING) {
new FlightStat(layout, y++, "Last reported location",
pos(stats.lat,"N","S"),
pos(stats.lon,"E","W"));
}
}
public void tell_closing() {
AltosUIPreferences.unregister_font_listener(this);
}
public void filter_changed(AltosFlightStats stats) {
set_values(stats);
}
public AltosFlightStatsTable() {
layout = new GridBagLayout();
setLayout(layout);
AltosUIPreferences.register_font_listener(this);
}
public AltosFlightStatsTable(AltosFlightStats stats) {
this();
set_stats(stats);
}
}

394
altosuilib/AltosGraph.java Normal file
View File

@@ -0,0 +1,394 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosGraph extends AltosUIGraph {
/* These are in 'priority' order so that earlier ones get simpler line styles,
* then they are grouped so that adjacent ones get sequential colors
*/
static final private AltosUILineStyle height_color = new AltosUILineStyle();
static final private AltosUILineStyle speed_color = new AltosUILineStyle();
static final private AltosUILineStyle accel_color = new AltosUILineStyle();
static final private AltosUILineStyle vert_accel_color = new AltosUILineStyle();
static final private AltosUILineStyle orient_color = new AltosUILineStyle();
static final private AltosUILineStyle azimuth_color = new AltosUILineStyle();
static final private AltosUILineStyle compass_color = new AltosUILineStyle();
static final private AltosUILineStyle gps_height_color = new AltosUILineStyle();
static final private AltosUILineStyle altitude_color = new AltosUILineStyle();
static final private AltosUILineStyle battery_voltage_color = new AltosUILineStyle();
static final private AltosUILineStyle pyro_voltage_color = new AltosUILineStyle();
static final private AltosUILineStyle drogue_voltage_color = new AltosUILineStyle();
static final private AltosUILineStyle main_voltage_color = new AltosUILineStyle();
static final private AltosUILineStyle igniter_marker_color = new AltosUILineStyle(1);
static final private AltosUILineStyle kalman_height_color = new AltosUILineStyle();
static final private AltosUILineStyle kalman_speed_color = new AltosUILineStyle();
static final private AltosUILineStyle kalman_accel_color = new AltosUILineStyle();
static final private AltosUILineStyle gps_nsat_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_nsat_solution_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_nsat_view_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_course_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_ground_speed_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_speed_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_climb_rate_color = new AltosUILineStyle ();
static final private AltosUILineStyle gps_pdop_color = new AltosUILineStyle();
static final private AltosUILineStyle gps_hdop_color = new AltosUILineStyle();
static final private AltosUILineStyle gps_vdop_color = new AltosUILineStyle();
static final private AltosUILineStyle temperature_color = new AltosUILineStyle ();
static final private AltosUILineStyle dbm_color = new AltosUILineStyle();
static final private AltosUILineStyle pressure_color = new AltosUILineStyle ();
static final private AltosUILineStyle state_color = new AltosUILineStyle(0);
static final private AltosUILineStyle accel_along_color = new AltosUILineStyle();
static final private AltosUILineStyle accel_across_color = new AltosUILineStyle();
static final private AltosUILineStyle accel_through_color = new AltosUILineStyle();
static final private AltosUILineStyle gyro_roll_color = new AltosUILineStyle();
static final private AltosUILineStyle gyro_pitch_color = new AltosUILineStyle();
static final private AltosUILineStyle gyro_yaw_color = new AltosUILineStyle();
static final private AltosUILineStyle mag_along_color = new AltosUILineStyle();
static final private AltosUILineStyle mag_across_color = new AltosUILineStyle();
static final private AltosUILineStyle mag_through_color = new AltosUILineStyle();
static final private AltosUILineStyle mag_total_color = new AltosUILineStyle();
static final private AltosUILineStyle motor_pressure_color = new AltosUILineStyle();
static AltosUnits dop_units = null;
static AltosUnits tick_units = null;
AltosUIFlightSeries flight_series;
boolean enable_axis(int product_id, String label) {
switch (product_id) {
case AltosLib.product_easymotor:
return(label.equals(AltosUIFlightSeries.motor_pressure_name) ||
label.equals(AltosUIFlightSeries.accel_name));
default:
return true;
}
}
AltosUITimeSeries[] setup(AltosFlightStats stats, AltosUIFlightSeries flight_series) {
AltosCalData cal_data = flight_series.cal_data();
int product_id = cal_data.device_type;
AltosUIAxis height_axis, speed_axis, accel_axis, voltage_axis, temperature_axis, nsat_axis, dbm_axis;
AltosUIAxis pressure_axis, thrust_axis;
AltosUIAxis gyro_axis, orient_axis, mag_axis;
AltosUIAxis course_axis, dop_axis, tick_axis;
AltosUIAxis motor_pressure_axis;
if (stats != null && stats.serial != AltosLib.MISSING && stats.product != null && stats.flight != AltosLib.MISSING)
setName(String.format("%s %d flight %d\n", stats.product, stats.serial, stats.flight));
height_axis = newAxis("Height", AltosConvert.height, height_color);
pressure_axis = newAxis("Pressure", AltosConvert.pressure, pressure_color, 0);
speed_axis = newAxis("Speed", AltosConvert.speed, speed_color);
thrust_axis = newAxis("Thrust", AltosConvert.force, accel_color);
tick_axis = newAxis("Tick", tick_units, accel_color, 0);
accel_axis = newAxis("Acceleration", AltosConvert.accel, accel_color);
voltage_axis = newAxis("Voltage", AltosConvert.voltage, battery_voltage_color);
temperature_axis = newAxis("Temperature", AltosConvert.temperature, temperature_color, 0);
nsat_axis = newAxis("Satellites", null, gps_nsat_color,
AltosUIAxis.axis_include_zero | AltosUIAxis.axis_integer);
dbm_axis = newAxis("Signal Strength", null, dbm_color, 0);
gyro_axis = newAxis("Rotation Rate", AltosConvert.rotation_rate, gyro_roll_color, 0);
orient_axis = newAxis("Angle", AltosConvert.orient, orient_color, 0);
mag_axis = newAxis("Magnetic Field", AltosConvert.magnetic_field, mag_along_color, 0);
course_axis = newAxis("Course", AltosConvert.orient, gps_course_color, 0);
dop_axis = newAxis("Dilution of Precision", dop_units, gps_pdop_color, 0);
motor_pressure_axis = newAxis("Motor Pressure", AltosConvert.pressure, motor_pressure_color, 0);
flight_series.register_axis("default",
speed_color,
false,
speed_axis);
flight_series.register_marker(AltosUIFlightSeries.state_name,
state_color,
enable_axis(product_id, AltosUIFlightSeries.state_name),
plot,
true);
flight_series.register_marker(AltosUIFlightSeries.pyro_fired_name,
igniter_marker_color,
true,
plot,
false);
flight_series.register_axis(AltosUIFlightSeries.tick_name,
accel_color,
false,
tick_axis);
flight_series.register_axis(AltosUIFlightSeries.accel_name,
accel_color,
enable_axis(product_id, AltosUIFlightSeries.accel_name),
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.vert_accel_name,
vert_accel_color,
enable_axis(product_id, AltosUIFlightSeries.vert_accel_name),
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.kalman_accel_name,
kalman_accel_color,
false,
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.rssi_name,
dbm_color,
false,
dbm_axis);
flight_series.register_axis(AltosUIFlightSeries.speed_name,
speed_color,
enable_axis(product_id, AltosUIFlightSeries.speed_name),
speed_axis);
flight_series.register_axis(AltosUIFlightSeries.kalman_speed_name,
kalman_speed_color,
enable_axis(product_id, AltosUIFlightSeries.kalman_speed_name),
speed_axis);
flight_series.register_axis(AltosUIFlightSeries.pressure_name,
pressure_color,
false,
pressure_axis);
flight_series.register_axis(AltosUIFlightSeries.height_name,
height_color,
enable_axis(product_id, AltosUIFlightSeries.height_name),
height_axis);
flight_series.register_axis(AltosUIFlightSeries.altitude_name,
altitude_color,
false,
height_axis);
flight_series.register_axis(AltosUIFlightSeries.kalman_height_name,
kalman_height_color,
false,
height_axis);
flight_series.register_axis(AltosUIFlightSeries.temperature_name,
temperature_color,
false,
temperature_axis);
flight_series.register_axis(AltosUIFlightSeries.battery_voltage_name,
battery_voltage_color,
false,
voltage_axis);
flight_series.register_axis(AltosUIFlightSeries.pyro_voltage_name,
pyro_voltage_color,
false,
voltage_axis);
flight_series.register_axis(AltosUIFlightSeries.apogee_voltage_name,
drogue_voltage_color,
false,
voltage_axis);
flight_series.register_axis(AltosUIFlightSeries.main_voltage_name,
main_voltage_color,
false,
voltage_axis);
flight_series.register_axis(AltosUIFlightSeries.sats_in_view_name,
gps_nsat_view_color,
false,
nsat_axis);
flight_series.register_axis(AltosUIFlightSeries.sats_in_soln_name,
gps_nsat_solution_color,
false,
nsat_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_pdop_name,
gps_pdop_color,
false,
dop_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_hdop_name,
gps_hdop_color,
false,
dop_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_vdop_name,
gps_vdop_color,
false,
dop_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_altitude_name,
gps_height_color,
false,
height_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_height_name,
gps_height_color,
false,
height_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_ground_speed_name,
gps_ground_speed_color,
false,
speed_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_ascent_rate_name,
gps_climb_rate_color,
false,
speed_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_course_name,
gps_course_color,
false,
course_axis);
flight_series.register_axis(AltosUIFlightSeries.gps_speed_name,
gps_speed_color,
false,
speed_axis);
flight_series.register_axis(AltosUIFlightSeries.accel_along_name,
accel_along_color,
false,
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.accel_across_name,
accel_across_color,
false,
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.accel_through_name,
accel_through_color,
false,
accel_axis);
flight_series.register_axis(AltosUIFlightSeries.gyro_roll_name,
gyro_roll_color,
false,
gyro_axis);
flight_series.register_axis(AltosUIFlightSeries.gyro_pitch_name,
gyro_pitch_color,
false,
gyro_axis);
flight_series.register_axis(AltosUIFlightSeries.gyro_yaw_name,
gyro_yaw_color,
false,
gyro_axis);
flight_series.register_axis(AltosUIFlightSeries.mag_along_name,
mag_along_color,
false,
mag_axis);
flight_series.register_axis(AltosUIFlightSeries.mag_across_name,
mag_across_color,
false,
mag_axis);
flight_series.register_axis(AltosUIFlightSeries.mag_through_name,
mag_through_color,
false,
mag_axis);
flight_series.register_axis(AltosUIFlightSeries.mag_total_name,
mag_total_color,
false,
mag_axis);
flight_series.register_axis(AltosUIFlightSeries.orient_name,
orient_color,
false,
orient_axis);
flight_series.register_axis(AltosUIFlightSeries.azimuth_name,
azimuth_color,
false,
orient_axis);
flight_series.register_axis(AltosUIFlightSeries.compass_name,
compass_color,
false,
orient_axis);
flight_series.register_axis(AltosUIFlightSeries.thrust_name,
accel_color,
enable_axis(product_id, AltosUIFlightSeries.thrust_name),
thrust_axis);
for (int channel = 0; channel < 8; channel++) {
flight_series.register_axis(flight_series.igniter_voltage_name(channel),
new AltosUILineStyle(),
false,
voltage_axis);
}
flight_series.register_axis(AltosUIFlightSeries.motor_pressure_name,
motor_pressure_color,
enable_axis(product_id, AltosUIFlightSeries.motor_pressure_name),
motor_pressure_axis);
flight_series.check_axes();
return flight_series.series(cal_data);
}
public void set_data(AltosFlightStats stats, AltosUIFlightSeries flight_series) {
set_series(setup(stats, flight_series));
}
public AltosGraph(AltosUIEnable enable) {
super(enable, "Flight");
}
public AltosGraph(AltosUIEnable enable, AltosFlightStats stats, AltosUIFlightSeries flight_series) {
this(enable);
this.flight_series = flight_series;
set_series(setup(stats, flight_series));
}
}

View File

@@ -0,0 +1,299 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import org.altusmetrum.altoslib_14.*;
public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener {
private AltosFlightInfoTableModel model;
static final int info_columns = 4;
static final int info_rows = 18;
private AltosState last_state;
private AltosListenerState last_listener_state;
int desired_row_height() {
FontMetrics infoValueMetrics = getFontMetrics(AltosUILib.table_value_font);
return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10;
}
int text_width(String t) {
FontMetrics infoValueMetrics = getFontMetrics(AltosUILib.table_value_font);
return infoValueMetrics.stringWidth(t);
}
void set_layout() {
setRowHeight(desired_row_height());
for (int i = 0; i < info_columns * 2; i++)
{
TableColumn column = getColumnModel().getColumn(i);
if ((i & 1) == 0)
column.setPreferredWidth(text_width(" Satellites Visible"));
else
column.setPreferredWidth(text_width("W 179°59.99999' "));
}
}
public AltosInfoTable() {
super(new AltosFlightInfoTableModel(info_rows, info_columns));
model = (AltosFlightInfoTableModel) getModel();
setFont(AltosUILib.table_value_font);
addHierarchyListener(this);
setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS);
setShowGrid(true);
set_layout();
doLayout();
}
public void font_size_changed(int font_size) {
setFont(AltosUILib.table_value_font);
set_layout();
doLayout();
}
public void units_changed(boolean imperial_units) {
}
public void hierarchyChanged(HierarchyEvent e) {
if (last_state != null && isShowing()) {
AltosState state = last_state;
AltosListenerState listener_state = last_listener_state;
last_state = null;
last_listener_state = null;
show(state, listener_state);
}
}
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
public void reset() {
model.reset();
}
void info_add_row(int col, String name, String value) {
model.addRow(col, name, value);
}
void info_add_row(int col, String name, String format, Object... parameters) {
info_add_row (col, name, String.format(format, parameters));
}
void info_add_deg(int col, String name, double v, int pos, int neg) {
int c = pos;
if (v < 0) {
c = neg;
v = -v;
}
double deg = Math.floor(v);
double min = (v - deg) * 60;
info_add_row(col, name, String.format("%c %3.0f°%08.5f'", c, deg, min));
}
void info_finish() {
model.finish();
}
public void clear() {
model.clear();
}
public String getName() { return "Table"; }
public void show(AltosState state, AltosListenerState listener_state) {
AltosCalData cal_data = state.cal_data();
if (!isShowing()) {
last_state = state;
last_listener_state = listener_state;
return;
}
reset();
if (state != null) {
if (cal_data.device_type != AltosLib.MISSING)
info_add_row(0, "Device", "%s", AltosLib.product_name(cal_data.device_type));
else if (cal_data.product != null)
info_add_row(0, "Device", "%s", cal_data.product);
if (state.tick() != AltosLib.MISSING)
info_add_row(0, "Tick", "%6d", state.tick());
if (state.altitude() != AltosLib.MISSING)
info_add_row(0, "Altitude", "%6.0f m", state.altitude());
if (cal_data.ground_altitude != AltosLib.MISSING)
info_add_row(0, "Pad altitude", "%6.0f m", cal_data.ground_altitude);
if (state.height() != AltosLib.MISSING)
info_add_row(0, "Height", "%6.0f m", state.height());
if (state.max_height() != AltosLib.MISSING)
info_add_row(0, "Max height", "%6.0f m", state.max_height());
if (state.acceleration() != AltosLib.MISSING)
info_add_row(0, "Acceleration", "%8.1f m/s²", state.acceleration());
if (state.max_acceleration() != AltosLib.MISSING)
info_add_row(0, "Max acceleration", "%8.1f m/s²", state.max_acceleration());
if (state.speed() != AltosLib.MISSING)
info_add_row(0, "Speed", "%8.1f m/s", state.speed());
if (state.max_speed() != AltosLib.MISSING)
info_add_row(0, "Max Speed", "%8.1f m/s", state.max_speed());
if (state.orient() != AltosLib.MISSING)
info_add_row(0, "Tilt", "%4.0f °", state.orient());
if (state.max_orient() != AltosLib.MISSING)
info_add_row(0, "Max Tilt", "%4.0f °", state.max_orient());
if (state.temperature != AltosLib.MISSING)
info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
if (state.battery_voltage != AltosLib.MISSING)
info_add_row(0, "Battery", "%9.2f V", state.battery_voltage);
if (state.apogee_voltage != AltosLib.MISSING)
info_add_row(0, "Drogue", "%9.2f V", state.apogee_voltage);
if (state.main_voltage != AltosLib.MISSING)
info_add_row(0, "Main", "%9.2f V", state.main_voltage);
}
if (listener_state != null) {
info_add_row(0, "CRC Errors", "%6d", listener_state.crc_errors);
if (listener_state.battery != AltosLib.MISSING)
info_add_row(0, "Receiver Battery", "%9.2f", listener_state.battery);
}
if (state != null) {
if (state.gps == null || !state.gps.connected) {
info_add_row(1, "GPS", "not available");
} else {
if (state.gps_ready)
info_add_row(1, "GPS state", "%s", "ready");
else
info_add_row(1, "GPS state", "wait (%d)",
state.gps_waiting);
if (state.gps.locked)
info_add_row(1, "GPS", " locked");
else if (state.gps.connected)
info_add_row(1, "GPS", " unlocked");
else
info_add_row(1, "GPS", " missing");
info_add_row(1, "Satellites", "%6d", state.gps.nsat);
if (state.gps.lat != AltosLib.MISSING)
info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
if (state.gps.lon != AltosLib.MISSING)
info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
if (state.gps.alt != AltosLib.MISSING)
info_add_row(1, "GPS altitude", "%8.1f m", state.gps.alt);
if (state.gps_height != AltosLib.MISSING)
info_add_row(1, "GPS height", "%8.1f m", state.gps_height);
if (state.gps.ground_speed != AltosLib.MISSING && state.gps.course != AltosLib.MISSING)
info_add_row(1, "GPS ground speed", "%6.1f m/s %3d°",
state.gps.ground_speed,
state.gps.course);
if (state.gps.climb_rate != AltosLib.MISSING)
info_add_row(1, "GPS climb rate", "%6.1f m/s",
state.gps.climb_rate);
if (state.gps.h_error != AltosLib.MISSING && state.gps.v_error != AltosLib.MISSING)
info_add_row(1, "GPS error", "%6.0f m(h)%6.0f m(v)",
state.gps.h_error, state.gps.v_error);
if (state.gps.pdop != AltosLib.MISSING &&
state.gps.hdop != AltosLib.MISSING &&
state.gps.vdop != AltosLib.MISSING)
info_add_row(1, "GPS dop", "%3.1fp/%3.1fh/%3.1fv",
state.gps.pdop,
state.gps.hdop,
state.gps.vdop);
if (state.npad > 0) {
if (state.from_pad != null) {
info_add_row(1, "Ground pad dist", "%6d m",
(int) (state.from_pad.distance + 0.5));
info_add_row(1, "Direction from pad", "%6d°",
(int) (state.from_pad.bearing + 0.5));
info_add_row(1, "Elevation from pad", "%6d°",
(int) (state.elevation + 0.5));
info_add_row(1, "Range from pad", "%6d m",
(int) (state.range + 0.5));
} else {
info_add_row(1, "Distance from pad", "unknown");
info_add_row(1, "Direction from pad", "unknown");
info_add_row(1, "Elevation from pad", "unknown");
info_add_row(1, "Range from pad", "unknown");
}
info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
info_add_row(1, "Pad GPS alt", "%6.0f m", state.gps_ground_altitude());
}
if (state.gps.year != AltosLib.MISSING)
info_add_row(2, "GPS date", "%04d-%02d-%02d",
state.gps.year,
state.gps.month,
state.gps.day);
if (state.gps.hour != AltosLib.MISSING)
info_add_row(2, "GPS time", " %02d:%02d:%02d",
state.gps.hour,
state.gps.minute,
state.gps.second);
//int nsat_vis = 0;
int c;
if (state.gps.cc_gps_sat == null)
info_add_row(2, "Satellites Visible", "%4d", 0);
else {
info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
info_add_row(2, "Satellite id,C/N0",
"%4d, %4d",
state.gps.cc_gps_sat[c].svid,
state.gps.cc_gps_sat[c].c_n0);
}
}
}
}
if (state != null && state.accel_along() != AltosLib.MISSING) {
info_add_row(3, "Accel along", "%8.1f m/s²", state.accel_along());
info_add_row(3, "Accel across", "%8.1f m/s²", state.accel_across());
info_add_row(3, "Accel through", "%8.1f m/s²", state.accel_through());
}
if (state != null && state.gyro_roll() != AltosLib.MISSING) {
info_add_row(3, "Gyro roll", "%8.1f °/s", state.gyro_roll());
info_add_row(3, "Gyro pitch", "%8.1f °/s", state.gyro_pitch());
info_add_row(3, "Gyro yaw", "%8.1f °/s", state.gyro_yaw());
}
if (state != null && state.mag_along() != AltosLib.MISSING) {
/* Report mag in nanoteslas (1 G = 100000 nT (or γ)) */
info_add_row(3, "Mag along", "%8.1f µT", state.mag_along() * 100.0);
info_add_row(3, "Mag across", "%8.1f µT", state.mag_across() * 100.0);
info_add_row(3, "Mag Through", "%8.1f µT", state.mag_through() * 100.0);
info_add_row(3, "Mag Bearing", "%8.1f°", Math.atan2(state.mag_across(), state.mag_through()) * 180/Math.PI);
}
if (state != null && state.igniter_voltage != null) {
for (int i = 0; i < state.igniter_voltage.length; i++)
info_add_row(3, AltosLib.igniter_name(i), "%9.2f V", state.igniter_voltage[i]);
}
info_finish();
}
}

46
altosuilib/AltosLed.java Normal file
View File

@@ -0,0 +1,46 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
public class AltosLed extends JLabel {
ImageIcon on, off;
ImageIcon create_icon(String path) {
java.net.URL imgURL = AltosUILib.class.getResource(path);
if (imgURL != null)
return new ImageIcon(imgURL);
System.err.printf("Cannot find icon \"%s\"\n", path);
return null;
}
public void set(boolean set) {
if (set)
setIcon(on);
else
setIcon(off);
}
public AltosLed(String on_path, String off_path) {
on = create_icon(on_path);
off = create_icon(off_path);
setIcon(off);
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
public class AltosLights extends JComponent {
GridBagLayout gridbag;
AltosLed red, green;
ImageIcon create_icon(String path, String description) {
java.net.URL imgURL = AltosUILib.class.getResource(path);
if (imgURL != null)
return new ImageIcon(imgURL, description);
System.err.printf("Cannot find icon \"%s\"\n", path);
return null;
}
public void set (boolean on) {
if (on) {
red.set(false);
green.set(true);
} else {
red.set(true);
green.set(false);
}
}
public AltosLights() {
GridBagConstraints c;
gridbag = new GridBagLayout();
setLayout(gridbag);
c = new GridBagConstraints();
red = new AltosLed("/redled.png", "/grayled.png");
c.gridx = 0; c.gridy = 0;
c.insets = new Insets (0, 5, 0, 5);
gridbag.setConstraints(red, c);
add(red);
red.set(true);
green = new AltosLed("/greenled.png", "/grayled.png");
c.gridx = 1; c.gridy = 0;
gridbag.setConstraints(green, c);
add(green);
green.set(false);
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public interface AltosPositionListener {
public void position_changed(int position);
}

View File

@@ -0,0 +1,230 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public class AltosRomconfigUI
extends AltosUIDialog
implements ActionListener
{
Container pane;
Box box;
JLabel product_label;
JLabel serial_label;
JLabel radio_calibration_label;
JLabel product_value;
JFrame owner;
JTextField serial_value;
JTextField radio_calibration_value;
JButton ok;
JButton cancel;
/* Build the UI using a grid bag */
public AltosRomconfigUI(JFrame frame, AltosRomconfig config) {
super (frame, "Configure Rom Values", true);
owner = frame;
GridBagConstraints c;
Insets il = new Insets(4,4,4,4);
Insets ir = new Insets(4,4,4,4);
pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
int y = 0;
/* Product name */
/* Serial */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
product_label = new JLabel("Product:");
pane.add(product_label, c);
c = new GridBagConstraints();
c.gridx = 3; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
String product = "unknown";
if (config != null)
product = config.usb_product;
product_value = new JLabel(product);
pane.add(product_value, c);
y++;
/* Serial */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
serial_label = new JLabel("Serial:");
pane.add(serial_label, c);
c = new GridBagConstraints();
c.gridx = 3; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
serial_value = new JTextField("00000000");
pane.add(serial_value, c);
y++;
if (config == null || AltosLib.has_radio(config.usb_id.pid)) {
/* Radio calibration value */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
c.ipady = 5;
radio_calibration_label = new JLabel("Radio Calibration:");
pane.add(radio_calibration_label, c);
c = new GridBagConstraints();
c.gridx = 3; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.anchor = GridBagConstraints.LINE_START;
c.insets = ir;
c.ipady = 5;
radio_calibration_value = new JTextField("00000000");
pane.add(radio_calibration_value, c);
y++;
}
/* Buttons */
c = new GridBagConstraints();
c.gridx = 0; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.insets = il;
ok = new JButton("OK");
pane.add(ok, c);
ok.addActionListener(this);
ok.setActionCommand("ok");
c = new GridBagConstraints();
c.gridx = 3; c.gridy = y;
c.gridwidth = 3;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.insets = il;
cancel = new JButton("Cancel");
pane.add(cancel, c);
cancel.addActionListener(this);
cancel.setActionCommand("cancel");
y++;
pack();
setLocationRelativeTo(owner);
set(config);
}
boolean selected;
/* Listen for events from our buttons */
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("ok")) {
AltosRomconfig romconfig = romconfig();
if (romconfig == null || !romconfig.valid()) {
JOptionPane.showMessageDialog(this,
"Invalid serial number or radio calibration value",
"Invalid rom configuration",
JOptionPane.ERROR_MESSAGE);
return;
}
selected = true;
}
setVisible(false);
}
int serial() {
return Integer.parseInt(serial_value.getText());
}
void set_serial(int serial) {
serial_value.setText(String.format("%d", serial));
}
int radio_calibration() {
if (radio_calibration_value == null)
return 0;
return Integer.parseInt(radio_calibration_value.getText());
}
void set_radio_calibration(int calibration) {
if (radio_calibration_value == null)
return;
radio_calibration_value.setText(String.format("%d", calibration));
}
public void set(AltosRomconfig config) {
if (config != null && config.valid()) {
set_serial(config.serial_number);
set_radio_calibration(config.radio_calibration);
}
}
AltosRomconfig romconfig() {
try {
return new AltosRomconfig(serial(), radio_calibration());
} catch (NumberFormatException ne) {
return null;
}
}
public AltosRomconfig showDialog() {
setVisible(true);
if (selected)
return romconfig();
return null;
}
public static AltosRomconfig show(JFrame frame, AltosRomconfig config) {
AltosRomconfigUI ui = new AltosRomconfigUI(frame, config);
return ui.showDialog();
}
}

677
altosuilib/AltosScanUI.java Normal file
View File

@@ -0,0 +1,677 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import java.util.*;
import java.text.*;
import java.util.concurrent.*;
import org.altusmetrum.altoslib_14.*;
class AltosScanResult {
String callsign;
int serial;
int flight;
AltosFrequency frequency;
int telemetry;
int rate;
boolean interrupted = false;
public String toString() {
return String.format("%-9.9s serial %-4d flight %-4d (%s %s %d)",
callsign, serial, flight,
frequency.toShortString(),
AltosLib.telemetry_name(telemetry),
AltosLib.ao_telemetry_rate_values[rate]);
}
public String toShortString() {
return String.format("%s %d %d %7.3f %d %d",
callsign, serial, flight, frequency, telemetry, rate);
}
public AltosScanResult(String in_callsign, int in_serial,
int in_flight, AltosFrequency in_frequency,
int in_telemetry,
int in_rate) {
callsign = in_callsign;
serial = in_serial;
flight = in_flight;
frequency = in_frequency;
telemetry = in_telemetry;
rate = in_rate;
}
public int hashCode() {
return serial ^ frequency.hashCode() ^ telemetry ^ rate;
}
public boolean equals(Object o) {
if (o == null)
return false;
if (!(o instanceof AltosScanResult))
return false;
AltosScanResult other = (AltosScanResult) o;
return (serial == other.serial &&
frequency.equals(other.frequency) &&
telemetry == other.telemetry &&
rate == other.rate);
}
public boolean up_to_date(AltosScanResult other) {
if (flight == 0 && other.flight != 0) {
flight = other.flight;
return false;
}
if (callsign.equals("N0CALL") && !other.callsign.equals("N0CALL")) {
callsign = other.callsign;
return false;
}
return true;
}
}
class AltosScanResults extends LinkedList<AltosScanResult> implements ListModel<AltosScanResult> {
LinkedList<ListDataListener> listeners = new LinkedList<ListDataListener>();
void changed(ListDataEvent de) {
for (ListDataListener l : listeners)
l.contentsChanged(de);
}
public boolean add(AltosScanResult r) {
int i = 0;
for (AltosScanResult old : this) {
if (old.equals(r)) {
if (!old.up_to_date(r))
changed (new ListDataEvent(this,
ListDataEvent.CONTENTS_CHANGED,
i, i));
return true;
}
i++;
}
super.add(r);
changed(new ListDataEvent(this,
ListDataEvent.INTERVAL_ADDED,
this.size() - 2, this.size() - 1));
return true;
}
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
public void removeListDataListener(ListDataListener l) {
listeners.remove(l);
}
public AltosScanResult getElementAt(int i) {
return this.get(i);
}
public int getSize() {
return this.size();
}
}
public class AltosScanUI
extends AltosUIDialog
implements ActionListener
{
AltosUIFrame owner;
AltosDevice device;
AltosConfigData config_data;
AltosTelemetryReader reader;
private JList<AltosScanResult> list;
private JLabel scanning_label;
private JLabel frequency_label;
private JLabel telemetry_label;
private JLabel rate_label;
private JButton cancel_button;
private JButton monitor_button;
private JCheckBox[] telemetry_boxes;
private JCheckBox[] rate_boxes;
javax.swing.Timer timer;
AltosScanResults results = new AltosScanResults();
int telemetry;
boolean select_telemetry = false;
int rate;
boolean select_rate = false;
final static int timeout = 1200;
TelemetryHandler handler;
Thread thread;
AltosFrequency[] frequencies;
int frequency_index;
int packet_count;
int tick_count;
void scan_exception(Exception e) {
if (e instanceof FileNotFoundException) {
JOptionPane.showMessageDialog(owner,
((FileNotFoundException) e).getMessage(),
"Cannot open target device",
JOptionPane.ERROR_MESSAGE);
} else if (e instanceof AltosSerialInUseException) {
JOptionPane.showMessageDialog(owner,
String.format("Device \"%s\" already in use",
device.toShortString()),
"Device in use",
JOptionPane.ERROR_MESSAGE);
} else if (e instanceof IOException) {
IOException ee = (IOException) e;
JOptionPane.showMessageDialog(owner,
device.toShortString(),
ee.getLocalizedMessage(),
JOptionPane.ERROR_MESSAGE);
} else {
JOptionPane.showMessageDialog(owner,
String.format("Connection to \"%s\" failed",
device.toShortString()),
"Connection Failed",
JOptionPane.ERROR_MESSAGE);
}
close();
}
class TelemetryHandler implements Runnable {
public void run() {
boolean interrupted = false;
try {
for (;;) {
try {
AltosState state = reader.read();
if (state == null)
continue;
packet_count++;
AltosCalData cal_data = state.cal_data();
if (cal_data.flight != AltosLib.MISSING) {
final AltosScanResult result = new AltosScanResult(cal_data.callsign,
cal_data.serial,
cal_data.flight,
frequencies[frequency_index],
telemetry,
rate);
Runnable r = new Runnable() {
public void run() {
results.add(result);
}
};
SwingUtilities.invokeLater(r);
}
} catch (ParseException pp) {
} catch (AltosCRCException ce) {
}
}
} catch (InterruptedException ee) {
interrupted = true;
} catch (IOException ie) {
} finally {
reader.close(interrupted);
}
}
}
void set_label() {
frequency_label.setText(String.format("Frequency: %s", frequencies[frequency_index].toString()));
if (select_telemetry)
telemetry_label.setText(String.format("Telemetry: %s", AltosLib.telemetry_name(telemetry)));
if (select_rate)
rate_label.setText(String.format("Rate: %d baud", AltosLib.ao_telemetry_rate_values[rate]));
}
void set_telemetry() {
reader.set_telemetry(telemetry);
}
void set_rate() {
reader.set_telemetry_rate(rate);
}
void set_frequency() throws InterruptedException, TimeoutException {
reader.set_frequency(frequencies[frequency_index].frequency);
reader.reset();
}
void next() throws InterruptedException, TimeoutException {
reader.set_monitor(false);
if (select_rate) {
boolean wrapped = false;
do {
++rate;
if (rate > AltosLib.ao_telemetry_rate_max) {
wrapped = true;
rate = 0;
}
} while (!rate_boxes[rate].isSelected());
set_rate();
if (!wrapped) {
set_label();
return;
}
}
if (select_telemetry) {
boolean wrapped = false;
do {
++telemetry;
if (telemetry > AltosLib.ao_telemetry_max) {
wrapped = true;
telemetry = AltosLib.ao_telemetry_min;
}
} while (!telemetry_boxes[telemetry - AltosLib.ao_telemetry_min].isSelected());
set_telemetry();
if (!wrapped) {
set_label();
return;
}
}
packet_count = 0;
tick_count = 0;
++frequency_index;
if (frequency_index >= frequencies.length)
frequency_index = 0;
set_frequency();
set_label();
reader.set_monitor(true);
}
void close() {
if (thread != null && thread.isAlive()) {
thread.interrupt();
try {
thread.join();
} catch (InterruptedException ie) {}
}
thread = null;
if (timer != null)
timer.stop();
setVisible(false);
dispose();
}
void tick_timer() throws InterruptedException, TimeoutException {
++tick_count;
if (packet_count == 0 || tick_count > 5)
next();
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
try {
if (cmd.equals("cancel"))
close();
if (cmd.equals("tick"))
tick_timer();
if (cmd.equals("telemetry")) {
int k;
int scanning_telemetry = 0;
for (k = AltosLib.ao_telemetry_min; k <= AltosLib.ao_telemetry_max; k++) {
int j = k - AltosLib.ao_telemetry_min;
if (telemetry_boxes[j].isSelected())
scanning_telemetry |= (1 << k);
}
if (scanning_telemetry == 0) {
scanning_telemetry |= (1 << AltosLib.ao_telemetry_standard);
telemetry_boxes[AltosLib.ao_telemetry_standard - AltosLib.ao_telemetry_min].setSelected(true);
}
AltosUIPreferences.set_scanning_telemetry(scanning_telemetry);
}
if (cmd.equals("rate")) {
int k;
int scanning_rate = 0;
for (k = 0; k <= AltosLib.ao_telemetry_rate_max; k++) {
if (rate_boxes[k].isSelected())
scanning_rate |= (1 << k);
}
if (scanning_rate == 0) {
scanning_rate = (1 << 0);
rate_boxes[0].setSelected(true);
}
AltosUIPreferences.set_scanning_telemetry_rate(scanning_rate);
}
if (cmd.equals("monitor")) {
close();
AltosScanResult r = (AltosScanResult) (list.getSelectedValue());
if (r != null) {
if (device != null) {
if (reader != null) {
reader.set_telemetry(r.telemetry);
reader.set_telemetry_rate(r.rate);
reader.set_frequency(r.frequency.frequency);
reader.save_frequency();
reader.save_telemetry();
reader.save_telemetry_rate();
owner.scan_device_selected(device);
}
}
}
}
} catch (TimeoutException te) {
close();
} catch (InterruptedException ie) {
close();
}
}
/* A window listener to catch closing events and tell the config code */
class ConfigListener extends WindowAdapter {
AltosScanUI ui;
public ConfigListener(AltosScanUI this_ui) {
ui = this_ui;
}
public void windowClosing(WindowEvent e) {
ui.actionPerformed(new ActionEvent(e.getSource(),
ActionEvent.ACTION_PERFORMED,
"close"));
}
}
private boolean open() {
device = AltosDeviceUIDialog.show(owner, AltosLib.product_basestation);
if (device == null)
return false;
try {
reader = new AltosTelemetryReader(new AltosSerial(device));
set_frequency();
set_telemetry();
set_rate();
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
reader.flush();
handler = new TelemetryHandler();
thread = new Thread(handler);
thread.start();
return true;
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(owner,
ee.getMessage(),
"Cannot open target device",
JOptionPane.ERROR_MESSAGE);
} catch (AltosSerialInUseException si) {
JOptionPane.showMessageDialog(owner,
String.format("Device \"%s\" already in use",
device.toShortString()),
"Device in use",
JOptionPane.ERROR_MESSAGE);
} catch (IOException ee) {
JOptionPane.showMessageDialog(owner,
device.toShortString(),
"Unkonwn I/O error",
JOptionPane.ERROR_MESSAGE);
} catch (TimeoutException te) {
JOptionPane.showMessageDialog(owner,
device.toShortString(),
"Timeout error",
JOptionPane.ERROR_MESSAGE);
} catch (InterruptedException ie) {
JOptionPane.showMessageDialog(owner,
device.toShortString(),
"Interrupted exception",
JOptionPane.ERROR_MESSAGE);
}
if (reader != null)
reader.close(false);
return false;
}
public AltosScanUI(AltosUIFrame in_owner, boolean in_select_telemetry) {
super(in_owner, "Scan Telemetry", false);
owner = in_owner;
select_telemetry = in_select_telemetry;
select_rate = true;
frequencies = AltosUIPreferences.common_frequencies();
frequency_index = 0;
telemetry = AltosLib.ao_telemetry_standard;
rate = 0;
if (!open())
return;
Container pane = getScrollablePane();
GridBagConstraints c = new GridBagConstraints();
Insets i = new Insets(4,4,4,4);
timer = new javax.swing.Timer(timeout, this);
timer.setActionCommand("tick");
timer.restart();
owner = in_owner;
pane.setLayout(new GridBagLayout());
scanning_label = new JLabel("Scanning:");
frequency_label = new JLabel("");
if (select_telemetry) {
telemetry_label = new JLabel("");
telemetry_label.setPreferredSize(new Dimension(100, 16));
telemetry = AltosLib.ao_telemetry_min;
} else {
telemetry = AltosLib.ao_telemetry_standard;
}
if (select_rate) {
rate_label = new JLabel("");
rate_label.setPreferredSize(new Dimension(100, 16));
}
set_label();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.LINE_START;
c.insets = i;
c.weightx = 0;
c.weighty = 0;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 2;
pane.add(scanning_label, c);
c.gridy = 1;
pane.add(frequency_label, c);
int y_offset_rate = 3;
int y_offset_telem = 3;
int check_x = 0;
if (select_rate && select_telemetry)
c.gridwidth = 1;
if (select_rate) {
c.gridy = 2;
c.gridx = check_x++;
pane.add(rate_label, c);
int scanning_rate = AltosUIPreferences.scanning_telemetry_rate();
rate_boxes = new JCheckBox[AltosLib.ao_telemetry_rate_max + 1];
for (int k = 0; k <= AltosLib.ao_telemetry_rate_max; k++) {
rate_boxes[k] = new JCheckBox(String.format("%d baud", AltosLib.ao_telemetry_rate_values[k]));
c.gridy = y_offset_rate + k;
pane.add(rate_boxes[k], c);
rate_boxes[k].setActionCommand("rate");
rate_boxes[k].addActionListener(this);
rate_boxes[k].setSelected((scanning_rate & (1 << k)) != 0);
}
y_offset_rate += AltosLib.ao_telemetry_rate_max + 1;
}
if (select_telemetry) {
c.gridy = 2;
c.gridx = check_x++;
pane.add(telemetry_label, c);
int scanning_telemetry = AltosUIPreferences.scanning_telemetry();
telemetry_boxes = new JCheckBox[AltosLib.ao_telemetry_max - AltosLib.ao_telemetry_min + 1];
for (int k = AltosLib.ao_telemetry_min; k <= AltosLib.ao_telemetry_max; k++) {
int j = k - AltosLib.ao_telemetry_min;
telemetry_boxes[j] = new JCheckBox(AltosLib.telemetry_name(k));
c.gridy = y_offset_telem + j;
pane.add(telemetry_boxes[j], c);
telemetry_boxes[j].setActionCommand("telemetry");
telemetry_boxes[j].addActionListener(this);
telemetry_boxes[j].setSelected((scanning_telemetry & (1 << k)) != 0);
}
y_offset_telem += (AltosLib.ao_telemetry_max - AltosLib.ao_telemetry_min + 1);
}
int y_offset = Math.max(y_offset_rate, y_offset_telem);
list = new JList<AltosScanResult>(results) {
//Subclass JList to workaround bug 4832765, which can cause the
//scroll pane to not let the user easily scroll up to the beginning
//of the list. An alternative would be to set the unitIncrement
//of the JScrollBar to a fixed value. You wouldn't get the nice
//aligned scrolling, but it should work.
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
int row;
if (orientation == SwingConstants.VERTICAL &&
direction < 0 && (row = getFirstVisibleIndex()) != -1) {
Rectangle r = getCellBounds(row, row);
if ((r.y == visibleRect.y) && (row != 0)) {
Point loc = r.getLocation();
loc.y--;
int prevIndex = locationToIndex(loc);
Rectangle prevR = getCellBounds(prevIndex, prevIndex);
if (prevR == null || prevR.y >= r.y) {
return 0;
}
return prevR.height;
}
}
return super.getScrollableUnitIncrement(
visibleRect, orientation, direction);
}
};
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
list.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
monitor_button.doClick(); //emulate button click
}
}
});
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(400, 80));
listScroller.setAlignmentX(LEFT_ALIGNMENT);
//Create a container so that we can add a title around
//the scroll pane. Can't add a title directly to the
//scroll pane because its background would be white.
//Lay out the label and scroll pane from top to bottom.
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
JLabel label = new JLabel("Select Device");
label.setLabelFor(list);
listPane.add(label);
listPane.add(Box.createRigidArea(new Dimension(0,5)));
listPane.add(listScroller);
listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
c.weightx = 1;
c.weighty = 1;
c.gridx = 0;
c.gridy = y_offset;
c.gridwidth = 2;
c.anchor = GridBagConstraints.CENTER;
pane.add(listPane, c);
cancel_button = new JButton("Cancel");
cancel_button.addActionListener(this);
cancel_button.setActionCommand("cancel");
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
c.weightx = 1;
c.weighty = 1;
c.gridx = 0;
c.gridy = y_offset + 1;
c.gridwidth = 1;
c.anchor = GridBagConstraints.CENTER;
pane.add(cancel_button, c);
monitor_button = new JButton("Monitor");
monitor_button.addActionListener(this);
monitor_button.setActionCommand("monitor");
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.insets = i;
c.weightx = 1;
c.weighty = 1;
c.gridx = 1;
c.gridy = y_offset + 1;
c.gridwidth = 1;
c.anchor = GridBagConstraints.CENTER;
pane.add(monitor_button, c);
pack();
setLocationRelativeTo(owner);
addWindowListener(new ConfigListener(this));
setVisible(true);
}
}

209
altosuilib/AltosSerial.java Normal file
View File

@@ -0,0 +1,209 @@
/*
* Copyright © 2010 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
/*
* Deal with TeleDongle on a serial port
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import libaltosJNI.*;
/*
* This class reads from the serial port and places each received
* line in a queue. Dealing with that queue is left up to other
* threads.
*/
public class AltosSerial extends AltosLink {
static java.util.List<String> devices_opened = Collections.synchronizedList(new LinkedList<String>());
public AltosDevice device;
SWIGTYPE_p_altos_file altos;
Thread input_thread;
String line;
byte[] line_bytes;
int line_count;
Frame frame;
public int getchar() {
if (altos == null)
return ERROR;
return libaltos.altos_getchar(altos, 0);
}
public void flush_output() {
super.flush_output();
if (altos != null) {
if (libaltos.altos_flush(altos) != 0)
close_serial();
}
}
JDialog timeout_dialog;
private void start_timeout_dialog_internal() {
Object[] options = { "Cancel" };
JOptionPane pane = new JOptionPane();
pane.setMessage(String.format("Connecting to %s, %7.3f MHz as %s", device.toShortString(), frequency, callsign));
pane.setOptions(options);
pane.setInitialValue(null);
timeout_dialog = pane.createDialog(frame, "Connecting...");
timeout_dialog.setVisible(true);
Object o = pane.getValue();
if (o == null)
return;
if (options[0].equals(o))
reply_abort = true;
timeout_dialog.dispose();
timeout_dialog = null;
}
/*
* These are required by the AltosLink implementation
*/
public boolean can_cancel_reply() {
/*
* Can cancel any replies not called from the dispatch thread
*/
return !SwingUtilities.isEventDispatchThread();
}
public boolean show_reply_timeout() {
if (!SwingUtilities.isEventDispatchThread() && frame != null) {
Runnable r = new Runnable() {
public void run() {
start_timeout_dialog_internal();
}
};
SwingUtilities.invokeLater(r);
return true;
}
return false;
}
public void hide_reply_timeout() {
Runnable r = new Runnable() {
public void run() {
timeout_dialog.setVisible(false);
}
};
SwingUtilities.invokeLater(r);
}
private synchronized void close_serial() {
synchronized (devices_opened) {
devices_opened.remove(device.getPath());
}
if (altos != null) {
libaltos.altos_free(altos);
altos = null;
}
abort_reply();
}
public void close() {
if (remote) {
try {
stop_remote();
} catch (InterruptedException ie) {
}
}
if (in_reply != 0)
System.out.printf("Uh-oh. Closing active serial device\n");
close_serial();
if (input_thread != null) {
try {
input_thread.interrupt();
input_thread.join();
} catch (InterruptedException ie) {
}
input_thread = null;
}
if (debug)
System.out.printf("Closing %s\n", device.getPath());
}
private void putc(char c) {
if (altos != null)
if (libaltos.altos_putchar(altos, c) != 0)
close_serial();
}
public void putchar(byte c) {
if (altos != null) {
if (debug)
System.out.printf(" %02x", (int) c & 0xff);
if (libaltos.altos_putchar(altos, (char) c) != 0)
close_serial();
}
}
public void print(String data) {
for (int i = 0; i < data.length(); i++)
putc(data.charAt(i));
}
private void open() throws FileNotFoundException, AltosSerialInUseException {
synchronized (devices_opened) {
if (devices_opened.contains(device.getPath()))
throw new AltosSerialInUseException(device);
devices_opened.add(device.getPath());
}
altos = device.open();
if (altos == null) {
final String message = device.getErrorString();
close();
throw new FileNotFoundException(String.format("%s (%s)",
device.toShortString(), message));
}
if (debug)
System.out.printf("Open %s\n", device.getPath());
input_thread = new Thread(this);
input_thread.start();
print("~\nE 0\n");
set_monitor(false);
flush_output();
}
public void set_frame(Frame in_frame) {
frame = in_frame;
}
public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException {
device = in_device;
frame = null;
serial = device.getSerial();
name = device.toShortString();
open();
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public class AltosSerialInUseException extends Exception {
public AltosDevice device;
public AltosSerialInUseException (AltosDevice in_device) {
device = in_device;
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright © 2017 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, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package org.altusmetrum.altosuilib_14;
public interface AltosShapeListener {
void set_shapes_visible(boolean visible);
void set_line_width(float width);
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright © 2017 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, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUIAccelCal
extends AltosUIDialog
implements AltosAccelCalListener, ActionListener
{
Frame owner;
AltosLink link;
AltosAccelCal cal;
AltosConfigValues config_values;
Thread thread;
Container pane;
JTextField message;
JButton antenna_up;
JButton antenna_down;
JButton ok;
JButton cancel;
boolean success;
int accel_plus, accel_minus;
private void make_visible() {
pack();
cal.start();
setVisible(true);
}
public boolean doit() {
success = false;
make_visible();
return success;
}
public int accel_cal_plus() {
if (success)
return accel_plus;
return AltosLib.MISSING;
}
public int accel_cal_minus() {
if (success)
return accel_minus;
return AltosLib.MISSING;
}
private void setDefaultButton(JButton button) {
this.getRootPane().setDefaultButton(button);
}
/* AltosAccelCalListener interface */
public void set_thread(AltosAccelCal cal, Thread thread) {
this.thread = thread;
}
public void set_phase(AltosAccelCal cal, final int phase) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
switch (phase) {
case AltosAccelCal.phase_antenna_up:
message.setText(String.format ("Orient antenna upwards and click on %s", up_msg()));
antenna_up.setEnabled(true);
setDefaultButton(antenna_up);
antenna_down.setEnabled(false);
ok.setEnabled(false);
break;
case AltosAccelCal.phase_antenna_down:
message.setText(String.format("Orient antenna downwards and click on %s", down_msg()));
antenna_up.setEnabled(false);
antenna_down.setEnabled(true);
setDefaultButton(antenna_down);
ok.setEnabled(false);
break;
}
}
});
}
public void cal_done(AltosAccelCal cal, int plus, int minus) {
accel_plus = plus;
accel_minus = minus;
success = true;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
message.setText(String.format("Calibration succeeded, plus %d minus %d, press OK to continue", accel_plus, accel_minus));
antenna_up.setEnabled(false);
antenna_down.setEnabled(false);
ok.setEnabled(true);
setDefaultButton(ok);
}
});
}
public void message(AltosAccelCal cal, final String msg) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
message.setText(msg);
}
});
}
public void error(AltosAccelCal cal, String msg) {
message(cal, msg);
}
/* ActionListener interface */
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if ("up".equals(cmd)) {
cal.signal(true);
antenna_up.setEnabled(false);
} else if ("down".equals(cmd)) {
cal.signal(true);
antenna_down.setEnabled(false);
this.setDefaultButton(antenna_down);
} else if ("ok".equals(cmd)) {
cal.signal(true);
this.setVisible(false);
if (success) {
config_values.set_accel_cal(accel_plus, accel_minus);
config_values.set_dirty();
}
try {
cal.abort();
} catch (InterruptedException ie) {
}
} else if ("cancel".equals(cmd)) {
cal.signal(false);
this.setVisible(false);
try {
cal.abort();
} catch (InterruptedException ie) {
}
}
}
public String up_msg() { return config_values.has_radio() ? "Antenna Up" : "Beeper Up"; }
public String down_msg() { return config_values.has_radio() ? "Antenna Down" : "Beeper Down"; }
public AltosUIAccelCal(Frame owner, AltosLink link, AltosConfigValues config_values) {
super(owner, "Calibrate Accelerometer", true);
this.owner = owner;
this.link = link;
this.config_values = config_values;
pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(4,4,4,4);
int x = 0;
int y = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridy = y;
c.gridwidth = 4;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
message = new JTextField(64);
pane.add(message, c);
y++; x = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridy = y;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
antenna_up = new JButton(up_msg());
antenna_up.setActionCommand("up");
antenna_up.setEnabled(false);
antenna_up.addActionListener(this);
pane.add(antenna_up, c);
x++;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridy = y;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
antenna_down = new JButton(down_msg());
antenna_down.setActionCommand("down");
antenna_down.setEnabled(false);
antenna_down.addActionListener(this);
pane.add(antenna_down, c);
x++;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridy = y;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
ok = new JButton("OK");
ok.setActionCommand("ok");
ok.setEnabled(false);
ok.addActionListener(this);
pane.add(ok, c);
x++;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridy = y;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
cancel = new JButton("Cancel");
cancel.setActionCommand("cancel");
cancel.setEnabled(true);
cancel.addActionListener(this);
pane.add(cancel, c);
cal = new AltosAccelCal(this.link, this);
}
}

103
altosuilib/AltosUIAxis.java Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosUIAxis extends NumberAxis {
String label;
AltosUnits units;
AltosUILineStyle line_style;
int ref;
int visible;
int index;
public final static int axis_integer = 1;
public final static int axis_include_zero = 2;
public final static int axis_default = axis_include_zero;
public void set_units() {
if (units != null) {
String u = units.parse_units();
if (u != null) {
setLabel(String.format("%s (%s)", label, u));
return;
}
}
setLabel(label);
}
public void set_enable(boolean enable) {
if (enable) {
visible++;
if (visible > ref)
System.out.printf("too many visible\n");
} else {
visible--;
if (visible < 0)
System.out.printf("too few visible\n");
}
setVisible(visible > 0);
if (enable)
autoAdjustRange();
}
public void ref(boolean enable) {
++ref;
if (enable) {
++visible;
setVisible(visible > 0);
}
}
public AltosUIAxis(String label, AltosUnits units, AltosUILineStyle line_style, int index, int flags) {
this.label = label;
this.units = units;
this.line_style = line_style;
this.index = index;
this.visible = 0;
this.ref = 0;
setLabelPaint(line_style.color);
setTickLabelPaint(line_style.color);
setVisible(false);
if ((flags & axis_integer) != 0)
setStandardTickUnits(NumberAxis.createIntegerTickUnits());
setAutoRangeIncludesZero((flags & axis_include_zero) != 0);
}
public AltosUIAxis(String label, AltosUnits units, AltosUILineStyle line_style, int index) {
this(label, units, line_style, index, axis_default);
}
}

View File

@@ -0,0 +1,308 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
import org.altusmetrum.altoslib_14.*;
class DelegatingRenderer implements ListCellRenderer<Object> {
// ...
public static void install(JComboBox<Object> comboBox) {
DelegatingRenderer renderer = new DelegatingRenderer(comboBox);
renderer.initialise();
comboBox.setRenderer(renderer);
}
// ...
private final JComboBox comboBox;
// ...
private ListCellRenderer<? super Object> delegate;
// ...
private DelegatingRenderer(JComboBox comboBox) {
this.comboBox = comboBox;
}
// ...
private void initialise() {
JComboBox<Object> c = new JComboBox<Object>();
delegate = c.getRenderer();
comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
delegate = new JComboBox<Object>().getRenderer();
}
});
}
// ...
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected, boolean cellHasFocus) {
return delegate.getListCellRendererComponent(list,
((UIManager.LookAndFeelInfo)value).getName(),
index, isSelected, cellHasFocus);
}
}
public class AltosUIConfigure
extends AltosUIDialog
{
public JFrame owner;
public Container pane;
public int row;
final static String[] font_size_names = { "Small", "Medium", "Large", "Huge" };
public GridBagConstraints constraints (int x, int width, int fill) {
GridBagConstraints c = new GridBagConstraints();
Insets insets = new Insets(4, 4, 4, 4);
c.insets = insets;
c.fill = fill;
if (width == 3)
c.anchor = GridBagConstraints.CENTER;
else if (x == 2)
c.anchor = GridBagConstraints.EAST;
else
c.anchor = GridBagConstraints.WEST;
c.gridx = x;
c.gridwidth = width;
c.gridy = row;
return c;
}
public GridBagConstraints constraints(int x, int width) {
return constraints(x, width, GridBagConstraints.NONE);
}
public void add_voice() {
}
public void add_log_dir() {
/* Log directory settings */
pane.add(new JLabel("Log Directory"), constraints(0, 1));
final JButton configure_log = new JButton(AltosUIPreferences.logdir().getPath());
configure_log.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
AltosUIPreferences.ConfigureLog();
configure_log.setText(AltosUIPreferences.logdir().getPath());
}
});
pane.add(configure_log, constraints(1, 2));
configure_log.setToolTipText("Which directory flight logs are stored in");
row++;
}
public void add_callsign() {
}
public void add_units() {
/* Imperial units setting */
pane.add(new JLabel("Imperial Units"), constraints(0, 1));
JRadioButton imperial_units = new JRadioButton("Enable", AltosUIPreferences.imperial_units());
imperial_units.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JRadioButton item = (JRadioButton) e.getSource();
boolean enabled = item.isSelected();
AltosUIPreferences.set_imperial_units(enabled);
}
});
imperial_units.setToolTipText("Use Imperial units instead of metric");
pane.add(imperial_units, constraints(1, 2));
row++;
}
public void add_font_size() {
/* Font size setting */
pane.add(new JLabel("Font size"), constraints(0, 1));
final JComboBox<String> font_size_value = new JComboBox<String>(font_size_names);
int font_size = AltosUIPreferences.font_size();
font_size_value.setSelectedIndex(font_size - AltosUILib.font_size_small);
font_size_value.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int size = font_size_value.getSelectedIndex() + AltosUILib.font_size_small;
AltosUIPreferences.set_font_size(size);
}
});
pane.add(font_size_value, constraints(1, 2, GridBagConstraints.BOTH));
font_size_value.setToolTipText("Font size used in telemetry window");
row++;
}
public void add_look_and_feel() {
/* Look & Feel setting */
pane.add(new JLabel("Look & feel"), constraints(0, 1));
/*
class LookAndFeelRenderer extends BasicComboBoxRenderer implements ListCellRenderer {
public LookAndFeelRenderer() {
super();
}
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
setText(((UIManager.LookAndFeelInfo) value).getName());
return this;
}
}
*/
final UIManager.LookAndFeelInfo[] look_and_feels = UIManager.getInstalledLookAndFeels();
final JComboBox<Object> look_and_feel_value = new JComboBox<Object>(look_and_feels);
DelegatingRenderer.install(look_and_feel_value);
String look_and_feel = AltosUIPreferences.look_and_feel();
for (int i = 0; i < look_and_feels.length; i++)
if (look_and_feel.equals(look_and_feels[i].getClassName()))
look_and_feel_value.setSelectedIndex(i);
look_and_feel_value.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int id = look_and_feel_value.getSelectedIndex();
AltosUIPreferences.set_look_and_feel(look_and_feels[id].getClassName());
}
});
pane.add(look_and_feel_value, constraints(1, 2, GridBagConstraints.BOTH));
look_and_feel_value.setToolTipText("Look&feel used for new windows");
row++;
}
public void add_position () {
}
public void add_serial_debug() {
/* Serial debug setting */
pane.add(new JLabel("Serial Debug"), constraints(0, 1));
JRadioButton serial_debug = new JRadioButton("Enable", AltosUIPreferences.serial_debug());
serial_debug.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JRadioButton item = (JRadioButton) e.getSource();
boolean enabled = item.isSelected();
AltosUIPreferences.set_serial_debug(enabled);
}
});
serial_debug.setToolTipText("Enable/Disable USB I/O getting sent to the console");
pane.add(serial_debug, constraints(1,2));
row++;
}
static final Integer map_caches[] = { 9, 25, 100 };
public void add_map_cache() {
pane.add(new JLabel("Map Cache Size"), constraints(0, 1));
final JComboBox<Integer> map_cache = new JComboBox<Integer>(map_caches);
map_cache.setEditable(true);
map_cache.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
int size = (Integer) (map_cache.getSelectedItem());
AltosUIPreferences.set_map_cache(size);
} catch (ClassCastException ce) {
map_cache.setSelectedItem(AltosUIPreferences.map_cache());
}
}
});
map_cache.setSelectedItem (AltosUIPreferences.map_cache());
pane.add(map_cache, constraints(1, 2, GridBagConstraints.BOTH));
row++;
}
public void add_bluetooth() {
}
public void add_frequencies() {
}
public AltosUIConfigure(JFrame in_owner, String name, String label) {
super(in_owner, name, false);
owner = in_owner;
pane = getScrollablePane();
pane.setLayout(new GridBagLayout());
row = 0;
/* Nice label at the top */
pane.add(new JLabel (label),
constraints(0, 3));
row++;
pane.add(new JLabel (String.format("AltOS version %s",
AltosVersion.version)),
constraints(0, 3));
row++;
add_voice();
add_log_dir();
add_callsign();
add_units();
add_serial_debug();
add_font_size();
add_look_and_feel();
add_position();
add_map_cache();
if (AltosUILib.has_bluetooth)
add_bluetooth();
add_frequencies();
/* And a close button at the bottom */
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
pane.add(close, constraints(0, 3));
pack();
setLocationRelativeTo(owner);
setVisible(true);
}
public AltosUIConfigure(JFrame in_owner) {
this(in_owner, "Configure AltosUI", "Configure AltOS UI");
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public class AltosUIDataMissing extends Exception {
public int id;
public AltosUIDataMissing(int id) {
this.id = id;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public interface AltosUIDataPoint {
public abstract double x() throws AltosUIDataMissing;
public abstract double y(int index) throws AltosUIDataMissing;
public abstract int id(int index) throws AltosUIDataMissing;
public abstract String id_name(int index) throws AltosUIDataMissing;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public interface AltosUIDataSet {
public abstract String name();
public abstract Iterable<AltosUIDataPoint> dataPoints();
}

View File

@@ -0,0 +1,84 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class AltosUIDialogListener extends WindowAdapter {
public void windowClosing (WindowEvent e) {
AltosUIPreferences.unregister_ui_listener((AltosUIDialog) e.getWindow());
}
}
public class AltosUIDialog extends JDialog implements AltosUIListener {
private Container scrollPane;
public Container getScrollablePane() {
if (scrollPane == null) {
Container content = super.getContentPane();
/* Create a container to hold the dialog contents */
scrollPane = new Container();
/* Make an opaque box to use the right color */
Box box = new Box(BoxLayout.X_AXIS);
box.add(scrollPane);
box.setOpaque(true);
/* Create a scrollpane to hold the box */
JScrollPane scroll = new JScrollPane();
JViewport view = scroll.getViewport();
view.add(box);
/* Add the scroll pane to the top level */
content.add(scroll);
}
return (Container) scrollPane;
}
public void ui_changed(String look_and_feel) {
SwingUtilities.updateComponentTreeUI(this);
this.pack();
}
public AltosUIDialog() {
AltosUIPreferences.register_ui_listener(this);
addWindowListener(new AltosUIDialogListener());
}
public AltosUIDialog(Frame frame, String label, boolean modal) {
super(frame, label, modal);
AltosUIPreferences.register_ui_listener(this);
addWindowListener(new AltosUIDialogListener());
}
public AltosUIDialog(Dialog dialog, String label, boolean modal) {
super(dialog, label, modal);
AltosUIPreferences.register_ui_listener(this);
addWindowListener(new AltosUIDialogListener());
}
public AltosUIDialog(Frame frame, boolean modal) {
super(frame, modal);
AltosUIPreferences.register_ui_listener(this);
addWindowListener(new AltosUIDialogListener());
}
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosUIEnable extends Container implements ChangeListener {
Insets il, ir;
int y;
int x;
JCheckBox imperial_units;
JCheckBox show_shapes;
JLabel line_width_label;
JSpinner line_width;
JLabel speed_filter_label;
JSlider speed_filter;
JLabel accel_filter_label;
JSlider accel_filter;
AltosFilterListener filter_listener;
AltosShapeListener shape_listener;
static final int max_rows = 14;
public void units_changed(boolean imperial_units) {
if (this.imperial_units != null) {
this.imperial_units.setSelected(imperial_units);
}
}
class GraphElement implements ActionListener {
AltosUIGrapher grapher;
JCheckBox enable;
String name;
public void actionPerformed(ActionEvent ae) {
grapher.set_enable(enable.isSelected());
}
GraphElement (String name, AltosUIGrapher grapher, boolean enabled) {
this.name = name;
this.grapher = grapher;
enable = new JCheckBox(name, enabled);
grapher.set_enable(enabled);
enable.addActionListener(this);
}
}
LinkedList<GraphElement> elements = new LinkedList<GraphElement>();
public void add(String name, AltosUIGrapher grapher, boolean enabled) {
GraphElement e = new GraphElement(name, grapher, enabled);
GridBagConstraints c = new GridBagConstraints();
elements.add(e);
/* Add element */
c = new GridBagConstraints();
c.gridx = x; c.gridy = y;
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.CENTER;
c.insets = ir;
add(e.enable, c);
/* Next row */
y++;
if (y == max_rows) {
x++;
y = 0;
}
}
public void stateChanged(ChangeEvent e) {
JSlider filter = (JSlider) e.getSource();
if (!filter.getValueIsAdjusting()) {
double speed_value = (int) speed_filter.getValue() / 1000.0;
double accel_value = (int) accel_filter.getValue() / 1000.0;
if (filter_listener != null) {
filter_listener.filter_changed(speed_value, accel_value);
}
}
}
public void set_shapes_visible(boolean visible) {
if (shape_listener != null)
shape_listener.set_shapes_visible(visible);
}
public void set_line_width(float width) {
if (shape_listener != null)
shape_listener.set_line_width(width);
}
public void register_shape_listener(AltosShapeListener shape_listener) {
this.shape_listener = shape_listener;
}
public void add_units() {
/* Imperial units setting */
/* Add label */
imperial_units = new JCheckBox("Imperial Units", AltosUIPreferences.imperial_units());
imperial_units.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox item = (JCheckBox) e.getSource();
boolean enabled = item.isSelected();
AltosUIPreferences.set_imperial_units(enabled);
}
});
imperial_units.setToolTipText("Use Imperial units instead of metric");
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0; c.gridy = 1000;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(imperial_units, c);
show_shapes = new JCheckBox("Show Markers", false);
show_shapes.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox item = (JCheckBox) e.getSource();
boolean enabled = item.isSelected();
set_shapes_visible(enabled);
}
});
show_shapes.setToolTipText("Show marker Use Imperial units instead of metric");
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 1001;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(show_shapes, c);
line_width_label = new JLabel("Line Width");
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 1001;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(line_width_label, c);
line_width = new JSpinner();
line_width.setValue(1);
line_width.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
int w = (Integer) line_width.getValue();
if (w < 1) {
w = 1;
line_width.setValue(w);
}
set_line_width(w);
}
});
c = new GridBagConstraints();
c.gridx = 2; c.gridy = 1001;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(line_width, c);
speed_filter_label = new JLabel("Speed Filter(ms)");
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 1002;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(speed_filter_label, c);
speed_filter = new JSlider(JSlider.HORIZONTAL, 0, 10000, (int) (filter_listener.speed_filter() * 1000.0));
Hashtable<Integer,JLabel> label_table = new Hashtable<Integer,JLabel>();
for (int i = 0; i <= 10000; i += 5000) {
label_table.put(i, new JLabel(String.format("%d", i)));
}
speed_filter.setPaintTicks(true);
speed_filter.setMajorTickSpacing(1000);
speed_filter.setMinorTickSpacing(250);
speed_filter.setLabelTable(label_table);
speed_filter.setPaintTrack(false);
speed_filter.setSnapToTicks(true);
speed_filter.setPaintLabels(true);
speed_filter.addChangeListener(this);
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 1002;
c.gridwidth = 1000; c.gridheight = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(speed_filter, c);
accel_filter_label = new JLabel("Acceleration Filter(ms)");
c = new GridBagConstraints();
c.gridx = 0; c.gridy = 1003;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(accel_filter_label, c);
accel_filter = new JSlider(JSlider.HORIZONTAL, 0, 10000, (int) (filter_listener.accel_filter() * 1000.0));
accel_filter.setPaintTicks(true);
accel_filter.setMajorTickSpacing(1000);
accel_filter.setMinorTickSpacing(250);
accel_filter.setLabelTable(label_table);
accel_filter.setPaintTrack(false);
accel_filter.setSnapToTicks(true);
accel_filter.setPaintLabels(true);
accel_filter.addChangeListener(this);
c = new GridBagConstraints();
c.gridx = 1; c.gridy = 1003;
c.gridwidth = 1000; c.gridheight = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.LINE_START;
c.insets = il;
add(accel_filter, c);
}
public AltosUIEnable(AltosFilterListener filter_listener) {
this.filter_listener = filter_listener;
il = new Insets(4,4,4,4);
ir = new Insets(4,4,4,4);
x = 0;
y = 0;
setLayout(new GridBagLayout());
add_units();
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright © 2017 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, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
class AltosUITimeSeriesAxis {
AltosUILineStyle line_style;
boolean enabled;
boolean marker;
boolean marker_top;
AltosUIAxis axis;
XYPlot plot;
public AltosUITimeSeriesAxis(AltosUILineStyle line_style, boolean enabled,
AltosUIAxis axis, XYPlot plot, boolean marker, boolean marker_top) {
this.line_style = line_style;
this.enabled = enabled;
this.axis = axis;
this.plot = plot;
this.marker = marker;
this.marker_top = marker_top;
}
}
public class AltosUIFlightSeries extends AltosFlightSeries {
Hashtable<String,AltosUITimeSeriesAxis> axes;
void fill_axes(String label, AltosUITimeSeriesAxis axis) {
for (AltosTimeSeries ts : series) {
AltosUITimeSeries uts = (AltosUITimeSeries) ts;
if (label.equals(ts.label) || (label.equals("default") && uts.line_style == null)) {
uts.custom_axis_set = true;
if (axis.marker)
uts.set_marker(axis.line_style, axis.enabled, axis.plot, axis.marker_top);
else
uts.set_axis(axis.line_style, axis.enabled, axis.axis);
}
}
}
void check_axes() {
for (AltosTimeSeries ts : series) {
AltosUITimeSeries uts = (AltosUITimeSeries) ts;
if (!uts.custom_axis_set)
System.out.printf("%s using default axis\n", ts.label);
}
}
public void register_axis(String label,
AltosUILineStyle line_style,
boolean enabled,
AltosUIAxis axis) {
AltosUITimeSeriesAxis tsa = new AltosUITimeSeriesAxis(line_style,
enabled,
axis,
null,
false,
false);
axes.put(label, tsa);
fill_axes(label, tsa);
}
public void register_marker(String label,
AltosUILineStyle line_style,
boolean enabled,
XYPlot plot,
boolean marker_top) {
AltosUITimeSeriesAxis tsa = new AltosUITimeSeriesAxis(line_style,
enabled,
null,
plot,
true,
marker_top);
axes.put(label, tsa);
fill_axes(label, tsa);
}
public AltosTimeSeries make_series(String label, AltosUnits units) {
AltosUITimeSeries time_series = new AltosUITimeSeries(label, units);
AltosUITimeSeriesAxis tsa = axes.get(label);
if (tsa == null)
tsa = axes.get("default");
else
time_series.custom_axis_set = true;
if (tsa != null) {
if (tsa.marker)
time_series.set_marker(tsa.line_style, tsa.enabled, tsa.plot, tsa.marker_top);
else
time_series.set_axis(tsa.line_style, tsa.enabled, tsa.axis);
}
return time_series;
}
public AltosUITimeSeries[] series(AltosCalData cal_data) {
finish();
return series.toArray(new AltosUITimeSeries[0]);
}
public AltosUIFlightSeries (AltosCalData cal_data) {
super(cal_data);
axes = new Hashtable<String,AltosUITimeSeriesAxis>();
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright © 2014 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public abstract class AltosUIFlightTab extends JComponent implements AltosFlightDisplay, HierarchyListener {
public GridBagLayout layout;
AltosState last_state;
AltosListenerState last_listener_state;
LinkedList<AltosUIIndicator> indicators = new LinkedList<AltosUIIndicator>();
public void add (AltosUIIndicator indicator) {
indicators.add(indicator);
}
public void remove(AltosUIIndicator indicator) {
indicators.remove(indicator);
}
public void reset() {
for (AltosUIIndicator i : indicators)
i.reset();
}
public void font_size_changed(int font_size) {
for (AltosUIIndicator i : indicators)
i.font_size_changed(font_size);
}
public void units_changed(boolean imperial_units) {
for (AltosUIIndicator i : indicators)
i.units_changed(imperial_units);
}
public void show(AltosState state, AltosListenerState listener_state) {
if (!isShowing()) {
last_state = state;
last_listener_state = listener_state;
return;
}
for (AltosUIIndicator i : indicators)
i.show(state, listener_state);
}
public void hierarchyChanged(HierarchyEvent e) {
if (last_state != null && isShowing()) {
AltosState state = last_state;
AltosListenerState listener_state = last_listener_state;
last_state = null;
last_listener_state = null;
show(state, listener_state);
}
}
abstract public String getName();
public AltosUIFlightTab() {
layout = new GridBagLayout();
setLayout(layout);
addHierarchyListener(this);
}
}

View File

@@ -0,0 +1,336 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
class AltosUIFrameListener extends WindowAdapter {
@Override
public void windowClosing (WindowEvent e) {
AltosUIFrame frame = (AltosUIFrame) e.getWindow();
AltosUIPreferences.unregister_ui_listener(frame);
AltosUIFrame.frame_closed();
frame.setVisible(false);
frame.dispose();
}
}
public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositionListener {
public void ui_changed(String look_and_feel) {
SwingUtilities.updateComponentTreeUI(this);
this.pack();
}
private Container scrollPane;
public Container getScrollablePane() {
if (scrollPane == null) {
Container content = super.getContentPane();
/* Create a container to hold the dialog contents */
scrollPane = new Container();
/* Make an opaque box to use the right color */
Box box = new Box(BoxLayout.X_AXIS);
box.add(scrollPane);
box.setOpaque(true);
/* Create a scrollpane to hold the box */
JScrollPane scroll = new JScrollPane();
JViewport view = scroll.getViewport();
view.add(box);
/* Add the scroll pane to the top level */
content.add(scroll);
}
return (Container) scrollPane;
}
static String[] altos_icon_names = {
"/altusmetrum-altosui-16.png",
"/altusmetrum-altosui-32.png",
"/altusmetrum-altosui-48.png",
"/altusmetrum-altosui-64.png",
"/altusmetrum-altosui-128.png",
"/altusmetrum-altosui-256.png"
};
static public String[] icon_names;
static public void set_icon_names(String[] new_icon_names) { icon_names = new_icon_names; }
public String[] icon_names() {
if (icon_names == null)
set_icon_names(altos_icon_names);
return icon_names;
}
public void set_icon() {
ArrayList<Image> icons = new ArrayList<Image>();
String[] icon_names = icon_names();
for (int i = 0; i < icon_names.length; i++) {
java.net.URL imgURL = AltosUIFrame.class.getResource(icon_names[i]);
if (imgURL != null)
icons.add(new ImageIcon(imgURL).getImage());
}
setIconImages(icons);
}
private boolean location_by_platform = true;
public void setLocationByPlatform(boolean lbp) {
location_by_platform = lbp;
super.setLocationByPlatform(lbp);
}
public void scan_device_selected(AltosDevice device) {
}
public void setSize() {
/* Smash sizes around so that the window comes up in the right shape */
Insets i = getInsets();
Dimension ps = rootPane.getPreferredSize();
ps.width += i.left + i.right;
ps.height += i.top + i.bottom;
setPreferredSize(ps);
setSize(ps);
}
public void setPosition (int position) {
Insets i = getInsets();
Dimension ps = getSize();
/* Stick the window in the desired location on the screen */
setLocationByPlatform(false);
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
Rectangle r = gc.getBounds();
/* compute X position */
int x = 0;
int y = 0;
switch (position) {
case AltosUILib.position_top_left:
case AltosUILib.position_left:
case AltosUILib.position_bottom_left:
x = 0;
break;
case AltosUILib.position_top:
case AltosUILib.position_center:
case AltosUILib.position_bottom:
x = (r.width - ps.width) / 2;
break;
case AltosUILib.position_top_right:
case AltosUILib.position_right:
case AltosUILib.position_bottom_right:
x = r.width - ps.width + i.right;
break;
}
/* compute Y position */
switch (position) {
case AltosUILib.position_top_left:
case AltosUILib.position_top:
case AltosUILib.position_top_right:
y = 0;
break;
case AltosUILib.position_left:
case AltosUILib.position_center:
case AltosUILib.position_right:
y = (r.height - ps.height) / 2;
break;
case AltosUILib.position_bottom_left:
case AltosUILib.position_bottom:
case AltosUILib.position_bottom_right:
y = r.height - ps.height + i.bottom;
break;
}
setLocation(x, y);
}
int position;
public void position_changed(int position) {
this.position = position;
if (!location_by_platform)
setPosition(position);
}
public void setVisible (boolean visible) {
if (visible)
setLocationByPlatform(location_by_platform);
super.setVisible(visible);
if (visible) {
setSize();
if (!location_by_platform)
setPosition(position);
}
}
static boolean global_settings_done;
public String getName() {
return "Altus Metrum";
}
public void macosx_quit_handler() {
System.out.printf("Got quit handler\n");
}
public void macosx_about_handler() {
System.out.printf("Got about handler\n");
}
public void macosx_preferences_handler() {
System.out.printf("Got preferences handler\n");
}
public void macosx_file_handler(String path) {
System.out.printf("Got file handler with \"%s\"\n", path);
}
/* Check that we are on Mac OS X. This is crucial to loading and using the OSXAdapter class.
*/
public static boolean MAC_OS_X = (System.getProperty("os.name").toLowerCase().startsWith("mac os x"));
private static boolean registered_for_macosx_events;
/* Generic registration with the Mac OS X application menu
* Checks the platform, then attempts to register with the Apple EAWT
* See OSXAdapter.java to see how this is done without directly referencing any Apple APIs
*/
public synchronized void register_for_macosx_events() {
if (registered_for_macosx_events)
return;
registered_for_macosx_events = true;
if (MAC_OS_X) {
try {
// Generate and register the OSXAdapter, passing it a hash of all the methods we wish to
// use as delegates for various com.apple.eawt.ApplicationListener methods
OSXAdapter.setQuitHandler(this, getClass().getDeclaredMethod("macosx_quit_handler", (Class[])null));
// OSXAdapter.setAboutHandler(this, getClass().getDeclaredMethod("macosx_about_handler", (Class[])null));
OSXAdapter.setPreferencesHandler(this, getClass().getDeclaredMethod("macosx_preferences_handler", (Class[])null));
OSXAdapter.setFileHandler(this, getClass().getDeclaredMethod("macosx_file_handler", new Class[] { String.class }));
} catch (Exception e) {
System.err.println("Error while loading the OSXAdapter:");
e.printStackTrace();
}
}
}
int row = 0;
public void next_row() {
row++;
}
int inset = 0;
public void set_inset(int i) {
inset = i;
}
public GridBagConstraints constraints (int x, int width, int fill, int anchor, double weightx, double weighty) {
return new GridBagConstraints(x, /* x */
row, /* y */
width, /* width */
1, /* height */
weightx, /* weightx */
weighty, /* weighty */
anchor, /* anchor */
fill, /* fill */
new Insets(inset,inset,inset,inset), /* insets */
0, /* ipadx */
0); /* ipady */
}
public GridBagConstraints constraints (int x, int width, int fill, int anchor) {
double weightx = 0;
double weighty = 0;
if (fill == GridBagConstraints.NONE) {
weightx = 0;
weighty = 0;
} else if (fill == GridBagConstraints.HORIZONTAL) {
weightx = 1;
weighty = 0;
} else if (fill == GridBagConstraints.VERTICAL) {
weightx = 0;
weighty = 1;
} else if (fill == GridBagConstraints.BOTH) {
weightx = 1;
weighty = 1;
}
return constraints (x, width, fill, anchor, weightx, weighty);
}
public GridBagConstraints constraints (int x, int width, int fill) {
return constraints (x, width, fill, GridBagConstraints.WEST);
}
public GridBagConstraints constraints(int x, int width) {
return constraints(x, width, GridBagConstraints.NONE);
}
static int open_frames;
public static void frame_opened() {
++open_frames;
}
public static void frame_closed() {
--open_frames;
if (open_frames == 0)
System.exit(0);
}
void init() {
AltosUIPreferences.register_ui_listener(this);
AltosUIPreferences.register_position_listener(this);
position = AltosUIPreferences.position();
frame_opened();
addWindowListener(new AltosUIFrameListener());
/* Try to make menus live in the menu bar like regular Mac apps */
if (!global_settings_done) {
try {
global_settings_done = true;
System.setProperty("com.apple.mrj.application.apple.menu.about.name", getName());
System.setProperty("com.apple.macos.useScreenMenuBar", "true");
System.setProperty("apple.laf.useScreenMenuBar", "true" ); // for older versions of Java
} catch (Exception e) {
}
}
set_icon();
}
public AltosUIFrame() {
init();
}
public AltosUIFrame(String name) {
super(name);
init();
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUIFreqList 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());
setVisible(true);
}
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 AltosUIFreqList () {
super(AltosUIPreferences.common_frequencies());
setMaximumRowCount(getItemCount());
setEditable(false);
product = "Unknown";
serial = 0;
}
public AltosUIFreqList(double in_frequency) {
this();
set_frequency(in_frequency);
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright © 2012 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosUIGraph implements AltosUnitsListener, AltosShapeListener {
XYPlot plot;
JFreeChart chart;
public ChartPanel panel;
NumberAxis xAxis;
AltosUIEnable enable;
AltosUITimeSeries[] series;
int axis_index;
int series_index;
Hashtable<Integer,Boolean> axes_added;
static final private Color gridline_color = new Color(0, 0, 0);
static final private Color border_color = new Color(255, 255, 255);
static final private Color background_color = new Color(255, 255, 255);
public JPanel panel() {
return panel;
}
public AltosUIAxis newAxis(String label, AltosUnits units, AltosUILineStyle line_style, int flags) {
AltosUIAxis axis = new AltosUIAxis(label, units, line_style, axis_index++, flags);
plot.setRangeAxis(axis.index, axis);
return axis;
}
public AltosUIAxis newAxis(String label, AltosUnits units, AltosUILineStyle line_style) {
return newAxis(label, units, line_style, AltosUIAxis.axis_default);
}
void addAxis(AltosUIAxis axis) {
if (!axes_added.containsKey(axis.index)) {
axes_added.put(axis.index, true);
plot.setRangeAxis(axis.index, axis);
}
}
public void addSeries(AltosUITimeSeries series) {
XYSeriesCollection dataset = new XYSeriesCollection(series.xy_series());
addAxis(series.axis);
series.renderer.setPlot(plot);
plot.setDataset(series_index, dataset);
plot.setRenderer(series_index, series.renderer);
plot.mapDatasetToRangeAxis(series_index, series.axis.index);
if (enable != null)
enable.add(series.label, series, series.enable);
series_index++;
}
public void addMarker(AltosUITimeSeries series) {
}
public void units_changed(boolean imperial_units) {
for (AltosUITimeSeries s : series)
s.set_units();
}
public void filter_changed() {
units_changed(false);
}
public void set_shapes_visible(boolean visible) {
for (AltosUITimeSeries s : series)
s.set_shapes_visible(visible);
}
public void set_line_width(float width) {
for (AltosUITimeSeries s : series)
s.set_line_width(width);
}
public void setName (String name) {
chart.setTitle(name);
}
public void set_series(AltosUITimeSeries[] series) {
this.series = series;
boolean any_enabled = false;
for (AltosUITimeSeries s : series)
if (s.enable)
any_enabled = true;
if (!any_enabled)
for (AltosUITimeSeries s : series)
s.set_enable(true);
for (AltosUITimeSeries s : series)
addSeries(s);
units_changed(false);
}
public AltosUIGraph(AltosUIEnable enable, String title) {
this.enable = enable;
this.series = null;
this.axis_index = 0;
if (enable != null)
enable.register_shape_listener(this);
axes_added = new Hashtable<Integer,Boolean>();
xAxis = new NumberAxis("Time (s)");
xAxis.setAutoRangeIncludesZero(true);
plot = new XYPlot();
plot.setDomainAxis(xAxis);
plot.setOrientation(PlotOrientation.VERTICAL);
plot.setDomainPannable(true);
plot.setRangePannable(true);
chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
plot, true);
ChartUtilities.applyCurrentTheme(chart);
plot.setDomainGridlinePaint(gridline_color);
plot.setRangeGridlinePaint(gridline_color);
plot.setBackgroundPaint(background_color);
plot.setBackgroundAlpha((float) 1);
chart.setBackgroundPaint(background_color);
chart.setBorderPaint(border_color);
panel = new ChartPanel(chart);
panel.setMouseWheelEnabled(true);
panel.setPreferredSize(new java.awt.Dimension(800, 500));
AltosPreferences.register_units_listener(this);
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
interface AltosUIGrapher {
public abstract void set_units();
public abstract boolean need_reset();
public abstract void clear();
public abstract void add(AltosUIDataPoint dataPoint);
public abstract void set_enable(boolean enable);
public abstract void setNotify(boolean notify);
public abstract void fireSeriesChanged();
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright © 2015 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altoslib_14;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.awt.image.*;
import java.awt.*;
import java.io.*;
import java.net.*;
public class AltosUIImage implements AltosImage {
public Image image;
/* Discard storage for image */
public void flush() {
image.flush();
}
public AltosUIImage(Image image) {
this.image = image;
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright © 2014 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public abstract class AltosUIIndicator implements AltosFontListener, AltosUnitsListener {
JLabel label;
JTextField[] values;
AltosLights lights;
int number_values;
boolean has_lights;
int value_width;
abstract public void show(AltosState state, AltosListenerState listener_state);
public void set_lights(boolean on) {
lights.set(on);
}
public void setVisible(boolean visible) {
if (lights != null)
lights.setVisible(visible);
label.setVisible(visible);
for (int i = 0; i < values.length; i++)
values[i].setVisible(visible);
}
public void reset() {
for (int i = 0; i < values.length; i++)
values[i].setText("");
if (lights != null)
lights.set(false);
}
public void show() {
if (lights != null)
lights.setVisible(true);
label.setVisible(true);
for (int i = 0; i < values.length; i++)
values[i].setVisible(true);
}
public void show(String... s) {
int n = Math.min(s.length, values.length);
show();
for (int i = 0; i < n; i++)
values[i].setText(s[i]);
}
public void show(String format, double value) {
show(String.format(format, value));
}
public void show(String format, int value) {
show(String.format(format, value));
}
public void show(String format1, double value1, String format2, double value2) {
show(String.format(format1, value1), String.format(format2, value2));
}
public void show(String format1, int value1, String format2, int value2) {
show(String.format(format1, value1), String.format(format2, value2));
}
public void hide() {
if (lights != null)
lights.setVisible(false);
label.setVisible(false);
for (int i = 0; i < values.length; i++)
values[i].setVisible(false);
}
public void font_size_changed(int font_size) {
label.setFont(AltosUILib.label_font);
for (int i = 0; i < values.length; i++)
values[i].setFont(AltosUILib.value_font);
}
public void units_changed(boolean imperial_units) {
}
public void set_label(String text) {
label.setText(text);
}
public void remove(Container container) {
if (lights != null)
container.remove(lights);
container.remove(label);
for (int i = 0; i < values.length; i++)
container.remove(values[i]);
}
public AltosUIIndicator (Container container, int x, int y, int label_width, String text, int number_values, boolean has_lights, int value_width, int value_space) {
GridBagLayout layout = (GridBagLayout)(container.getLayout());
GridBagConstraints c = new GridBagConstraints();
c.weighty = 1;
if (has_lights) {
lights = new AltosLights();
c.gridx = x; c.gridy = y;
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0;
layout.setConstraints(lights, c);
container.add(lights);
}
label = new JLabel(text);
label.setFont(AltosUILib.label_font);
label.setHorizontalAlignment(SwingConstants.LEFT);
c.gridx = x + 1; c.gridy = y;
c.gridwidth = label_width;
c.insets = new Insets(AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad, AltosUILib.tab_elt_pad);
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0;
layout.setConstraints(label, c);
container.add(label);
values = new JTextField[number_values];
for (int i = 0; i < values.length; i++) {
values[i] = new JTextField(AltosUILib.text_width);
values[i].setEditable(false);
values[i].setFont(AltosUILib.value_font);
values[i].setHorizontalAlignment(SwingConstants.RIGHT);
c.gridx = 1 + label_width + x + i * value_space; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.gridwidth = value_width;
layout.setConstraints(values[i], c);
container.add(values[i]);
}
}
public AltosUIIndicator (Container container, int x, int y, int label_width, String text, int number_values, boolean has_lights, int value_width) {
this(container, x, y, label_width, text, number_values, has_lights, value_width, 1);
}
public AltosUIIndicator (Container container, int x, int y, String text, int number_values, boolean has_lights, int value_width) {
this(container, x, y, 1, text, number_values, has_lights, value_width);
}
public AltosUIIndicator (Container container, int y, String text, int number_values, boolean has_lights, int value_width) {
this(container, 0, y, text, number_values, has_lights, value_width);
}
public AltosUIIndicator (Container container, int y, String text, int number_values, boolean has_lights) {
this(container, 0, y, text, number_values, has_lights, 1);
}
public AltosUIIndicator (Container container, int y, String text, int number_values) {
this(container, 0, y, text, number_values, false, 1);
}
public AltosUIIndicator (Container container, int y, String text) {
this(container, 0, y, text, 1, false, 1);
}
}

127
altosuilib/AltosUILib.java Normal file
View File

@@ -0,0 +1,127 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import libaltosJNI.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUILib extends AltosLib {
public static final int tab_elt_pad = 5;
public static Font label_font;
public static Font value_font;
public static Font status_font;
public static Font table_label_font;
public static Font table_value_font;
final public static int font_size_small = 1;
final public static int font_size_medium = 2;
final public static int font_size_large = 3;
final public static int font_size_huge = 4;
final public static int position_top_left = 0;
final public static int position_top = 1;
final public static int position_top_right = 2;
final public static int position_left = 3;
final public static int position_center = 4;
final public static int position_right = 5;
final public static int position_bottom_left = 6;
final public static int position_bottom = 7;
final public static int position_bottom_right = 8;
public static void set_fonts(int size) {
int brief_size;
int table_size;
int status_size;
switch (size) {
case font_size_small:
brief_size = 16;
status_size = 18;
table_size = 11;
break;
default:
case font_size_medium:
brief_size = 22;
status_size = 24;
table_size = 14;
break;
case font_size_large:
brief_size = 26;
status_size = 30;
table_size = 17;
break;
case font_size_huge:
brief_size = 30;
status_size = 36;
table_size = 24;
break;
}
label_font = new Font("Dialog", Font.PLAIN, brief_size);
value_font = new Font("Monospaced", Font.PLAIN, brief_size);
status_font = new Font("SansSerif", Font.BOLD, status_size);
table_label_font = new Font("SansSerif", Font.PLAIN, table_size);
table_value_font = new Font("Monospaced", Font.PLAIN, table_size);
}
static public final int text_width = 20;
static public boolean initialized = false;
static public boolean loaded_library = false;
static public boolean has_bluetooth = false;
static final String[] library_names = {
"altos",
"altos32",
"altos64",
"altos_i686",
"altos_amd64",
"altos_aarch64",
"altos_armel",
"altos_armhf"
};
public static boolean load_library() {
if (!initialized) {
for (String name : library_names) {
try {
System.loadLibrary(name);
libaltos.altos_init();
loaded_library = true;
break;
} catch (UnsatisfiedLinkError e) {
loaded_library = false;
}
}
if (!loaded_library)
System.out.printf("Cannot find 'libaltos' device access library\n");
String OS = System.getProperty("os.name");
if (OS.startsWith("L") || OS.startsWith("W"))
has_bluetooth = true;
initialized = true;
}
return loaded_library;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright © 2017 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, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosUILineStyle {
public Color color;
public float[] dash;
static private Color color(int r, int g, int b) {
return new Color(r,g,b);
}
static final private Color[] colors = {
new Color(0,0,0),
new Color(230,0,0), // red
new Color(216,103,0), // orange
new Color(200,200,0), // yellow
new Color(0,180,0), // green
new Color(0,140,140), // cyan
new Color(130,0,0), // dark red
new Color(0,100,0), // dark green
new Color(0,0,255), // blue
new Color(140,0,140), // magenta
new Color(150,150,150), // gray
};
static final private float[][] dashes = {
{ 0 },
{ 2, 4 },
{ 4, 4 },
{ 6, 4 },
{ 6, 4, 2, 4 }
};
static int color_index, dash_index;
public AltosUILineStyle () {
color = colors[color_index];
dash = dashes[dash_index];
color_index = (color_index + 1) % colors.length;
if (color_index == 0) {
dash_index = (dash_index + 1) % dashes.length;
if (dash_index == 0)
System.out.printf("too many line styles\n");
}
}
public AltosUILineStyle(int index) {
index = index % (colors.length * dashes.length);
int c = index % colors.length;
int d = index / colors.length;
color = colors[c];
dash = dashes[d];
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
public interface AltosUIListener {
public void ui_changed(String look_and_feel);
}

675
altosuilib/AltosUIMap.java Normal file
View File

@@ -0,0 +1,675 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.io.*;
import java.lang.Math;
import java.awt.geom.*;
import java.util.*;
import java.util.concurrent.*;
import javax.imageio.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosMapInterface {
AltosMap map;
Graphics2D g;
Font tile_font;
Font line_font;
AltosMapMark nearest_mark;
static Point2D.Double point2d(AltosPointDouble pt) {
return new Point2D.Double(pt.x, pt.y);
}
static final AltosPointDouble point_double(Point pt) {
return new AltosPointDouble(pt.x, pt.y);
}
class MapMark extends AltosMapMark {
public void paint(AltosMapTransform t) {
double lat = lat_lon.lat;
double lon;
double first_lon = t.first_lon(lat_lon.lon);
double last_lon = t.last_lon(lat_lon.lon);
for (lon = first_lon; lon <= last_lon; lon += 360.0) {
AltosPointDouble pt = t.screen(lat, lon);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
if (0 <= state && state < AltosUIMap.stateColors.length)
g.setColor(AltosUIMap.stateColors[state]);
else
g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
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);
if (label != null) {
Rectangle2D bounds;
bounds = line_font.getStringBounds(label, g.getFontRenderContext());
float x = (float) pt.x;
float y = (float) pt.y + (float) bounds.getHeight() / 2.0f;
g.setFont(line_font);
g.setColor(Color.WHITE);
for (int dy = -2; dy <= 2; dy += 2)
for (int dx = -2; dx <= 2; dx += 2)
g.drawString(label, x + dx, y + dy);
if (0 <= state && state < AltosUIMap.stateColors.length)
g.setColor(AltosUIMap.stateColors[state]);
else
g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
g.drawString(label, x, y);
}
}
}
MapMark(double lat, double lon, int state, String label) {
super(lat, lon, state, label);
}
MapMark(double lat, double lon, int state) {
super(lat, lon, state);
}
}
class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener {
private VolatileImage create_back_buffer() {
return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
}
private void do_paint(Graphics my_g) {
g = (Graphics2D) my_g;
map.paint();
}
public void paint(Graphics my_g) {
VolatileImage back_buffer = create_back_buffer();
Graphics2D top_g = (Graphics2D) my_g;
do {
GraphicsConfiguration gc = getGraphicsConfiguration();
int code = back_buffer.validate(gc);
if (code == VolatileImage.IMAGE_INCOMPATIBLE)
back_buffer = create_back_buffer();
Graphics g_back = back_buffer.getGraphics();
g_back.setClip(top_g.getClip());
do_paint(g_back);
g_back.dispose();
top_g.drawImage(back_buffer, 0, 0, this);
} while (back_buffer.contentsLost());
back_buffer.flush();
}
public void repaint(AltosRectangle damage) {
repaint(damage.x, damage.y, damage.width, damage.height);
}
private boolean is_drag_event(MouseEvent e) {
return e.getModifiersEx() == InputEvent.BUTTON1_DOWN_MASK;
}
/* MouseMotionListener methods */
public void mouseDragged(MouseEvent e) {
map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e));
}
String pos(double p, String pos, String neg) {
if (p == AltosLib.MISSING)
return "";
String h = pos;
if (p < 0) {
h = neg;
p = -p;
}
int deg = (int) Math.floor(p);
double min = (p - Math.floor(p)) * 60.0;
return String.format("%s %4d° %9.6f'", h, deg, min);
}
String height(double h, String label) {
if (h == AltosLib.MISSING)
return "";
return String.format(" %s%s",
AltosConvert.height.show(6, h),
label);
}
String speed(double s, String label) {
if (s == AltosLib.MISSING)
return "";
return String.format(" %s%s",
AltosConvert.speed.show(6, s),
label);
}
public void mouseMoved(MouseEvent e) {
AltosMapPathPoint point = map.nearest(e.getPoint().x, e.getPoint().y);
if (point != null) {
if (nearest_mark == null)
nearest_mark = map.add_mark(point.gps.lat,
point.gps.lon,
point.state);
else {
nearest_mark.lat_lon.lat = point.gps.lat;
nearest_mark.lat_lon.lon = point.gps.lon;
nearest_mark.state = point.state;
}
nearest_label.setText(String.format("%9.2f sec %s%s%s%s",
point.time,
pos(point.gps.lat,
"N", "S"),
pos(point.gps.lon,
"E", "W"),
height(point.gps_height, ""),
speed(point.gps.ground_speed, "(h)"),
speed(point.gps.climb_rate, "(v)")));
} else {
nearest_label.setText("");
}
repaint();
}
/* MouseListener methods */
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e));
}
public void mouseReleased(MouseEvent e) {
}
/* MouseWheelListener methods */
public void mouseWheelMoved(MouseWheelEvent e) {
int zoom_change = e.getWheelRotation();
map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y));
}
/* ComponentListener methods */
public void componentHidden(ComponentEvent e) {
}
public void componentMoved(ComponentEvent e) {
}
public void componentResized(ComponentEvent e) {
map.set_transform();
}
public void componentShown(ComponentEvent e) {
map.set_transform();
}
MapView() {
addComponentListener(this);
addMouseMotionListener(this);
addMouseListener(this);
addMouseWheelListener(this);
}
}
class MapLine extends AltosMapLine {
public void paint(AltosMapTransform t) {
if (start == null || end == null)
return;
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Line2D.Double line = new Line2D.Double(point2d(t.screen(start)),
point2d(t.screen(end)));
g.setColor(Color.WHITE);
g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.draw(line);
g.setColor(Color.BLUE);
g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.draw(line);
String message = line_dist();
Rectangle2D bounds;
bounds = line_font.getStringBounds(message, g.getFontRenderContext());
float x = (float) line.x1;
float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
if (line.x1 < line.x2) {
x -= (float) bounds.getWidth() + 2.0f;
} else {
x += 2.0f;
}
g.setFont(line_font);
g.setColor(Color.WHITE);
for (int dy = -2; dy <= 2; dy += 2)
for (int dx = -2; dx <= 2; dx += 2)
g.drawString(message, x + dx, y + dy);
g.setColor(Color.BLUE);
g.drawString(message, x, y);
}
public MapLine() {
}
}
class MapPath extends AltosMapPath {
public void paint(AltosMapTransform t) {
Point2D.Double prev = null;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for (AltosMapPathPoint point : points) {
Point2D.Double cur = point2d(t.screen(point.gps.lat, point.gps.lon));
if (prev != null) {
Line2D.Double line = new Line2D.Double (prev, cur);
Rectangle bounds = line.getBounds();
if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
if (0 <= point.state && point.state < AltosUIMap.stateColors.length)
g.setColor(AltosUIMap.stateColors[point.state]);
else
g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
g.draw(line);
}
}
prev = cur;
}
}
}
class MapTile extends AltosMapTile {
public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
super(cache, upper_left, center, zoom, maptype, px_size, scale);
}
public void paint(AltosMapTransform t) {
AltosPointDouble point_double = t.screen(upper_left);
Point point = new Point((int) (point_double.x + 0.5),
(int) (point_double.y + 0.5));
if (!g.hitClip(point.x, point.y, px_size, px_size))
return;
AltosImage altos_image = get_image();
AltosUIImage ui_image = (AltosUIImage) altos_image;
Image image = null;
if (ui_image != null)
image = ui_image.image;
if (image != null) {
g.drawImage(image, point.x, point.y, null);
/*
* Useful when debugging map fetching problems
*
String message = String.format("%.6f %.6f", center.lat, center.lon);
g.setFont(tile_font);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext());
float x = px_size / 2.0f;
float y = px_size / 2.0f;
x = x - (float) bounds.getWidth() / 2.0f;
y = y + (float) bounds.getHeight() / 2.0f;
g.setColor(Color.RED);
g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
*/
} else {
g.setColor(Color.GRAY);
g.fillRect(point.x, point.y, px_size, px_size);
if (t.has_location()) {
String message = null;
switch (status) {
case AltosMapTile.fetching:
message = "Fetching...";
break;
case AltosMapTile.bad_request:
message = "Internal error";
break;
case AltosMapTile.failed:
message = "Network error";
break;
case AltosMapTile.forbidden:
message = "Outside of known launch areas";
break;
}
if (message != null && tile_font != null) {
g.setFont(tile_font);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext());
float x = px_size / 2.0f;
float y = px_size / 2.0f;
x = x - (float) bounds.getWidth() / 2.0f;
y = y + (float) bounds.getHeight() / 2.0f;
g.setColor(Color.BLACK);
g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
}
}
}
}
}
public static final 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
Color.BLACK, // invalid
Color.CYAN, // stateless
};
/* AltosMapInterface functions */
public AltosMapPath new_path() {
return new MapPath();
}
public AltosMapLine new_line() {
return new MapLine();
}
public AltosImage load_image(File file) throws Exception {
return new AltosUIImage(ImageIO.read(file));
}
public AltosMapMark new_mark(double lat, double lon, int state) {
return new MapMark(lat, lon, state);
}
public AltosMapMark new_mark(double lat, double lon, int state, String label) {
return new MapMark(lat, lon, state, label);
}
public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
}
public int width() {
return view.getWidth();
}
public int height() {
return view.getHeight();
}
public void repaint() {
view.repaint();
}
public void repaint(AltosRectangle damage) {
view.repaint(damage);
}
public void set_zoom_label(String label) {
zoom_label.setText(label);
}
public void select_object(AltosLatLon latlon) {
debug("select at %f,%f\n", latlon.lat, latlon.lon);
}
public void debug(String format, Object ... arguments) {
if (AltosUIPreferences.serial_debug())
System.out.printf(format, arguments);
}
/* AltosFlightDisplay interface */
public void set_font() {
tile_font = AltosUILib.value_font;
line_font = AltosUILib.status_font;
if (nearest_label != null)
nearest_label.setFont(AltosUILib.value_font);
}
public void font_size_changed(int font_size) {
set_font();
repaint();
}
public void units_changed(boolean imperial_units) {
repaint();
}
JLabel zoom_label;
JLabel nearest_label;
public void set_maptype(int type) {
/*
map.set_maptype(type);
maptype_combo.setSelectedIndex(type);
*/
}
/* AltosUIMapPreload functions */
public void set_zoom(int zoom) {
map.set_zoom(zoom);
}
public void add_mark(double lat, double lon, int status) {
map.add_mark(lat, lon, status);
}
public void add_mark(double lat, double lon, int status, String label) {
map.add_mark(lat, lon, status, label);
}
public void clear_marks() {
map.clear_marks();
}
/* AltosFlightDisplay interface */
public void reset() {
// nothing
}
public void show(AltosState state, AltosListenerState listener_state) {
map.show(state, listener_state);
}
public void show(AltosGPS gps, double time, int state, double gps_height) {
map.show(gps, time, state, gps_height);
}
public String getName() {
return "Map";
}
/* AltosGraphUI interface */
public void centre(AltosState state) {
map.centre(state);
}
public void centre(AltosGPS gps) {
map.centre(gps);
}
/* internal layout bits */
private GridBagLayout layout = new GridBagLayout();
/*
JComboBox<String> maptype_combo;
*/
MapView view;
public AltosUIMap() {
set_font();
view = new MapView();
view.setPreferredSize(new Dimension(500,500));
view.setVisible(true);
view.setEnabled(true);
GridBagLayout my_layout = new GridBagLayout();
setLayout(my_layout);
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.BOTH;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 10;
c.weightx = 1;
c.weighty = 1;
add(view, c);
int y = 0;
zoom_label = new JLabel("", JLabel.CENTER);
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = y++;
c.weightx = 0;
c.weighty = 0;
add(zoom_label, c);
JButton zoom_reset = new JButton("0");
zoom_reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.set_zoom(map.default_zoom);
}
});
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = y++;
c.weightx = 0;
c.weighty = 0;
add(zoom_reset, c);
JButton zoom_in = new JButton("+");
zoom_in.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.set_zoom(map.get_zoom() + 1);
}
});
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = y++;
c.weightx = 0;
c.weighty = 0;
add(zoom_in, c);
JButton zoom_out = new JButton("-");
zoom_out.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
map.set_zoom(map.get_zoom() - 1);
}
});
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = y++;
c.weightx = 0;
c.weighty = 0;
add(zoom_out, c);
nearest_label = new JLabel("", JLabel.LEFT);
nearest_label.setFont(tile_font);
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 11;
c.weightx = 0;
c.weighty = 0;
c.gridwidth = 1;
c.gridheight = 1;
add(nearest_label, c);
/*
maptype_combo = new JComboBox<String>(map.maptype_labels);
maptype_combo.setEditable(false);
maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
maptype_combo.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
map.set_maptype(maptype_combo.getSelectedIndex());
}
});
c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = y++;
c.weightx = 0;
c.weighty = 0;
add(maptype_combo, c);
*/
map = new AltosMap(this);
}
}

View File

@@ -0,0 +1,576 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
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_14.*;
class AltosUIMapPos extends Box implements ActionListener {
AltosUIMapPreload preload;
AltosUIFrame owner;
JLabel label;
JComboBox hemi;
JTextField deg;
JLabel deg_label;
JTextField min;
JLabel min_label;
/* ActionListener interface */
public void actionPerformed(ActionEvent e) {
preload.center_map();
}
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 ParseException {
int h = hemi.getSelectedIndex();
String d_t = deg.getText();
String m_t = min.getText();
double d, m, v;
try {
d = AltosParse.parse_double_locale(d_t);
} catch (ParseException pe) {
JOptionPane.showMessageDialog(owner,
String.format("Invalid degrees \"%s\"",
d_t),
"Invalid number",
JOptionPane.ERROR_MESSAGE);
throw pe;
}
try {
if (m_t.equals(""))
m = 0;
else
m = AltosParse.parse_double_locale(m_t);
} catch (ParseException pe) {
JOptionPane.showMessageDialog(owner,
String.format("Invalid minutes \"%s\"",
m_t),
"Invalid number",
JOptionPane.ERROR_MESSAGE);
throw pe;
}
v = d + m/60.0;
if (h == 1)
v = -v;
return v;
}
public AltosUIMapPos(AltosUIFrame in_owner,
AltosUIMapPreload preload,
String label_value,
String[] hemi_names,
double default_value) {
super(BoxLayout.X_AXIS);
owner = in_owner;
this.preload = preload;
label = new JLabel(label_value);
hemi = new JComboBox<String>(hemi_names);
hemi.setEditable(false);
deg = new JTextField(5);
deg.addActionListener(this);
deg.setMinimumSize(deg.getPreferredSize());
deg.setHorizontalAlignment(JTextField.RIGHT);
deg_label = new JLabel("°");
min = new JTextField(9);
min.addActionListener(this);
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);
}
}
public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener, AltosUnitsListener, AltosFontListener {
AltosUIFrame owner;
AltosUIMap map;
AltosUIMapPos lat;
AltosUIMapPos lon;
JProgressBar pbar;
JLabel site_list_label;
java.util.List<AltosLaunchSite> sites;
JComboBox<AltosLaunchSite> site_list;
JToggleButton load_button;
JButton close_button;
/*
JCheckBox[] maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1];
*/
JComboBox<Integer> min_zoom;
JComboBox<Integer> max_zoom;
JLabel radius_label;
JComboBox<Double> radius;
int scale = 1;
Integer[] zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
Double[] radius_mi = { 1.0, 2.0, 5.0, 10.0, 20.0 };
Double radius_def_mi = 5.0;
Double[] radius_km = { 2.0, 5.0, 10.0, 20.0, 30.0 };
Double radius_def_km = 10.0;
AltosMapLoader loader;
static final String[] lat_hemi_names = { "N", "S" };
static final String[] lon_hemi_names = { "E", "W" };
double latitude, longitude;
long loader_notify_time;
/* AltosMapLoaderListener interfaces */
public void loader_start(final int max) {
loader_notify_time = System.currentTimeMillis();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
pbar.setMaximum(max);
pbar.setValue(0);
pbar.setString("");
}
});
}
public void loader_notify(final int cur, final int max, final String name) {
long now = System.currentTimeMillis();
if (now - loader_notify_time < 100)
return;
loader_notify_time = now;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
pbar.setValue(cur);
pbar.setString(name);
}
});
}
public void loader_done(int max) {
loader = null;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
pbar.setValue(0);
pbar.setString("");
load_button.setSelected(false);
}
});
}
public void debug(String format, Object ... arguments) {
if (AltosSerial.debug)
System.out.printf(format, arguments);
}
private int all_types() {
/*
int all_types = 0;
for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
if (maptypes[t].isSelected())
all_types |= (1 << t);
return all_types;
*/
return 1 << AltosMap.maptype_hybrid;
}
void add_mark(double lat, double lon, int state, String label) {
map.add_mark(lat, lon, state, label);
}
void reset_marks() {
map.clear_marks();
AltosLatLon centre = null;
String centre_name = null;
if (map != null && map.map != null)
centre = map.map.centre;
for (AltosLaunchSite site : sites) {
if (centre != null && centre.lat == site.latitude && centre.lon == site.longitude)
centre_name = site.name;
else
add_mark(site.latitude, site.longitude, AltosLib.ao_flight_main, site.name);
}
if (centre != null)
add_mark(centre.lat, centre.lon, AltosLib.ao_flight_boost, centre_name);
}
void center_map(double latitude, double longitude) {
map.map.centre(new AltosLatLon(latitude, longitude));
reset_marks();
}
void center_map() {
try {
center_map(lat.get_value(), lon.get_value());
} catch (ParseException pe) {
}
}
public void itemStateChanged(ItemEvent e) {
int state = e.getStateChange();
if (state == ItemEvent.SELECTED) {
Object o = e.getItem();
if (o instanceof AltosLaunchSite) {
AltosLaunchSite site = (AltosLaunchSite) o;
lat.set_value(site.latitude);
lon.set_value(site.longitude);
center_map(site.latitude, site.longitude);
}
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("close")) {
if (loader != null)
loader.abort();
setVisible(false);
}
if (cmd.equals("load")) {
if (loader == null) {
try {
latitude = lat.get_value();
longitude = lon.get_value();
int min_z = (Integer) min_zoom.getSelectedItem();
int max_z = (Integer) max_zoom.getSelectedItem();
if (max_z < min_z)
max_z = min_z;
Double r = (Double) radius.getSelectedItem();
if (AltosPreferences.imperial_units())
r = AltosConvert.miles_to_meters(r);
else
r = r * 1000;
center_map(latitude, longitude);
loader = new AltosMapLoader(this,
latitude, longitude,
min_z, max_z, r,
all_types(), scale);
} catch (ParseException pe) {
load_button.setSelected(false);
}
}
}
}
public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
this.sites = sites;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int i = 1;
for (AltosLaunchSite site : sites) {
site_list.insertItemAt(site, i);
i++;
}
reset_marks();
}
});
}
private void set_radius_values() {
radius_label.setText(String.format("Map Radius (%s)",
AltosPreferences.imperial_units() ? "mi" : "km"));
Double[] radii;
if (AltosPreferences.imperial_units())
radii = radius_mi;
else
radii = radius_km;
radius.removeAllItems();
for (Double r : radii) {
radius.addItem(r);
}
radius.setSelectedItem(radii[2]);
radius.setMaximumRowCount(radii.length);
}
public void units_changed(boolean imperial_units) {
map.units_changed(imperial_units);
set_radius_values();
}
public void font_size_changed(int font_size) {
map.font_size_changed(font_size);
}
public AltosUIMapPreload(AltosUIFrame in_owner) {
owner = in_owner;
Container pane = getScrollablePane();
GridBagConstraints c = new GridBagConstraints();
Insets i = new Insets(4,4,4,4);
setTitle("AltOS Load Maps");
pane.setLayout(new GridBagLayout());
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
AltosUIPreferences.unregister_font_listener(AltosUIMapPreload.this);
AltosPreferences.unregister_units_listener(AltosUIMapPreload.this);
}
});
AltosPreferences.register_units_listener(this);
AltosUIPreferences.register_font_listener(this);
map = new AltosUIMap();
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 = 10;
c.anchor = GridBagConstraints.CENTER;
pane.add(map, c);
pbar = new JProgressBar();
pbar.setMinimum(0);
pbar.setMaximum(1);
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 = 10;
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<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) });
site_list.addItemListener(this);
new AltosLaunchSites(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 AltosUIMapPos(owner, this,
"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 AltosUIMapPos(owner, this,
"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);
/*
JLabel types_label = new JLabel("Map Types");
c.gridx = 2;
c.gridwidth = 2;
c.gridy = 2;
pane.add(types_label, c);
c.gridwidth = 1;
for (int type = AltosMap.maptype_hybrid; type <= AltosMap.maptype_terrain; type++) {
maptypes[type] = new JCheckBox(AltosMap.maptype_labels[type],
type == AltosMap.maptype_hybrid);
c.gridx = 2 + (type >> 1);
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = (type & 1) + 3;
pane.add(maptypes[type], c);
}
*/
JLabel min_zoom_label = new JLabel("Minimum Zoom");
c.gridx = 4;
c.gridy = 2;
pane.add(min_zoom_label, c);
min_zoom = new JComboBox<Integer>(zooms);
min_zoom.setSelectedItem(zooms[10]);
min_zoom.setEditable(false);
c.gridx = 5;
c.gridy = 2;
pane.add(min_zoom, c);
JLabel max_zoom_label = new JLabel("Maximum Zoom");
c.gridx = 4;
c.gridy = 3;
pane.add(max_zoom_label, c);
max_zoom = new JComboBox<Integer>(zooms);
max_zoom.setSelectedItem(zooms[14]);
max_zoom.setEditable(false);
c.gridx = 5;
c.gridy = 3;
pane.add(max_zoom, c);
radius_label = new JLabel();
c.gridx = 4;
c.gridy = 4;
pane.add(radius_label, c);
radius = new JComboBox<Double>();
radius.setEditable(true);
c.gridx = 5;
c.gridy = 4;
pane.add(radius, c);
set_radius_values();
pack();
setLocationRelativeTo(owner);
setVisible(true);
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright © 2013 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
public class AltosUIMarker implements AltosUIGrapher {
ArrayList<ValueMarker> markers;
int last_id;
XYPlot plot;
boolean enabled;
int fetch;
Color color;
private void remove_markers() {
for (ValueMarker marker : markers)
plot.removeDomainMarker(marker);
}
private void add_markers() {
for (ValueMarker marker : markers)
plot.addDomainMarker(marker);
}
public void set_units() {
}
public boolean need_reset() { return true; }
public void set_enable(boolean enable) {
if (enabled == enable)
return;
if (enable)
add_markers();
else
remove_markers();
enabled = enable;
}
public void clear() {
if (enabled)
remove_markers();
markers = new ArrayList<ValueMarker>();
}
public void add(AltosUIDataPoint dataPoint) {
try {
int id = dataPoint.id(fetch);
if (id < 0)
return;
if (id == last_id)
return;
ValueMarker marker = new ValueMarker(dataPoint.x());
marker.setLabel(dataPoint.id_name(fetch));
marker.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
marker.setPaint(color);
if (enabled)
plot.addDomainMarker(marker);
markers.add(marker);
last_id = id;
} catch (AltosUIDataMissing m) {
}
}
public AltosUIMarker (int fetch, Color color, XYPlot plot, boolean enable) {
markers = new ArrayList<ValueMarker>();
last_id = -1;
this.fetch = fetch;
this.color = color;
this.plot = plot;
this.enabled = enable;
}
public void setNotify(boolean notify) {
}
public void fireSeriesChanged() {
}
public AltosUIMarker (int fetch, Color color, XYPlot plot) {
this(fetch, color, plot, true);
}
}

View File

@@ -0,0 +1,223 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.*;
import java.awt.Component;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUIPreferences extends AltosPreferences {
/* font size preferences name */
final static String fontSizePreference = "FONT-SIZE";
/* Look&Feel preference name */
final static String lookAndFeelPreference = "LOOK-AND-FEEL";
/* Window position preference name */
final static String positionPreference = "POSITION";
/* Maps cache size preference name */
final static String mapCachePreference = "MAP-CACHE";
/* UI Component to pop dialogs up */
static Component component;
static LinkedList<AltosFontListener> font_listeners;
static int font_size = AltosUILib.font_size_medium;
static LinkedList<AltosUIListener> ui_listeners;
static String look_and_feel = null;
/* Serial debug */
public static boolean serial_debug;
static LinkedList<AltosPositionListener> position_listeners;
public static int position = AltosUILib.position_top_left;
public static void init() {
AltosPreferences.init(new AltosUIPreferencesBackend());
font_listeners = new LinkedList<AltosFontListener>();
font_size = backend.getInt(fontSizePreference, AltosUILib.font_size_medium);
AltosUILib.set_fonts(font_size);
look_and_feel = backend.getString(lookAndFeelPreference, UIManager.getSystemLookAndFeelClassName());
ui_listeners = new LinkedList<AltosUIListener>();
serial_debug = backend.getBoolean(serialDebugPreference, false);
AltosLink.set_debug(serial_debug);
position = backend.getInt(positionPreference, AltosUILib.position_top_left);
position_listeners = new LinkedList<AltosPositionListener>();
}
static { init(); }
public static void set_component(Component in_component) {
component = in_component;
}
private static boolean check_dir(File dir) {
if (!dir.exists()) {
if (!dir.mkdirs()) {
JOptionPane.showMessageDialog(component,
dir.getName(),
"Cannot create directory",
JOptionPane.ERROR_MESSAGE);
return false;
}
} else if (!dir.isDirectory()) {
JOptionPane.showMessageDialog(component,
dir.getName(),
"Is not a directory",
JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}
/* Configure the log directory. This is where all telemetry and eeprom files
* will be written to, and where replay will look for telemetry files
*/
public static void ConfigureLog() {
JFileChooser logdir_chooser = new JFileChooser(logdir.getParentFile());
logdir_chooser.setDialogTitle("Configure Data Logging Directory");
logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) {
File dir = logdir_chooser.getSelectedFile();
if (check_dir(dir))
set_logdir(dir);
}
}
public static int font_size() {
synchronized (backend) {
return font_size;
}
}
static void set_fonts() {
}
public static void set_font_size(int new_font_size) {
synchronized (backend) {
font_size = new_font_size;
backend.putInt(fontSizePreference, font_size);
flush_preferences();
AltosUILib.set_fonts(font_size);
for (AltosFontListener l : font_listeners) {
l.font_size_changed(font_size);
}
}
}
public static void register_font_listener(AltosFontListener l) {
synchronized (backend) {
font_listeners.add(l);
}
}
public static void unregister_font_listener(AltosFontListener l) {
synchronized (backend) {
font_listeners.remove(l);
}
}
public static void set_look_and_feel(String new_look_and_feel) {
try {
UIManager.setLookAndFeel(new_look_and_feel);
} catch (Exception e) {
}
synchronized(backend) {
look_and_feel = new_look_and_feel;
backend.putString(lookAndFeelPreference, look_and_feel);
flush_preferences();
for (AltosUIListener l : ui_listeners)
l.ui_changed(look_and_feel);
}
}
public static String look_and_feel() {
synchronized (backend) {
return look_and_feel;
}
}
public static void register_ui_listener(AltosUIListener l) {
synchronized(backend) {
ui_listeners.add(l);
}
}
public static void unregister_ui_listener(AltosUIListener l) {
synchronized (backend) {
ui_listeners.remove(l);
}
}
public static void set_serial_debug(boolean new_serial_debug) {
AltosLink.set_debug(new_serial_debug);
synchronized (backend) {
serial_debug = new_serial_debug;
backend.putBoolean(serialDebugPreference, serial_debug);
flush_preferences();
}
}
public static boolean serial_debug() {
synchronized (backend) {
return serial_debug;
}
}
public static void register_position_listener(AltosPositionListener l) {
synchronized(backend) {
position_listeners.add(l);
}
}
public static void unregister_position_listener(AltosPositionListener l) {
synchronized (backend) {
position_listeners.remove(l);
}
}
public static void set_position(int new_position) {
synchronized (backend) {
position = new_position;
backend.putInt(positionPreference, position);
flush_preferences();
for (AltosPositionListener l : position_listeners)
l.position_changed(position);
}
}
public static int position() {
synchronized (backend) {
return position;
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.File;
import java.util.prefs.*;
import org.altusmetrum.altoslib_14.*;
import javax.swing.filechooser.FileSystemView;
public class AltosUIPreferencesBackend extends AltosPreferencesBackend {
private Preferences _preferences = null;
public AltosUIPreferencesBackend() {
_preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
}
public AltosUIPreferencesBackend(Preferences in_preferences) {
_preferences = in_preferences;
}
public String getString(String key, String def) {
return _preferences.get(key, def);
}
public void putString(String key, String value) {
_preferences.put(key, value);
}
public int getInt(String key, int def) {
return _preferences.getInt(key, def);
}
public void putInt(String key, int value) {
_preferences.putInt(key, value);
}
public double getDouble(String key, double def) {
return _preferences.getDouble(key, def);
}
public void putDouble(String key, double value) {
_preferences.putDouble(key, value);
}
public boolean getBoolean(String key, boolean def) {
return _preferences.getBoolean(key, def);
}
public void putBoolean(String key, boolean value) {
_preferences.putBoolean(key, value);
}
public byte[] getBytes(String key, byte[] def) {
return _preferences.getByteArray(key, def);
}
public void putBytes(String key, byte[] value) {
_preferences.putByteArray(key, value);
}
public boolean nodeExists(String key) {
try {
return _preferences.nodeExists(key);
} catch (BackingStoreException be) {
return false;
}
}
public AltosPreferencesBackend node(String key) {
return new AltosUIPreferencesBackend(_preferences.node(key));
}
public String[] keys() {
try {
return _preferences.keys();
} catch (BackingStoreException be) {
return null;
}
}
public void remove(String key) {
_preferences.remove(key);
}
public void flush() {
try {
_preferences.flush();
} catch (BackingStoreException ee) {
System.err.printf("Cannot save preferences\n");
}
}
public File homeDirectory() {
/* Use the file system view default directory */
return FileSystemView.getFileSystemView().getDefaultDirectory();
}
public void debug(String format, Object ... arguments) {
System.out.printf(format, arguments);
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUIRateList extends JComboBox<String> {
String product;
int serial;
public void set_rate(int new_rate) {
if (new_rate != AltosLib.MISSING)
setSelectedIndex(new_rate);
setVisible(new_rate != AltosLib.MISSING);
}
public void set_product(String new_product) {
product = new_product;
}
public void set_serial(int new_serial) {
serial = new_serial;
}
public int rate() {
return getSelectedIndex();
}
public AltosUIRateList () {
super();
for (int i = 0; i < AltosLib.ao_telemetry_rate_values.length; i++)
addItem(String.format("%d baud", AltosLib.ao_telemetry_rate_values[i]));
setMaximumRowCount(getItemCount());
setEditable(false);
product = "Unknown";
serial = 0;
}
public AltosUIRateList(int in_rate) {
this();
set_rate(in_rate);
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright © 2014 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUITelemetryList extends JComboBox<String> {
public int get_selected() {
return getSelectedIndex() + 1;
}
public void set_selected(int telemetry) {
setSelectedIndex(telemetry-1);
}
public AltosUITelemetryList(int serial) {
super();
for (int i = AltosLib.ao_telemetry_min; i <= AltosLib.ao_telemetry_max; i++)
addItem(AltosLib.telemetry_name(i));
int telemetry = AltosPreferences.telemetry(serial);
if (telemetry < AltosLib.ao_telemetry_min || AltosLib.ao_telemetry_max < telemetry)
telemetry = AltosLib.ao_telemetry_standard;
setMaximumRowCount(AltosLib.ao_telemetry_max);
set_selected(telemetry);
revalidate();
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright © 2017 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.io.*;
import java.util.ArrayList;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
import org.jfree.ui.*;
import org.jfree.chart.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.renderer.*;
import org.jfree.chart.renderer.xy.*;
import org.jfree.chart.labels.*;
import org.jfree.data.xy.*;
import org.jfree.data.*;
class AltosUITime extends AltosUnits {
public double value(double v, boolean imperial_units) { return v; }
public double inverse(double v, boolean imperial_unis) { return v; }
public String show_units(boolean imperial_units) { return "s"; }
public String say_units(boolean imperial_units) { return "seconds"; }
public int show_fraction(int width, boolean imperial_units) {
if (width < 5)
return 0;
return width - 5;
}
public int say_fraction(boolean imperial_units) { return 0; }
}
class AltosXYSeries extends XYSeries {
public AltosXYSeries(String label) {
super(label);
}
}
public class AltosUITimeSeries extends AltosTimeSeries implements AltosUIGrapher {
AltosUILineStyle line_style;
boolean enable;
boolean custom_axis_set;
AltosUIAxis axis;
boolean marker;
boolean marker_top;
XYLineAndShapeRenderer renderer;
XYPlot plot;
AltosXYSeries xy_series;
ArrayList<ValueMarker> markers;
float width;
/* AltosUIGrapher interface */
public boolean need_reset() {
return false;
}
public void clear() {
}
public void add(AltosUIDataPoint dataPoint) {
}
public void setNotify(boolean notify) {
}
public void fireSeriesChanged() {
}
public void set_data() {
if (marker) {
if (markers != null) {
for (ValueMarker marker : markers)
plot.removeDomainMarker(marker);
}
markers = new ArrayList<ValueMarker>();
for (AltosTimeValue v : this) {
String s = units.string_value(v.value);
ValueMarker marker = new ValueMarker(v.time);
marker.setLabel(s);
if (marker_top) {
marker.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
marker.setLabelTextAnchor(TextAnchor.TOP_LEFT);
} else {
marker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
marker.setLabelTextAnchor(TextAnchor.BOTTOM_LEFT);
}
marker.setPaint(line_style.color);
marker.setStroke(new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
if (enable)
plot.addDomainMarker(marker);
markers.add(marker);
}
} else {
xy_series.clear();
xy_series.setNotify(false);
for (AltosTimeValue v : this) {
double value = v.value;
if (units != null)
value = units.graph_value(value);
xy_series.add(v.time, value);
}
xy_series.setNotify(true);
}
clear_changed();
}
public void set_units() {
axis.set_units();
StandardXYToolTipGenerator ttg;
if (units != null) {
String time_example = (new AltosUITime()).graph_format(7);
String example = units.graph_format(7);
ttg = new StandardXYToolTipGenerator(String.format("{1}s: {2}%s ({0})",
units.graph_units()),
new java.text.DecimalFormat(time_example),
new java.text.DecimalFormat(example));
renderer.setBaseToolTipGenerator(ttg);
}
set_data();
}
public AltosXYSeries xy_series() {
return xy_series;
}
public void set_enable(boolean enable) {
if (this.enable != enable) {
this.enable = enable;
if (marker) {
for (ValueMarker marker : markers) {
if (enable)
plot.addDomainMarker(marker);
else
plot.removeDomainMarker(marker);
}
} else {
renderer.setSeriesVisible(0, enable);
axis.set_enable(enable);
}
}
}
// public BasicStroke(float width, int cap, int join, float miterlimit,
// float dash[], float dash_phase)
public void set_line_width(float width) {
this.width = width;
if (markers != null) {
for (ValueMarker marker : markers) {
marker.setStroke(new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
}
} else {
if (line_style.dash[0] == 0.0)
renderer.setSeriesStroke(0, new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
else
renderer.setSeriesStroke(0, new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f, line_style.dash, 0.0f));
}
}
public void set_axis(AltosUILineStyle line_style, boolean enable, AltosUIAxis axis) {
this.line_style = line_style;
this.enable = enable;
this.axis = axis;
this.marker = false;
this.width = 1.0f;
axis.ref(this.enable);
renderer = new XYLineAndShapeRenderer(true, false);
renderer.setSeriesPaint(0, line_style.color);
set_line_width(this.width);
renderer.setSeriesVisible(0, enable);
xy_series = new AltosXYSeries(label);
}
public void set_marker(AltosUILineStyle line_style, boolean enable, XYPlot plot, boolean marker_top) {
this.line_style = line_style;
this.enable = enable;
this.marker = true;
this.plot = plot;
this.marker_top = marker_top;
}
public void set_shapes_visible(boolean shapes_visible) {
renderer.setSeriesShapesVisible(0, shapes_visible);
}
public AltosUITimeSeries(String label, AltosUnits units) {
super(label, units);
}
public AltosUITimeSeries(String label, AltosUnits units,
AltosUILineStyle line_style, boolean enable,
AltosUIAxis axis) {
this(label, units);
set_axis(line_style, enable, axis);
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright © 2014 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
AltosUnits units;
abstract public double value(AltosState state, int i);
public double good() { return 0; }
public boolean good(double value) { return value != AltosLib.MISSING && value >= good(); }
public boolean hide(double value) { return false; }
public boolean hide(AltosState state, int i) {
if (state == null)
return hide(AltosLib.MISSING);
return hide(value(state, i));
}
public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
return hide(state, i);
}
public double value (AltosState state, AltosListenerState listener_state, int i) {
return value(state, i);
}
public double[] last_values;
private void show(boolean force, double... v) {
show();
for (int i = 0; i < values.length; i++) {
if (force || v[i] != last_values[i]) {
String value_text;
boolean good = false;
if (v[i] == AltosLib.MISSING) {
value_text = "Missing";
} else {
value_text = units.show(8, v[i]);
if (i == 0)
good = good(v[i]);
}
last_values[i] = v[i];
if (i == 0 && lights != null)
set_lights(good);
values[i].setText(value_text);
}
}
}
boolean hide = false;
public void show(double... v) {
show(false, v);
}
public void units_changed(boolean imperial_units) {
if (!hide)
show(true, last_values);
}
public void show (AltosState state, AltosListenerState listener_state) {
double[] v = new double[values.length];
hide = false;
for (int i = 0; i < values.length; i++) {
if (state != null)
v[i] = value(state, listener_state, i);
else
v[i] = AltosLib.MISSING;
if (hide(state, listener_state, i))
hide = true;
}
if (hide)
hide();
else
show(v);
}
public void reset() {
for (int i = 0; i < last_values.length; i++)
last_values[i] = AltosLib.MISSING;
}
public AltosUIUnitsIndicator (Container container, int x, int y, int label_width, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
super(container, x, y, label_width, name, number_values, has_lights, width);
this.units = units;
last_values = new double[values.length];
for (int i = 0; i < last_values.length; i++)
last_values[i] = AltosLib.MISSING;
}
public AltosUIUnitsIndicator (Container container, int x, int y, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
this(container, x, y, 1, units, name, number_values, has_lights, width);
}
public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name, int number_values, boolean has_lights, int width) {
this(container, 0, y, units, name, number_values, has_lights, width);
}
public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name, int width) {
this(container, 0, y, units, name, 1, false, width);
}
public AltosUIUnitsIndicator (Container container, int y, AltosUnits units, String name) {
this(container, 0, y, units, name, 1, false, 1);
}
public AltosUIUnitsIndicator (Container container, int x,int y, AltosUnits units, String name) {
this(container, x, y, units, name, 1, false, 1);
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2014 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.awt.*;
import javax.swing.*;
import org.altusmetrum.altoslib_14.*;
public abstract class AltosUIVoltageIndicator extends AltosUIUnitsIndicator {
abstract public double voltage(AltosState state);
abstract public double good();
public double value(AltosState state, int i) {
return voltage(state);
}
double last_voltage = AltosLib.MISSING;
public AltosUIVoltageIndicator (Container container, int x, int y, String name, int width) {
super(container, x, y, AltosConvert.voltage, name, 1, true, width);
}
public AltosUIVoltageIndicator (Container container, int y, String name, int width) {
this(container, 0, y, name, width);
}
}

View File

@@ -0,0 +1,144 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
import java.util.*;
import libaltosJNI.*;
import org.altusmetrum.altoslib_14.*;
public class AltosUSBDevice extends altos_device implements AltosDevice {
public String toString() {
String name = getName();
if (name == null || "".equals(name))
name = "Altus Metrum";
return String.format("%-20.20s %4d %s",
name, getSerial(), getPath());
}
public String toShortString() {
String name = getName();
if (name == null || "".equals(name))
name = "Altus Metrum";
return String.format("%s %d %s",
name, getSerial(), getPath());
}
public String getErrorString() {
altos_error error = new altos_error();
libaltos.altos_get_last_error(error);
return String.format("%s (%d)", error.getString(), error.getCode());
}
public SWIGTYPE_p_altos_file open() {
return libaltos.altos_open(this);
}
public boolean isAltusMetrum() {
if (getVendor() != AltosUILib.vendor_altusmetrum)
return false;
if (getProduct() < AltosUILib.product_altusmetrum_min)
return false;
if (getProduct() > AltosUILib.product_altusmetrum_max)
return false;
return true;
}
public boolean matchProduct(int want_product) {
if (!isAltusMetrum())
return false;
if (want_product == AltosUILib.product_any)
return true;
int have_product = getProduct();
if (want_product == AltosUILib.product_basestation)
return have_product == AltosUILib.product_teledongle ||
have_product == AltosUILib.product_telebt ||
have_product == AltosUILib.product_megadongle;
if (want_product == AltosUILib.product_altimeter)
return have_product == AltosUILib.product_telemetrum ||
have_product == AltosUILib.product_telemega ||
have_product == AltosUILib.product_easytimer ||
have_product == AltosUILib.product_easymega ||
have_product == AltosUILib.product_telegps ||
have_product == AltosUILib.product_easymini ||
have_product == AltosUILib.product_telemini;
if (have_product == AltosUILib.product_altusmetrum) /* old devices match any request */
return true;
if (want_product == have_product)
return true;
return false;
}
public int hashCode() {
return getVendor() ^ getProduct() ^ getSerial() ^ getPath().hashCode();
}
public AltosUsbId usb_id() {
return new AltosUsbId(getVendor(), getProduct());
}
public String usb_product() {
return getName();
}
public boolean equals(Object o) {
if (o == null)
return false;
if (!(o instanceof AltosUSBDevice))
return false;
AltosUSBDevice other = (AltosUSBDevice) o;
return getVendor() == other.getVendor() &&
getProduct() == other.getProduct() &&
getSerial() == other.getSerial() &&
getPath().equals(other.getPath());
}
static public java.util.List<AltosDevice> list(int product) {
if (!AltosUILib.load_library())
return null;
SWIGTYPE_p_altos_list list = libaltos.altos_list_start();
ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>();
if (list != null) {
for (;;) {
AltosUSBDevice device = new AltosUSBDevice();
if (libaltos.altos_list_next(list, device) == 0)
break;
if (device.matchProduct(product))
device_list.add(device);
}
libaltos.altos_list_finish(list);
}
return device_list;
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altosuilib_14;
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)");
}
}
}
}

106
altosuilib/Makefile.am Normal file
View File

@@ -0,0 +1,106 @@
AM_JAVACFLAGS=$(JAVAC_VERSION_FLAGS) -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked
JAVAROOT=bin
CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../altoslib/*:../libaltos:/usr/share/java/*"
SRC=.
altosuilibdir = $(datadir)/java
altosuilib_JAVA = \
AltosDevice.java \
AltosDeviceDialog.java \
AltosPositionListener.java \
AltosUIAccelCal.java \
AltosUIConfigure.java \
AltosUIAxis.java \
AltosUIDataMissing.java \
AltosUIDataPoint.java \
AltosUIDataSet.java \
AltosUIGrapher.java \
AltosUIDialog.java \
AltosUIEnable.java \
AltosUIFrame.java \
AltosUILib.java \
AltosUIListener.java \
AltosUIMarker.java \
AltosUIPreferencesBackend.java \
AltosUIPreferences.java \
AltosUIFlightSeries.java \
AltosUIGraph.java \
AltosGraph.java \
AltosShapeListener.java \
AltosUSBDevice.java \
AltosVoice.java \
AltosDisplayThread.java \
AltosDeviceUIDialog.java \
AltosSerial.java \
AltosSerialInUseException.java \
AltosConfigFreqUI.java \
AltosScanUI.java \
AltosEepromDelete.java \
AltosEepromGrapher.java \
AltosEepromManage.java \
AltosEepromMonitorUI.java \
AltosEepromSelect.java \
AltosCSVUI.java \
AltosDataChooser.java \
AltosLights.java \
AltosLed.java \
AltosFlashUI.java \
AltosRomconfigUI.java \
AltosInfoTable.java \
AltosFlightInfoTableModel.java \
AltosFlightStatsTable.java \
AltosBTDevice.java \
AltosBTDeviceIterator.java \
AltosBTManage.java \
AltosBTKnown.java \
AltosUILineStyle.java \
AltosUIMap.java \
AltosUIMapPreload.java \
AltosUIFlightTab.java \
AltosUIIndicator.java \
AltosUIUnitsIndicator.java \
AltosUIVoltageIndicator.java \
AltosUIFreqList.java \
AltosUITelemetryList.java \
AltosUIRateList.java \
AltosUIImage.java \
AltosUITimeSeries.java \
OSXAdapter.java
JAR=altosuilib_$(ALTOSUILIB_VERSION).jar
# Icons
ICONDIR=$(top_srcdir)/icon
ICONS= $(ICONDIR)/redled.png $(ICONDIR)/redoff.png \
$(ICONDIR)/greenled.png $(ICONDIR)/greenoff.png \
$(ICONDIR)/grayon.png $(ICONDIR)/grayled.png
# icon base names for jar
ICONJAR= -C $(ICONDIR) redled.png -C $(ICONDIR) redoff.png \
-C $(ICONDIR) greenled.png -C $(ICONDIR) greenoff.png \
-C $(ICONDIR) grayon.png -C $(ICONDIR) grayled.png
all-local: $(JAR)
clean-local:
-rm -rf $(JAVAROOT) altosuilib_*.jar
install-altosuilibJAVA: $(JAR)
@$(NORMAL_INSTALL)
test -z "$(altosuilibdir)" || $(MKDIR_P) "$(DESTDIR)$(altosuilibdir)"
echo " $(INSTALL_DATA)" "$(JAR)" "'$(DESTDIR)$(altosuilibdir)/$(JAR)"; \
$(INSTALL_DATA) "$(JAR)" "$(DESTDIR)$(altosuilibdir)"
$(JAVAROOT):
mkdir -p $(JAVAROOT)
$(JAR): classaltosuilib.stamp $(ICONS)
jar cf $@ $(ICONJAR) -C $(JAVAROOT) .
if STRIP_NONDETERMINISM
$(STRIP_NONDETERMINISM) $@
endif

206
altosuilib/OSXAdapter.java Executable file
View File

@@ -0,0 +1,206 @@
/*
File: OSXAdapter.java
Abstract: Hooks existing preferences/about/quit functionality from an
existing Java app into handlers for the Mac OS X application menu.
Uses a Proxy object to dynamically implement the
com.apple.eawt.ApplicationListener interface and register it with the
com.apple.eawt.Application object. This allows the complete project
to be both built and run on any platform without any stubs or
placeholders. Useful for developers looking to implement Mac OS X
features while supporting multiple platforms with minimal impact.
Version: 2.0
Disclaimer: IMPORTANT: This Apple software is supplied to you by
Apple Inc. ("Apple") in consideration of your agreement to the
following terms, and your use, installation, modification or
redistribution of this Apple software constitutes acceptance of these
terms. If you do not agree with these terms, please do not use,
install, modify or redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc.
may be used to endorse or promote products derived from the Apple
Software without specific prior written permission from Apple. Except
as expressly stated in this notice, no other rights or licenses, express
or implied, are granted by Apple herein, including but not limited to
any patent rights that may be infringed by your derivative works or by
other works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright © 2003-2007 Apple, Inc., All Rights Reserved
*/
package org.altusmetrum.altosuilib_14;
import java.lang.reflect.*;
import java.util.HashMap;
public class OSXAdapter implements InvocationHandler {
protected Object targetObject;
protected Method targetMethod;
protected String proxySignature;
static Object macOSXApplication;
// Pass this method an Object and Method equipped to perform application shutdown logic
// The method passed should return a boolean stating whether or not the quit should occur
public static void setQuitHandler(Object target, Method quitHandler) {
setHandler(new OSXAdapter("handleQuit", target, quitHandler));
}
// Pass this method an Object and Method equipped to display application info
// They will be called when the About menu item is selected from the application menu
public static void setAboutHandler(Object target, Method aboutHandler) {
boolean enableAboutMenu = (target != null && aboutHandler != null);
if (enableAboutMenu) {
setHandler(new OSXAdapter("handleAbout", target, aboutHandler));
}
// If we're setting a handler, enable the About menu item by calling
// com.apple.eawt.Application reflectively
try {
Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class });
enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) });
} catch (Exception ex) {
System.err.println("OSXAdapter could not access the About Menu");
ex.printStackTrace();
}
}
// Pass this method an Object and a Method equipped to display application options
// They will be called when the Preferences menu item is selected from the application menu
public static void setPreferencesHandler(Object target, Method prefsHandler) {
boolean enablePrefsMenu = (target != null && prefsHandler != null);
if (enablePrefsMenu) {
setHandler(new OSXAdapter("handlePreferences", target, prefsHandler));
}
// If we're setting a handler, enable the Preferences menu item by calling
// com.apple.eawt.Application reflectively
try {
Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class });
enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) });
} catch (Exception ex) {
System.err.println("OSXAdapter could not access the About Menu");
ex.printStackTrace();
}
}
// Pass this method an Object and a Method equipped to handle document events from the Finder
// Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the
// application bundle's Info.plist
public static void setFileHandler(Object target, Method fileHandler) {
setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) {
// Override OSXAdapter.callTarget to send information on the
// file to be opened
public boolean callTarget(Object appleEvent) {
if (appleEvent != null) {
try {
Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null);
String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null);
this.targetMethod.invoke(this.targetObject, new Object[] { filename });
} catch (Exception ex) {
}
}
return true;
}
});
}
// setHandler creates a Proxy object from the passed OSXAdapter and adds it as an ApplicationListener
public static void setHandler(OSXAdapter adapter) {
try {
Class applicationClass = Class.forName("com.apple.eawt.Application");
if (macOSXApplication == null) {
macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null);
}
Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener");
Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass });
// Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener
Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter);
addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy });
} catch (ClassNotFoundException cnfe) {
System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")");
} catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods
System.err.println("Mac OS X Adapter could not talk to EAWT:");
ex.printStackTrace();
}
}
// Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for example),
// the Object that will ultimately perform the task, and the Method to be called on that Object
protected OSXAdapter(String proxySignature, Object target, Method handler) {
this.proxySignature = proxySignature;
this.targetObject = target;
this.targetMethod = handler;
}
// Override this method to perform any operations on the event
// that comes with the various callbacks
// See setFileHandler above for an example
public boolean callTarget(Object appleEvent) throws InvocationTargetException, IllegalAccessException {
Object result = targetMethod.invoke(targetObject, (Object[])null);
if (result == null) {
return true;
}
return Boolean.valueOf(result.toString()).booleanValue();
}
// InvocationHandler implementation
// This is the entry point for our proxy object; it is called every time an ApplicationListener method is invoked
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
if (isCorrectMethod(method, args)) {
boolean handled = callTarget(args[0]);
setApplicationEventHandled(args[0], handled);
}
// All of the ApplicationListener methods are void; return null regardless of what happens
return null;
}
// Compare the method that was called to the intended method when the OSXAdapter instance was created
// (e.g. handleAbout, handleQuit, handleOpenFile, etc.)
protected boolean isCorrectMethod(Method method, Object[] args) {
return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1);
}
// It is important to mark the ApplicationEvent as handled and cancel the default behavior
// This method checks for a boolean result from the proxy method and sets the event accordingly
protected void setApplicationEventHandled(Object event, boolean handled) {
if (event != null) {
try {
Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class });
// If the target method returns a boolean, use that as a hint
setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) });
} catch (Exception ex) {
System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event);
ex.printStackTrace();
}
}
}
}