diff options
author | Bdale Garbee <bdale@gag.com> | 2010-08-27 03:08:53 -0600 |
---|---|---|
committer | Bdale Garbee <bdale@gag.com> | 2010-08-27 03:08:53 -0600 |
commit | c443f43f8dee6e0fcbcecf9d09e948fd928b7af4 (patch) | |
tree | 653fe6ba91e165aaf8a6b4eef17602c5f1bd62a9 /ao-tools | |
parent | 295043112ccde35092945c286596f9045ee6fa05 (diff) | |
parent | 2923cf5057f9cef110dd547d8677ea5b60e00796 (diff) |
Merge branch 'new-packet-format' of ssh://git.gag.com/scm/git/fw/altos into new-package-format
Diffstat (limited to 'ao-tools')
-rw-r--r-- | ao-tools/altosui/Altos.java | 86 | ||||
-rw-r--r-- | ao-tools/altosui/AltosCSV.java | 138 | ||||
-rw-r--r-- | ao-tools/altosui/AltosCSVUI.java | 83 | ||||
-rw-r--r-- | ao-tools/altosui/AltosDebug.java | 46 | ||||
-rw-r--r-- | ao-tools/altosui/AltosEepromReader.java | 11 | ||||
-rw-r--r-- | ao-tools/altosui/AltosFlash.java | 3 | ||||
-rw-r--r-- | ao-tools/altosui/AltosFlashUI.java | 26 | ||||
-rw-r--r-- | ao-tools/altosui/AltosGPS.java | 61 | ||||
-rw-r--r-- | ao-tools/altosui/AltosGreatCircle.java | 15 | ||||
-rw-r--r-- | ao-tools/altosui/AltosLogfileChooser.java | 83 | ||||
-rw-r--r-- | ao-tools/altosui/AltosParse.java | 10 | ||||
-rw-r--r-- | ao-tools/altosui/AltosReader.java | 2 | ||||
-rw-r--r-- | ao-tools/altosui/AltosRomconfig.java | 49 | ||||
-rw-r--r-- | ao-tools/altosui/AltosRomconfigUI.java | 21 | ||||
-rw-r--r-- | ao-tools/altosui/AltosSerial.java | 32 | ||||
-rw-r--r-- | ao-tools/altosui/AltosState.java | 5 | ||||
-rw-r--r-- | ao-tools/altosui/AltosTelemetry.java | 34 | ||||
-rw-r--r-- | ao-tools/altosui/AltosTelemetryReader.java | 25 | ||||
-rw-r--r-- | ao-tools/altosui/AltosUI.java | 114 | ||||
-rw-r--r-- | ao-tools/altosui/Makefile | 2 |
20 files changed, 693 insertions, 153 deletions
diff --git a/ao-tools/altosui/Altos.java b/ao-tools/altosui/Altos.java index 53359e23..07bd01ae 100644 --- a/ao-tools/altosui/Altos.java +++ b/ao-tools/altosui/Altos.java @@ -114,4 +114,90 @@ public class Altos { static final int AO_GPS_DATE_VALID = (1 << 6); static final int AO_GPS_NUM_SAT_SHIFT = 0; static final int AO_GPS_NUM_SAT_MASK = 0xf; + + static boolean isspace(int c) { + switch (c) { + case ' ': + case '\t': + return true; + } + return false; + } + + static boolean ishex(int c) { + if ('0' <= c && c <= '9') + return true; + if ('a' <= c && c <= 'f') + return true; + if ('A' <= c && c <= 'F') + return true; + return false; + } + + static boolean ishex(String s) { + for (int i = 0; i < s.length(); i++) + if (!ishex(s.charAt(i))) + return false; + return true; + } + + static int fromhex(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + if ('a' <= c && c <= 'f') + return c - 'a' + 10; + if ('A' <= c && c <= 'F') + return c - 'A' + 10; + return -1; + } + + static int fromhex(String s) throws NumberFormatException { + int c, v = 0; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (!ishex(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid hex \"%s\"", s)); + return v; + } + v = v * 16 + fromhex(c); + } + return v; + } + + static boolean isdec(int c) { + if ('0' <= c && c <= '9') + return true; + return false; + } + + static boolean isdec(String s) { + for (int i = 0; i < s.length(); i++) + if (!isdec(s.charAt(i))) + return false; + return true; + } + + static int fromdec(int c) { + if ('0' <= c && c <= '9') + return c - '0'; + return -1; + } + + static int fromdec(String s) throws NumberFormatException { + int c, v = 0; + int sign = 1; + for (int i = 0; i < s.length(); i++) { + c = s.charAt(i); + if (i == 0 && c == '-') { + sign = -1; + } else if (!isdec(c)) { + if (i == 0) + throw new NumberFormatException(String.format("invalid number \"%s\"", s)); + return v; + } else + v = v * 10 + fromdec(c); + } + return v * sign; + } } diff --git a/ao-tools/altosui/AltosCSV.java b/ao-tools/altosui/AltosCSV.java index 24936758..db50e7a2 100644 --- a/ao-tools/altosui/AltosCSV.java +++ b/ao-tools/altosui/AltosCSV.java @@ -19,12 +19,20 @@ package altosui; import java.lang.*; import java.io.*; +import java.text.*; +import java.util.*; + import altosui.AltosRecord; +import altosui.AltosReader; public class AltosCSV { - File name; - PrintStream out; - boolean header_written; + File name; + PrintStream out; + boolean header_written; + boolean seen_boost; + int boost_tick; + LinkedList<AltosRecord> pad_records; + AltosState state; static final int ALTOS_CSV_VERSION = 1; @@ -36,6 +44,7 @@ public class AltosCSV { * flight number * callsign * time (seconds since boost) + * rssi * * Flight status * state @@ -45,6 +54,7 @@ public class AltosCSV { * acceleration (m/s²) * pressure (mBar) * altitude (m) + * height (m) * accelerometer speed (m/s) * barometer speed (m/s) * temp (°C) @@ -65,6 +75,8 @@ public class AltosCSV { * hour (0-23) * minute (0-59) * second (0-59) + * from_pad_dist (m) + * from_pad_dir (deg true) * * GPS Sat data * hdop @@ -72,12 +84,14 @@ public class AltosCSV { */ void write_general_header() { - out.printf("version serial flight call time"); + out.printf("version serial flight call time rssi"); } void write_general(AltosRecord record) { - out.printf("%s,%d,%d,%s,%d", - record.version, record.serial, record.flight, record.callsign, record.tick); + out.printf("%s,%d,%d,%s,%8.2f,%4d", + record.version, record.serial, record.flight, record.callsign, + (double) record.time, + record.rssi); } void write_flight_header() { @@ -85,31 +99,135 @@ public class AltosCSV { } void write_flight(AltosRecord record) { - out.printf("%d,%s", record.state, record.state()); + out.printf("%d,%8s", record.state, record.state()); + } + + void write_basic_header() { + out.printf("acceleration pressure altitude height accel_speed baro_speed temperature battery_voltage drogue_voltage main_voltage"); + } + + void write_basic(AltosRecord record) { + out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f", + record.acceleration(), + record.pressure(), + record.altitude(), + record.height(), + record.accel_speed(), + state.baro_speed, + record.temperature(), + record.battery_voltage(), + record.drogue_voltage(), + record.main_voltage()); + } + + void write_gps_header() { + out.printf("connected locked nsat latitude longitude altitude year month day hour minute second pad_dist pad_dir"); + } + + void write_gps(AltosRecord record) { + AltosGPS gps = record.gps; + if (gps == null) + gps = new AltosGPS(); + + AltosGreatCircle from_pad = state.from_pad; + if (from_pad == null) + from_pad = new AltosGreatCircle(); + + out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%4.0f", + gps.connected?1:0, + gps.locked?1:0, + gps.nsat, + gps.lat, + gps.lon, + gps.alt, + gps.year, + gps.month, + gps.day, + gps.hour, + gps.minute, + gps.second, + from_pad.distance, + from_pad.bearing); } void write_header() { out.printf("# "); write_general_header(); out.printf(" "); write_flight_header(); + out.printf(" "); write_basic_header(); + out.printf(" "); write_gps_header(); + out.printf ("\n"); + } + + void write_one(AltosRecord record) { + state = new AltosState(record, state); + write_general(record); out.printf(","); + write_flight(record); out.printf(","); + write_basic(record); out.printf(","); + write_gps(record); out.printf ("\n"); } + void flush_pad() { + while (!pad_records.isEmpty()) { + write_one (pad_records.remove()); + } + } + public void write(AltosRecord record) { if (!header_written) { write_header(); header_written = true; } - write_general(record); out.printf(","); - write_flight(record); - out.printf ("\n"); + if (!seen_boost) { + if (record.state >= Altos.ao_flight_boost) { + seen_boost = true; + boost_tick = record.tick; + flush_pad(); + } + } + if (seen_boost) + write_one(record); + else + pad_records.add(record); } public PrintStream out() { return out; } + public void close() { + if (!pad_records.isEmpty()) { + boost_tick = pad_records.element().tick; + flush_pad(); + } + out.close(); + } + + public void write(AltosReader reader) { + AltosRecord record; + + reader.write_comments(out()); + try { + for (;;) { + record = reader.read(); + if (record == null) + break; + write(record); + } + } catch (IOException ie) { + System.out.printf("IOException\n"); + } catch (ParseException pe) { + System.out.printf("ParseException %s\n", pe.getMessage()); + } + } + public AltosCSV(File in_name) throws FileNotFoundException { name = in_name; out = new PrintStream(name); + pad_records = new LinkedList<AltosRecord>(); + } + + public AltosCSV(String in_string) throws FileNotFoundException { + this(new File(in_string)); } } diff --git a/ao-tools/altosui/AltosCSVUI.java b/ao-tools/altosui/AltosCSVUI.java new file mode 100644 index 00000000..2d812361 --- /dev/null +++ b/ao-tools/altosui/AltosCSVUI.java @@ -0,0 +1,83 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.LinkedBlockingQueue; + +import altosui.AltosLogfileChooser; +import altosui.AltosCSV; + +public class AltosCSVUI + extends JDialog + implements Runnable, ActionListener +{ + JFrame frame; + Thread thread; + AltosReader reader; + AltosCSV writer; + + public void run() { + AltosLogfileChooser chooser; + + chooser = new AltosLogfileChooser(frame); + reader = chooser.runDialog(); + if (reader == null) + return; + JFileChooser csv_chooser; + + File file = chooser.file(); + String path = file.getPath(); + int dot = path.lastIndexOf("."); + if (dot >= 0) + path = path.substring(0,dot); + path = path.concat(".csv"); + csv_chooser = new JFileChooser(path); + int ret = csv_chooser.showSaveDialog(frame); + if (ret == JFileChooser.APPROVE_OPTION) { + try { + writer = new AltosCSV(csv_chooser.getSelectedFile()); + } catch (FileNotFoundException ee) { + JOptionPane.showMessageDialog(frame, + file.getName(), + "Cannot open file", + JOptionPane.ERROR_MESSAGE); + } + writer.write(reader); + reader.close(); + writer.close(); + } + } + + public void actionPerformed(ActionEvent e) { + } + + public AltosCSVUI(JFrame in_frame) { + frame = in_frame; + thread = new Thread(this); + thread.start(); + } +} diff --git a/ao-tools/altosui/AltosDebug.java b/ao-tools/altosui/AltosDebug.java index 83ea5bcb..ca2e5a90 100644 --- a/ao-tools/altosui/AltosDebug.java +++ b/ao-tools/altosui/AltosDebug.java @@ -58,46 +58,12 @@ public class AltosDebug extends AltosSerial { public static final byte GET_CHIP_ID = 0x68; - static boolean ishex(int c) { - if ('0' <= c && c <= '9') - return true; - if ('a' <= c && c <= 'f') - return true; - if ('A' <= c && c <= 'F') - return true; - return false; - } - - static boolean ishex(String s) { - for (int i = 0; i < s.length(); i++) - if (!ishex(s.charAt(i))) - return false; - return true; - } - static boolean isspace(int c) { - switch (c) { - case ' ': - case '\t': - return true; - } - return false; - } - - static int fromhex(int c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; - } - boolean debug_mode; void ensure_debug_mode() { if (!debug_mode) { - printf("D\n"); + printf("m 0\nD\n"); + flush_reply(); debug_mode = true; } } @@ -144,14 +110,14 @@ public class AltosDebug extends AltosSerial { int start = 0; while (i < length) { String line = get_reply().trim(); - if (!ishex(line) || line.length() % 2 != 0) + if (!Altos.ishex(line) || line.length() % 2 != 0) throw new IOException( String.format ("Invalid reply \"%s\"", line)); int this_time = line.length() / 2; for (int j = 0; j < this_time; j++) - data[start + j] = (byte) ((fromhex(line.charAt(j*2)) << 4) + - fromhex(line.charAt(j*2+1))); + data[start + j] = (byte) ((Altos.fromhex(line.charAt(j*2)) << 4) + + Altos.fromhex(line.charAt(j*2+1))); start += this_time; i += this_time; } @@ -198,7 +164,7 @@ public class AltosDebug extends AltosSerial { String line = get_reply().trim(); String tokens[] = line.split("\\s+"); for (int j = 0; j < tokens.length; j++) { - if (!ishex(tokens[j]) || + if (!Altos.ishex(tokens[j]) || tokens[j].length() != 2) throw new IOException( String.format diff --git a/ao-tools/altosui/AltosEepromReader.java b/ao-tools/altosui/AltosEepromReader.java index c29fd90b..3f2d4c62 100644 --- a/ao-tools/altosui/AltosEepromReader.java +++ b/ao-tools/altosui/AltosEepromReader.java @@ -133,8 +133,6 @@ public class AltosEepromReader extends AltosReader { ground_pres += state.pres; state.ground_pres = (int) (ground_pres / n_pad_samples); state.flight_pres = state.ground_pres; - System.out.printf("ground pressure %d altitude %f\n", - record.b, state.altitude()); ground_accel += state.accel; state.ground_accel = (int) (ground_accel / n_pad_samples); state.flight_accel = state.ground_accel; @@ -156,7 +154,6 @@ public class AltosEepromReader extends AltosReader { seen |= seen_deploy; break; case Altos.AO_LOG_STATE: - System.out.printf("state %d\n", record.a); state.state = record.a; break; case Altos.AO_LOG_GPS_TIME: @@ -218,6 +215,7 @@ public class AltosEepromReader extends AltosReader { case Altos.AO_LOG_PRODUCT: break; case Altos.AO_LOG_SERIAL_NUMBER: + state.serial = record.a; break; case Altos.AO_LOG_SOFTWARE_VERSION: break; @@ -228,6 +226,7 @@ public class AltosEepromReader extends AltosReader { public void write_comments(PrintStream out) { Iterator<AltosOrderedRecord> iterator = records.iterator(); + out.printf("# Comments\n"); while (iterator.hasNext()) { AltosOrderedRecord record = iterator.next(); switch (record.cmd) { @@ -250,7 +249,7 @@ public class AltosEepromReader extends AltosReader { out.printf ("# Accel cal: %d %d\n", record.a, record.b); break; case Altos.AO_LOG_RADIO_CAL: - out.printf ("# Radio cal: %d %d\n", record.a); + out.printf ("# Radio cal: %d\n", record.a); break; case Altos.AO_LOG_MANUFACTURER: out.printf ("# Manufacturer: %s\n", record.data); @@ -296,10 +295,10 @@ public class AltosEepromReader extends AltosReader { break; tick = record.tick; if (!saw_boost && record.cmd == Altos.AO_LOG_STATE && - record.a == Altos.ao_flight_boost) + record.a >= Altos.ao_flight_boost) { saw_boost = true; - boost_tick = state.tick; + boost_tick = tick; } records.add(record); } diff --git a/ao-tools/altosui/AltosFlash.java b/ao-tools/altosui/AltosFlash.java index 0f92d6e7..b7018555 100644 --- a/ao-tools/altosui/AltosFlash.java +++ b/ao-tools/altosui/AltosFlash.java @@ -247,6 +247,7 @@ public class AltosFlash { int flash_addr = image.address; int image_start = 0; + action("start", 0); action(0, image.data.length); while (remain > 0 && !aborted) { int this_time = remain; @@ -314,6 +315,8 @@ public class AltosFlash { } public AltosRomconfig romconfig() { + if (!check_rom_config()) + return null; return rom_config; } diff --git a/ao-tools/altosui/AltosFlashUI.java b/ao-tools/altosui/AltosFlashUI.java index 0c2041e3..73a97a6b 100644 --- a/ao-tools/altosui/AltosFlashUI.java +++ b/ao-tools/altosui/AltosFlashUI.java @@ -57,8 +57,10 @@ public class AltosFlashUI } else { String cmd = e.getActionCommand(); if (cmd.equals("done")) - dispose(); - else { + ; + else if (cmd.equals("start")) { + setVisible(true); + } else { pbar.setValue(e.getID()); pbar.setString(cmd); } @@ -70,14 +72,15 @@ public class AltosFlashUI flash.addActionListener(this); try { flash.open(); - if (!flash.check_rom_config()) { - AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); - romconfig_ui.showDialog(); - AltosRomconfig romconfig = romconfig_ui.romconfig(); - if (romconfig == null) - return; - flash.set_romconfig(romconfig); - } + AltosRomconfigUI romconfig_ui = new AltosRomconfigUI (frame); + + romconfig_ui.set(flash.romconfig()); + romconfig_ui.showDialog(); + + AltosRomconfig romconfig = romconfig_ui.romconfig(); + if (romconfig == null || !romconfig.valid()) + return; + flash.set_romconfig(romconfig); serial_value.setText(String.format("%d", flash.romconfig().serial_number)); file_value.setText(file.toString()); @@ -88,15 +91,14 @@ public class AltosFlashUI "Cannot open image", file.toString(), JOptionPane.ERROR_MESSAGE); - return; } catch (IOException e) { JOptionPane.showMessageDialog(frame, e.getMessage(), file.toString(), JOptionPane.ERROR_MESSAGE); - return; } catch (InterruptedException ie) { } + dispose(); } public void abort() { diff --git a/ao-tools/altosui/AltosGPS.java b/ao-tools/altosui/AltosGPS.java index b3ee67e8..acb6fb2c 100644 --- a/ao-tools/altosui/AltosGPS.java +++ b/ao-tools/altosui/AltosGPS.java @@ -52,14 +52,16 @@ public class AltosGPS { AltosGPSSat[] cc_gps_sat; /* tracking data */ - void ParseGPSTime(String date, String time) throws ParseException { + void ParseGPSDate(String date) throws ParseException { String[] ymd = date.split("-"); if (ymd.length != 3) throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0); year = AltosParse.parse_int(ymd[0]); month = AltosParse.parse_int(ymd[1]); day = AltosParse.parse_int(ymd[2]); + } + void ParseGPSTime(String time) throws ParseException { String[] hms = time.split(":"); if (hms.length != 3) throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0); @@ -73,7 +75,7 @@ public class AltosGPS { hour = minute = second = 0; } - public AltosGPS(String[] words, int i) throws ParseException { + public AltosGPS(String[] words, int i, int version) throws ParseException { AltosParse.word(words[i++], "GPS"); nsat = AltosParse.parse_int(words[i++]); AltosParse.word(words[i++], "sat"); @@ -92,32 +94,44 @@ public class AltosGPS { locked = true; connected = true; - ParseGPSTime(words[i], words[i+1]); i += 2; + if (version > 1) + ParseGPSDate(words[i++]); + else + year = month = day = 0; + ParseGPSTime(words[i++]); lat = AltosParse.parse_coord(words[i++]); lon = AltosParse.parse_coord(words[i++]); - alt = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "m")); - ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); - course = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "°")); - climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); - hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); - h_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(herr)")); - v_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(verr)")); + alt = AltosParse.parse_int(words[i++]); + if (version > 1 || (i < words.length && !words[i].equals("SAT"))) { + ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)")); + course = AltosParse.parse_int(words[i++]); + climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)")); + hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)")); + h_error = AltosParse.parse_int(words[i++]); + v_error = AltosParse.parse_int(words[i++]); + } } else { i++; } - AltosParse.word(words[i++], "SAT"); - int tracking_channels = 0; - if (words[i].equals("not-connected")) - tracking_channels = 0; - else - tracking_channels = AltosParse.parse_int(words[i]); - i++; - cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels]; - for (int chan = 0; chan < tracking_channels; chan++) { - cc_gps_sat[chan] = new AltosGPS.AltosGPSSat(); - cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); - cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); - } + if (i < words.length) { + AltosParse.word(words[i++], "SAT"); + int tracking_channels = 0; + if (words[i].equals("not-connected")) + tracking_channels = 0; + else + tracking_channels = AltosParse.parse_int(words[i]); + i++; + cc_gps_sat = new AltosGPS.AltosGPSSat[tracking_channels]; + for (int chan = 0; chan < tracking_channels; chan++) { + cc_gps_sat[chan] = new AltosGPS.AltosGPSSat(); + cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]); + /* Older versions included SiRF status bits */ + if (version < 2) + i++; + cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]); + } + } else + cc_gps_sat = new AltosGPS.AltosGPSSat[0]; } public void set_latitude(int in_lat) { @@ -172,6 +186,7 @@ public class AltosGPS { nsat = old.nsat; locked = old.locked; connected = old.connected; + date_valid = old.date_valid; lat = old.lat; /* degrees (+N -S) */ lon = old.lon; /* degrees (+E -W) */ alt = old.alt; /* m */ diff --git a/ao-tools/altosui/AltosGreatCircle.java b/ao-tools/altosui/AltosGreatCircle.java index 878da03e..07c02c16 100644 --- a/ao-tools/altosui/AltosGreatCircle.java +++ b/ao-tools/altosui/AltosGreatCircle.java @@ -17,6 +17,8 @@ package altosui; +import altosui.AltosGPS; + import java.lang.Math; public class AltosGreatCircle { @@ -28,8 +30,8 @@ public class AltosGreatCircle { static final double rad = Math.PI / 180; static final double earth_radius = 6371.2 * 1000; /* in meters */ - AltosGreatCircle (double start_lat, double start_lon, - double end_lat, double end_lon) + public AltosGreatCircle (double start_lat, double start_lon, + double end_lat, double end_lon) { double lat1 = rad * start_lat; double lon1 = rad * -start_lon; @@ -63,4 +65,13 @@ public class AltosGreatCircle { distance = d * earth_radius; bearing = course * 180/Math.PI; } + + public AltosGreatCircle(AltosGPS start, AltosGPS end) { + this(start.lat, start.lon, end.lat, end.lon); + } + + public AltosGreatCircle() { + distance = 0; + bearing = 0; + } } diff --git a/ao-tools/altosui/AltosLogfileChooser.java b/ao-tools/altosui/AltosLogfileChooser.java new file mode 100644 index 00000000..36b51de6 --- /dev/null +++ b/ao-tools/altosui/AltosLogfileChooser.java @@ -0,0 +1,83 @@ +/* + * Copyright © 2010 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; + +import altosui.AltosPreferences; +import altosui.AltosReader; +import altosui.AltosEepromReader; +import altosui.AltosTelemetryReader; + +public class AltosLogfileChooser extends JFileChooser { + JFrame frame; + String filename; + File file; + + public String filename() { + return filename; + } + + public File file() { + return file; + } + + public AltosReader runDialog() { + int ret; + + ret = showOpenDialog(frame); + if (ret == APPROVE_OPTION) { + file = getSelectedFile(); + if (file == null) + return null; + filename = file.getName(); + try { + FileInputStream in; + + in = new FileInputStream(file); + if (filename.endsWith("eeprom")) + return new AltosEepromReader(in); + else + return new AltosTelemetryReader(in); + } catch (FileNotFoundException fe) { + JOptionPane.showMessageDialog(frame, + filename, + "Cannot open file", + JOptionPane.ERROR_MESSAGE); + } + } + return null; + } + + public AltosLogfileChooser(JFrame in_frame) { + frame = in_frame; + setDialogTitle("Select Flight Record File"); + setFileFilter(new FileNameExtensionFilter("Flight data file", + "eeprom", + "telem")); + setCurrentDirectory(AltosPreferences.logdir()); + } +}
\ No newline at end of file diff --git a/ao-tools/altosui/AltosParse.java b/ao-tools/altosui/AltosParse.java index a60dc694..4d82de78 100644 --- a/ao-tools/altosui/AltosParse.java +++ b/ao-tools/altosui/AltosParse.java @@ -20,10 +20,16 @@ package altosui; import java.text.*; import java.lang.*; +import altosui.Altos; + public class AltosParse { + static boolean isdigit(char c) { + return '0' <= c && c <= '9'; + } + static int parse_int(String v) throws ParseException { try { - return Integer.parseInt(v); + return Altos.fromdec(v); } catch (NumberFormatException e) { throw new ParseException("error parsing int " + v, 0); } @@ -31,7 +37,7 @@ public class AltosParse { static int parse_hex(String v) throws ParseException { try { - return Integer.parseInt(v, 16); + return Altos.fromhex(v); } catch (NumberFormatException e) { throw new ParseException("error parsing hex " + v, 0); } diff --git a/ao-tools/altosui/AltosReader.java b/ao-tools/altosui/AltosReader.java index 81779e2b..5be8795d 100644 --- a/ao-tools/altosui/AltosReader.java +++ b/ao-tools/altosui/AltosReader.java @@ -25,4 +25,6 @@ import altosui.AltosRecord; public class AltosReader { public AltosRecord read() throws IOException, ParseException { return null; } + public void close() { } + public void write_comments(PrintStream out) { } } diff --git a/ao-tools/altosui/AltosRomconfig.java b/ao-tools/altosui/AltosRomconfig.java index 844da7c4..8c9cb27a 100644 --- a/ao-tools/altosui/AltosRomconfig.java +++ b/ao-tools/altosui/AltosRomconfig.java @@ -47,14 +47,57 @@ public class AltosRomconfig { } } + static void put_string(String value, byte[] bytes, int start) { + for (int i = 0; i < value.length(); i++) + bytes[start + i] = (byte) value.charAt(i); + } + + static final int AO_USB_DESC_STRING = 3; + + static void put_usb_serial(int value, byte[] bytes, int start) { + int offset = start + 0xa; + int string_num = 0; + System.out.printf("Put usb serial %d\n", value); + + while (offset < bytes.length && bytes[offset] != 0) { + if (bytes[offset + 1] == AO_USB_DESC_STRING) { + ++string_num; + if (string_num == 4) + break; + } + offset += ((int) bytes[offset]) & 0xff; + } + System.out.printf("offset %d content %d\n", + offset, bytes[offset]); + if (offset >= bytes.length || bytes[offset] == 0) + return; + int len = ((((int) bytes[offset]) & 0xff) - 2) / 2; + String fmt = String.format("%%0%dd", len); + System.out.printf("existing serial length %d format %s\n", len, fmt); + + String s = String.format(fmt, value); + if (s.length() != len) { + System.out.printf("weird usb length issue %s isn't %d\n", + s, len); + return; + } + for (int i = 0; i < len; i++) { + bytes[offset + 2 + i*2] = (byte) s.charAt(i); + bytes[offset + 2 + i*2+1] = 0; + } + } + public AltosRomconfig(byte[] bytes, int offset) { version = get_int(bytes, offset + 0, 2); check = get_int(bytes, offset + 2, 2); + System.out.printf("version %d check %d\n", version, check); if (check == (~version & 0xffff)) { switch (version) { + case 2: case 1: serial_number = get_int(bytes, offset + 4, 2); radio_calibration = get_int(bytes, offset + 6, 4); + System.out.printf("serial %d cal %d\n", serial_number, radio_calibration); valid = true; break; } @@ -77,6 +120,8 @@ public class AltosRomconfig { throw new IOException("image does not contain existing rom config"); switch (existing.version) { + case 2: + put_usb_serial(serial_number, bytes, offset); case 1: put_int(serial_number, bytes, offset + 4, 2); put_int(radio_calibration, bytes, offset + 6, 4); @@ -86,7 +131,9 @@ public class AltosRomconfig { public void write (AltosHexfile hexfile) throws IOException { write(hexfile.data, 0xa0 - hexfile.address); - new AltosRomconfig(hexfile); + AltosRomconfig check = new AltosRomconfig(hexfile); + if (!check.valid()) + throw new IOException("writing new rom config failed\n"); } public AltosRomconfig(int in_serial_number, int in_radio_calibration) { diff --git a/ao-tools/altosui/AltosRomconfigUI.java b/ao-tools/altosui/AltosRomconfigUI.java index 21c34ef4..bc511865 100644 --- a/ao-tools/altosui/AltosRomconfigUI.java +++ b/ao-tools/altosui/AltosRomconfigUI.java @@ -143,12 +143,31 @@ public class AltosRomconfigUI return Integer.parseInt(serial_value.getText()); } + void set_serial(int serial) { + serial_value.setText(String.format("%d", serial)); + } + int radio_calibration() { return Integer.parseInt(radio_calibration_value.getText()); } + void set_radio_calibration(int calibration) { + 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); + } + } + public AltosRomconfig romconfig() { - return new AltosRomconfig(serial(), radio_calibration()); + try { + return new AltosRomconfig(serial(), radio_calibration()); + } catch (NumberFormatException ne) { + return null; + } } public AltosRomconfig showDialog() { diff --git a/ao-tools/altosui/AltosSerial.java b/ao-tools/altosui/AltosSerial.java index d02e25a9..5b47960f 100644 --- a/ao-tools/altosui/AltosSerial.java +++ b/ao-tools/altosui/AltosSerial.java @@ -45,6 +45,8 @@ public class AltosSerial implements Runnable { LinkedBlockingQueue<String> reply_queue; Thread input_thread; String line; + byte[] line_bytes; + int line_count; public void run () { int c; @@ -60,18 +62,36 @@ public class AltosSerial implements Runnable { continue; synchronized(this) { if (c == '\n') { - if (line != "") { + if (line_count != 0) { + try { + line = new String(line_bytes, 0, line_count, "UTF-8"); + } catch (UnsupportedEncodingException ue) { + line = ""; + for (int i = 0; i < line_count; i++) + line = line + line_bytes[i]; + } if (line.startsWith("VERSION")) { for (int e = 0; e < monitors.size(); e++) { LinkedBlockingQueue<String> q = monitors.get(e); q.put(line); } - } else + } else { +// System.out.printf("GOT: %s\n", line); reply_queue.put(line); + } + line_count = 0; line = ""; } } else { - line = line + (char) c; + if (line_bytes == null) { + line_bytes = new byte[256]; + } else if (line_count == line_bytes.length) { + byte[] new_line_bytes = new byte[line_count * 2]; + System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count); + line_bytes = new_line_bytes; + } + line_bytes[line_count] = (byte) c; + line_count++; } } } @@ -80,6 +100,11 @@ public class AltosSerial implements Runnable { } public void flush_reply() { + libaltos.altos_flush(altos); + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } reply_queue.clear(); } @@ -132,6 +157,7 @@ public class AltosSerial implements Runnable { } public void print(String data) { +//h System.out.printf("\"%s\" ", data); for (int i = 0; i < data.length(); i++) putc(data.charAt(i)); } diff --git a/ao-tools/altosui/AltosState.java b/ao-tools/altosui/AltosState.java index deeb4c77..c13dfe68 100644 --- a/ao-tools/altosui/AltosState.java +++ b/ao-tools/altosui/AltosState.java @@ -124,11 +124,6 @@ public class AltosState { } if (state == Altos.ao_flight_pad) { - if (data.gps == null) - System.out.printf("on pad, gps null\n"); - else - System.out.printf ("on pad gps lat %f lon %f locked %d nsat %d\n", - data.gps.lat, data.gps.lon, data.gps.locked ? 1 : 0, data.gps.nsat); if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) { npad++; if (npad > 1) { diff --git a/ao-tools/altosui/AltosTelemetry.java b/ao-tools/altosui/AltosTelemetry.java index af29b8c0..bc62690b 100644 --- a/ao-tools/altosui/AltosTelemetry.java +++ b/ao-tools/altosui/AltosTelemetry.java @@ -57,8 +57,12 @@ public class AltosTelemetry extends AltosRecord { String[] words = line.split("\\s+"); int i = 0; - AltosParse.word (words[i++], "VERSION"); - version = AltosParse.parse_int(words[i++]); + if (words[i].equals("CALL")) { + version = 0; + } else { + AltosParse.word (words[i++], "VERSION"); + version = AltosParse.parse_int(words[i++]); + } AltosParse.word (words[i++], "CALL"); callsign = words[i++]; @@ -66,12 +70,19 @@ public class AltosTelemetry extends AltosRecord { AltosParse.word (words[i++], "SERIAL"); serial = AltosParse.parse_int(words[i++]); - AltosParse.word (words[i++], "FLIGHT"); - flight = AltosParse.parse_int(words[i++]); + if (version >= 2) { + AltosParse.word (words[i++], "FLIGHT"); + flight = AltosParse.parse_int(words[i++]); + } else + flight = 0; AltosParse.word(words[i++], "RSSI"); rssi = AltosParse.parse_int(words[i++]); + /* Older telemetry data had mis-computed RSSI value */ + if (version <= 2) + rssi = (rssi + 74) / 2 - 74; + AltosParse.word(words[i++], "STATUS"); status = AltosParse.parse_hex(words[i++]); @@ -113,12 +124,17 @@ public class AltosTelemetry extends AltosRecord { AltosParse.word(words[i++], "gp:"); ground_pres = AltosParse.parse_int(words[i++]); - AltosParse.word(words[i++], "a+:"); - accel_plus_g = AltosParse.parse_int(words[i++]); + if (version >= 1) { + AltosParse.word(words[i++], "a+:"); + accel_plus_g = AltosParse.parse_int(words[i++]); - AltosParse.word(words[i++], "a-:"); - accel_minus_g = AltosParse.parse_int(words[i++]); + AltosParse.word(words[i++], "a-:"); + accel_minus_g = AltosParse.parse_int(words[i++]); + } else { + accel_plus_g = ground_accel; + accel_minus_g = ground_accel + 530; + } - gps = new AltosGPS(words, i); + gps = new AltosGPS(words, i, version); } } diff --git a/ao-tools/altosui/AltosTelemetryReader.java b/ao-tools/altosui/AltosTelemetryReader.java index f1f6788c..a3402f9c 100644 --- a/ao-tools/altosui/AltosTelemetryReader.java +++ b/ao-tools/altosui/AltosTelemetryReader.java @@ -47,20 +47,25 @@ public class AltosTelemetryReader extends AltosReader { try { for (;;) { String line = AltosRecord.gets(input); - if (line == null) + if (line == null) { break; - AltosTelemetry record = new AltosTelemetry(line); - if (record == null) - break; - if (!saw_boost && record.state >= Altos.ao_flight_boost) - { - saw_boost = true; - boost_tick = record.tick; } - records.add(record); + try { + AltosTelemetry record = new AltosTelemetry(line); + if (record == null) + break; + if (!saw_boost && record.state >= Altos.ao_flight_boost) + { + saw_boost = true; + boost_tick = record.tick; + } + records.add(record); + } catch (ParseException pe) { + System.out.printf("parse exception %s\n", pe.getMessage()); + } } } catch (IOException io) { - } catch (ParseException pe) { + System.out.printf("io exception\n"); } record_iterator = records.iterator(); try { diff --git a/ao-tools/altosui/AltosUI.java b/ao-tools/altosui/AltosUI.java index 9fd47ea7..4f3b5dde 100644 --- a/ao-tools/altosui/AltosUI.java +++ b/ao-tools/altosui/AltosUI.java @@ -42,6 +42,8 @@ import altosui.AltosFlightStatusTableModel; import altosui.AltosFlightInfoTableModel; import altosui.AltosChannelMenu; import altosui.AltosFlashUI; +import altosui.AltosLogfileChooser; +import altosui.AltosCSVUI; import libaltosJNI.*; @@ -529,33 +531,12 @@ public class AltosUI extends JFrame { * Replay a flight from telemetry data */ private void Replay() { - JFileChooser logfile_chooser = new JFileChooser(); - - logfile_chooser.setDialogTitle("Select Flight Record File"); - logfile_chooser.setFileFilter(new FileNameExtensionFilter("Flight data file", "eeprom", "telem")); - logfile_chooser.setCurrentDirectory(AltosPreferences.logdir()); - int returnVal = logfile_chooser.showOpenDialog(AltosUI.this); - - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = logfile_chooser.getSelectedFile(); - if (file == null) - System.out.println("No file selected?"); - String filename = file.getName(); - try { - FileInputStream replay = new FileInputStream(file); - DisplayThread thread; - if (filename.endsWith("eeprom")) - thread = new ReplayEepromThread(replay, filename); - else - thread = new ReplayTelemetryThread(replay, filename); - run_display(thread); - } catch (FileNotFoundException ee) { - JOptionPane.showMessageDialog(AltosUI.this, - filename, - "Cannot open telemetry file", - JOptionPane.ERROR_MESSAGE); - } - } + AltosLogfileChooser chooser = new AltosLogfileChooser( + AltosUI.this); + AltosReader reader = chooser.runDialog(); + if (reader != null) + run_display(new ReplayThread(reader, + chooser.filename())); } /* Connect to TeleMetrum, either directly or through @@ -565,6 +546,14 @@ public class AltosUI extends JFrame { new AltosEepromDownload(AltosUI.this); } + /* Load a flight log file and write out a CSV file containing + * all of the data in standard units + */ + + private void ExportData() { + new AltosCSVUI(AltosUI.this); + } + /* Create the AltosUI menus */ private void createMenu() { @@ -603,6 +592,14 @@ public class AltosUI extends JFrame { }); menu.add(item); + item = new JMenuItem("Export Data",KeyEvent.VK_F); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + ExportData(); + } + }); + menu.add(item); + item = new JMenuItem("Quit",KeyEvent.VK_Q); item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK)); @@ -722,8 +719,67 @@ public class AltosUI extends JFrame { this.setJMenuBar(menubar); } + + static String replace_extension(String input, String extension) { + int dot = input.lastIndexOf("."); + if (dot > 0) + input = input.substring(0,dot); + return input.concat(extension); + } + + static AltosReader open_logfile(String filename) { + File file = new File (filename); + try { + FileInputStream in; + + in = new FileInputStream(file); + if (filename.endsWith("eeprom")) + return new AltosEepromReader(in); + else + return new AltosTelemetryReader(in); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static AltosCSV open_csv(String filename) { + File file = new File (filename); + try { + return new AltosCSV(file); + } catch (FileNotFoundException fe) { + System.out.printf("Cannot open '%s'\n", filename); + return null; + } + } + + static void process_file(String input) { + String output = replace_extension(input,".csv"); + if (input.equals(output)) { + System.out.printf("Not processing '%s'\n", input); + return; + } + System.out.printf("Processing \"%s\" to \"%s\"\n", input, output); + AltosReader reader = open_logfile(input); + if (reader == null) + return; + AltosCSV writer = open_csv(output); + if (writer == null) + return; + writer.write(reader); + reader.close(); + writer.close(); + } + public static void main(final String[] args) { - AltosUI altosui = new AltosUI(); - altosui.setVisible(true); + + /* Handle batch-mode */ + if (args.length > 0) { + for (int i = 0; i < args.length; i++) + process_file(args[i]); + } else { + AltosUI altosui = new AltosUI(); + altosui.setVisible(true); + } } }
\ No newline at end of file diff --git a/ao-tools/altosui/Makefile b/ao-tools/altosui/Makefile index d4d3ee7a..d3ecb889 100644 --- a/ao-tools/altosui/Makefile +++ b/ao-tools/altosui/Makefile @@ -8,6 +8,7 @@ CLASSFILES=\ AltosConfigUI.class \ AltosConvert.class \ AltosCSV.class \ + AltosCSVUI.class \ AltosDebug.class \ AltosEepromDownload.class \ AltosEepromMonitor.class \ @@ -22,6 +23,7 @@ CLASSFILES=\ AltosGreatCircle.class \ AltosHexfile.class \ AltosLog.class \ + AltosLogfileChooser.class \ AltosParse.class \ AltosPreferences.class \ AltosRecord.class \ |