Initial Commit - Copy from Altus Metrum AltOS
This commit is contained in:
4
altoslib/.gitignore
vendored
Normal file
4
altoslib/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bin
|
||||
classaltoslib.stamp
|
||||
altoslib*.jar
|
||||
AltosVersion.java
|
||||
52
altoslib/AltosAccel.java
Normal file
52
altoslib/AltosAccel.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosAccel extends AltosUnits {
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.meters_to_feet(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.feet_to_meters(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "ft/s²";
|
||||
return "m/s²";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "feet per second squared";
|
||||
return "meters per second squared";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
return width / 9;
|
||||
}
|
||||
}
|
||||
230
altoslib/AltosAccelCal.java
Normal file
230
altoslib/AltosAccelCal.java
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosAccelCal implements Runnable {
|
||||
|
||||
AltosLink link;
|
||||
AltosAccelCalListener listener;
|
||||
|
||||
boolean remote;
|
||||
boolean close_on_exit;
|
||||
double frequency;
|
||||
String callsign;
|
||||
|
||||
Thread accel_thread;
|
||||
|
||||
AltosConfigData config_data;
|
||||
|
||||
public static final int phase_antenna_up = 0;
|
||||
public static final int phase_antenna_down = 1;
|
||||
|
||||
void start_link() throws InterruptedException, TimeoutException {
|
||||
if (remote) {
|
||||
link.set_radio_frequency(frequency);
|
||||
link.set_callsign(callsign);
|
||||
link.start_remote();
|
||||
} else
|
||||
link.flush_input();
|
||||
}
|
||||
|
||||
boolean stop_link() throws InterruptedException, TimeoutException {
|
||||
if (remote)
|
||||
link.stop_remote();
|
||||
return link.reply_abort;
|
||||
}
|
||||
|
||||
public void set_frequency(double in_frequency) {
|
||||
frequency = in_frequency;
|
||||
link.abort_reply();
|
||||
}
|
||||
|
||||
public void set_callsign(String in_callsign) {
|
||||
callsign = in_callsign;
|
||||
link.abort_reply();
|
||||
}
|
||||
|
||||
public void abort() throws InterruptedException {
|
||||
while (accel_thread.isAlive()) {
|
||||
accel_thread.interrupt();
|
||||
link.abort_reply();
|
||||
Thread.sleep(100);
|
||||
}
|
||||
accel_thread.join();
|
||||
}
|
||||
|
||||
static private final String press_msg = "press a key...";
|
||||
|
||||
private Semaphore ui_signal_semaphore;
|
||||
private boolean ui_signal_reply;
|
||||
|
||||
public void signal(boolean reply) {
|
||||
System.out.printf("Signal cal semaphore %b\n", reply);
|
||||
ui_signal_reply = reply;
|
||||
ui_signal_semaphore.release();
|
||||
}
|
||||
|
||||
private boolean wait_signal() throws InterruptedException {
|
||||
System.out.printf("\twait for cal signal...\n");
|
||||
ui_signal_semaphore.acquire();
|
||||
System.out.printf("\tgot cal signal %b\n", ui_signal_reply);
|
||||
return ui_signal_reply;
|
||||
}
|
||||
|
||||
private boolean wait_press(int timeout) throws InterruptedException {
|
||||
for (;;) {
|
||||
String line = link.get_reply(timeout);
|
||||
if (line == null) {
|
||||
System.out.printf("get_reply timeout\n");
|
||||
return false;
|
||||
}
|
||||
System.out.printf("got line %s\n", line);
|
||||
if (line.contains(press_msg))
|
||||
return true;
|
||||
if (line.contains("Invalid"))
|
||||
return false;
|
||||
if (line.contains("Syntax"))
|
||||
return false;
|
||||
if (line.contains("Calibrating"))
|
||||
listener.message(this, line);
|
||||
}
|
||||
}
|
||||
|
||||
static final int cal_timeout = 20 * 1000;
|
||||
|
||||
public void run() {
|
||||
System.out.printf("start accel cal procedure\n");
|
||||
try {
|
||||
AltosConfigData new_config = null;
|
||||
|
||||
try {
|
||||
start_link();
|
||||
config_data = link.config_data();
|
||||
|
||||
/* set back to antenna up for calibration */
|
||||
if (config_data.pad_orientation != 0)
|
||||
link.printf("c o 0\n");
|
||||
|
||||
/* Start calibration */
|
||||
try {
|
||||
System.out.printf("*** start cal\n");
|
||||
link.set_match(press_msg);
|
||||
link.printf("c a 0\n");
|
||||
System.out.printf("*** wait press\n");
|
||||
if (!wait_press(cal_timeout))
|
||||
throw new TimeoutException("timeout");
|
||||
System.out.printf("*** set_phase antenna_up\n");
|
||||
listener.set_phase(this, phase_antenna_up);
|
||||
System.out.printf("*** wait_signal\n");
|
||||
if (!wait_signal())
|
||||
throw new InterruptedException("aborted");
|
||||
link.set_match(press_msg);
|
||||
System.out.printf("*** send newline\n");
|
||||
link.printf("\n");
|
||||
System.out.printf("*** wait press\n");
|
||||
if (!wait_press(cal_timeout))
|
||||
throw new TimeoutException("timeout");
|
||||
System.out.printf("***set_phase antenna_down\n");
|
||||
listener.set_phase(this, phase_antenna_down);
|
||||
System.out.printf("*** wait_signal\n");
|
||||
if (!wait_signal())
|
||||
throw new InterruptedException("aborted");
|
||||
System.out.printf("*** send newline and version command\n");
|
||||
link.printf("\nv\n");
|
||||
} catch (TimeoutException e) {
|
||||
throw e;
|
||||
} catch (InterruptedException e) {
|
||||
throw e;
|
||||
}
|
||||
link.set_match(null);
|
||||
|
||||
boolean worked = true;
|
||||
for (;;) {
|
||||
String line = link.get_reply(cal_timeout);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
System.out.printf("*** waiting for finish: %s\n", line);
|
||||
if (line.contains("Invalid"))
|
||||
worked = false;
|
||||
if (line.contains("software-version"))
|
||||
break;
|
||||
if (line.contains("Calibrating"))
|
||||
listener.message(this, line);
|
||||
}
|
||||
System.out.printf("*** worked: %b\n", worked);
|
||||
if (worked)
|
||||
new_config = new AltosConfigData(link);
|
||||
} finally {
|
||||
int plus = config_data.accel_cal_plus(config_data.pad_orientation);
|
||||
int minus = config_data.accel_cal_minus(config_data.pad_orientation);
|
||||
System.out.printf("Restore orientation %d +g %d -g %d\n",
|
||||
config_data.pad_orientation,
|
||||
plus, minus);
|
||||
if (config_data.pad_orientation != AltosLib.MISSING)
|
||||
link.printf("c o %d\n", config_data.pad_orientation);
|
||||
if (plus != AltosLib.MISSING && minus != AltosLib.MISSING && plus != 0) {
|
||||
if (plus < 0)
|
||||
plus = 65536 + plus;
|
||||
if (minus < 0)
|
||||
minus = 65536 + minus;
|
||||
if (config_data.accel_zero_along != AltosLib.MISSING)
|
||||
link.printf("c a %d %d %d %d %d\n",
|
||||
plus, minus,
|
||||
config_data.accel_zero_along,
|
||||
config_data.accel_zero_across,
|
||||
config_data.accel_zero_through);
|
||||
else
|
||||
link.printf("c a %d %d\n", plus, minus);
|
||||
}
|
||||
link.flush_output();
|
||||
stop_link();
|
||||
}
|
||||
if (new_config != null) {
|
||||
int plus = new_config.accel_cal_plus(AltosLib.AO_PAD_ORIENTATION_ANTENNA_UP);
|
||||
int minus = new_config.accel_cal_minus(AltosLib.AO_PAD_ORIENTATION_ANTENNA_UP);
|
||||
System.out.printf("*** +1g %d -1g %d\n", plus, minus);
|
||||
listener.cal_done(this, plus, minus);
|
||||
if (!wait_signal())
|
||||
throw new InterruptedException("aborted");
|
||||
} else
|
||||
listener.error(this, "Calibration failed");
|
||||
} catch (TimeoutException te) {
|
||||
System.out.printf("timeout");
|
||||
listener.error(this, "timeout");
|
||||
} catch (InterruptedException ie) {
|
||||
System.out.printf("interrupted\n");
|
||||
listener.error(this, "interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
accel_thread = new Thread(this);
|
||||
listener.set_thread(this, accel_thread);
|
||||
accel_thread.start();
|
||||
}
|
||||
|
||||
public AltosAccelCal(AltosLink link, AltosAccelCalListener listener) {
|
||||
this.link = link;
|
||||
this.listener = listener;
|
||||
ui_signal_semaphore = new Semaphore(0);
|
||||
}
|
||||
}
|
||||
34
altoslib/AltosAccelCalListener.java
Normal file
34
altoslib/AltosAccelCalListener.java
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public interface AltosAccelCalListener {
|
||||
public void set_thread(AltosAccelCal cal, Thread thread);
|
||||
|
||||
public void set_phase(AltosAccelCal cal, int phase);
|
||||
|
||||
public void cal_done(AltosAccelCal cal, int plus, int minus);
|
||||
|
||||
public void error(AltosAccelCal cal, String msg);
|
||||
|
||||
public void message(AltosAccelCal cal, String msg);
|
||||
}
|
||||
121
altoslib/AltosAdxl375.java
Normal file
121
altoslib/AltosAdxl375.java
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosAdxl375 implements Cloneable {
|
||||
|
||||
private int[] accels = new int[3];
|
||||
private int axis;
|
||||
|
||||
public static final int X_AXIS = 0;
|
||||
public static final int Y_AXIS = 1;
|
||||
public static final int Z_AXIS = 2;
|
||||
|
||||
public boolean parse_line(String line) throws NumberFormatException {
|
||||
if (line.startsWith("ADXL375 value")) {
|
||||
String[] items = line.split("\\s+");
|
||||
if (axis == AltosLib.MISSING)
|
||||
throw new NumberFormatException("No ADXL375 axis specified");
|
||||
if (items.length >= 3) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
accels[i] = Integer.parseInt(items[2+i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AltosAdxl375 clone() {
|
||||
AltosAdxl375 n = new AltosAdxl375(axis);
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
n.accels[i] = accels[i];
|
||||
return n;
|
||||
}
|
||||
|
||||
private int accel_along() {
|
||||
return accels[axis];
|
||||
}
|
||||
|
||||
private int accel_across() {
|
||||
if (axis == X_AXIS)
|
||||
return accels[Y_AXIS];
|
||||
else
|
||||
return accels[X_AXIS];
|
||||
}
|
||||
|
||||
private int accel_through() {
|
||||
return accels[Z_AXIS];
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link, boolean three_axis, int imu_type) throws InterruptedException, AltosUnknownProduct {
|
||||
try {
|
||||
AltosCalData cal_data = listener.cal_data();
|
||||
AltosAdxl375 adxl375 = new AltosAdxl375(link, cal_data.adxl375_axis);
|
||||
|
||||
if (adxl375 != null) {
|
||||
int accel = adxl375.accel_along();
|
||||
if (!cal_data.adxl375_inverted)
|
||||
accel = -accel;
|
||||
if (cal_data.pad_orientation == 1)
|
||||
accel = -accel;
|
||||
listener.set_acceleration(cal_data.acceleration(accel));
|
||||
if (three_axis) {
|
||||
cal_data.set_imu_type(imu_type);
|
||||
double accel_along = cal_data.accel_along(-accel);
|
||||
double accel_across = cal_data.accel_across(adxl375.accel_across());
|
||||
double accel_through = cal_data.accel_through(adxl375.accel_through());
|
||||
listener.set_accel_ground(accel_along,
|
||||
accel_across,
|
||||
accel_through);
|
||||
listener.set_accel(accel_along,
|
||||
accel_across,
|
||||
accel_through);
|
||||
}
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
} catch (NumberFormatException ne) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosAdxl375() {
|
||||
for (int i = 0; i < 3; i++)
|
||||
accels[i] = AltosLib.MISSING;
|
||||
axis = AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public AltosAdxl375(int axis) {
|
||||
this();
|
||||
this.axis = axis;
|
||||
}
|
||||
|
||||
public AltosAdxl375(AltosLink link, int axis) throws InterruptedException, TimeoutException, NumberFormatException {
|
||||
this(axis);
|
||||
link.printf("A\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
if (parse_line(line))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
altoslib/AltosCRCException.java
Normal file
27
altoslib/AltosCRCException.java
Normal 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.altoslib_14;
|
||||
|
||||
public class AltosCRCException extends Exception {
|
||||
public int rssi;
|
||||
|
||||
public AltosCRCException (int in_rssi) {
|
||||
rssi = in_rssi;
|
||||
}
|
||||
}
|
||||
578
altoslib/AltosCSV.java
Normal file
578
altoslib/AltosCSV.java
Normal file
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class AltosCSV implements AltosWriter {
|
||||
File name;
|
||||
PrintStream out;
|
||||
boolean header_written;
|
||||
boolean seen_boost;
|
||||
int boost_tick;
|
||||
|
||||
boolean has_call;
|
||||
boolean has_basic;
|
||||
boolean has_accel;
|
||||
boolean has_baro;
|
||||
boolean has_pyro;
|
||||
boolean has_radio;
|
||||
boolean has_battery;
|
||||
boolean has_flight_state;
|
||||
boolean has_3d_accel;
|
||||
boolean has_imu;
|
||||
boolean has_igniter;
|
||||
boolean has_gps;
|
||||
boolean has_gps_sat;
|
||||
boolean has_companion;
|
||||
boolean has_motor_pressure;
|
||||
|
||||
AltosFlightSeries series;
|
||||
int[] indices;
|
||||
|
||||
static final int ALTOS_CSV_VERSION = 6;
|
||||
|
||||
/* Version 4 format:
|
||||
*
|
||||
* General info
|
||||
* version number
|
||||
* serial number
|
||||
* flight number
|
||||
* callsign
|
||||
* time (seconds since boost)
|
||||
*
|
||||
* Radio info (if available)
|
||||
* rssi
|
||||
* link quality
|
||||
*
|
||||
* Flight status
|
||||
* state
|
||||
* state name
|
||||
*
|
||||
* Basic sensors
|
||||
* acceleration (m/s²)
|
||||
* pressure (mBar)
|
||||
* altitude (m)
|
||||
* height (m)
|
||||
* accelerometer speed (m/s)
|
||||
* barometer speed (m/s)
|
||||
* temp (°C)
|
||||
* drogue (V)
|
||||
* main (V)
|
||||
*
|
||||
* Battery
|
||||
* battery (V)
|
||||
*
|
||||
* Advanced sensors (if available)
|
||||
* accel_x (m/s²)
|
||||
* accel_y (m/s²)
|
||||
* accel_z (m/s²)
|
||||
* gyro_x (d/s)
|
||||
* gyro_y (d/s)
|
||||
* gyro_z (d/s)
|
||||
* mag_x (g)
|
||||
* mag_y (g)
|
||||
* mag_z (g)
|
||||
* tilt (d)
|
||||
*
|
||||
* Extra igniter voltages (if available)
|
||||
* pyro (V)
|
||||
* igniter_a (V)
|
||||
* igniter_b (V)
|
||||
* igniter_c (V)
|
||||
* igniter_d (V)
|
||||
*
|
||||
* GPS data (if available)
|
||||
* connected (1/0)
|
||||
* locked (1/0)
|
||||
* nsat (used for solution)
|
||||
* latitude (°)
|
||||
* longitude (°)
|
||||
* altitude (m)
|
||||
* year (e.g. 2010)
|
||||
* month (1-12)
|
||||
* day (1-31)
|
||||
* hour (0-23)
|
||||
* minute (0-59)
|
||||
* second (0-59)
|
||||
* from_pad_dist (m)
|
||||
* from_pad_azimuth (deg true)
|
||||
* from_pad_range (m)
|
||||
* from_pad_elevation (deg from horizon)
|
||||
* pdop
|
||||
* hdop
|
||||
* vdop
|
||||
*
|
||||
* GPS Sat data
|
||||
* C/N0 data for all 32 valid SDIDs
|
||||
*
|
||||
* Companion data
|
||||
* companion_id (1-255. 10 is TeleScience)
|
||||
* time of last companion data (seconds since boost)
|
||||
* update_period (0.1-2.55 minimum telemetry interval)
|
||||
* channels (0-12)
|
||||
* channel data for all 12 possible channels
|
||||
*/
|
||||
|
||||
void write_general_header() {
|
||||
out.printf(Locale.ROOT,"version,serial,flight");
|
||||
if (series.cal_data().callsign != null)
|
||||
out.printf(Locale.ROOT,",call");
|
||||
out.printf(Locale.ROOT,",time");
|
||||
}
|
||||
|
||||
double time() {
|
||||
return series.time(indices);
|
||||
}
|
||||
|
||||
void write_general() {
|
||||
out.printf(Locale.ROOT,"%s, %d, %d",
|
||||
ALTOS_CSV_VERSION,
|
||||
series.cal_data().serial,
|
||||
series.cal_data().flight);
|
||||
if (series.cal_data().callsign != null)
|
||||
out.printf(Locale.ROOT,",%s", series.cal_data().callsign);
|
||||
out.printf(Locale.ROOT,", %8.2f", time());
|
||||
}
|
||||
|
||||
void write_radio_header() {
|
||||
out.printf(Locale.ROOT,"rssi,lqi");
|
||||
}
|
||||
|
||||
int rssi() {
|
||||
return (int) series.value(AltosFlightSeries.rssi_name, indices);
|
||||
}
|
||||
|
||||
int status() {
|
||||
return (int) series.value(AltosFlightSeries.status_name, indices);
|
||||
}
|
||||
|
||||
void write_radio() {
|
||||
out.printf(Locale.ROOT,"%4d, %3d",
|
||||
rssi(), status() & 0x7f);
|
||||
}
|
||||
|
||||
void write_flight_header() {
|
||||
out.printf(Locale.ROOT,"state,state_name");
|
||||
}
|
||||
|
||||
int state() {
|
||||
return (int) series.value(AltosFlightSeries.state_name, indices);
|
||||
}
|
||||
|
||||
void write_flight() {
|
||||
int state = state();
|
||||
out.printf(Locale.ROOT,"%2d,%8s", state, AltosLib.state_name(state));
|
||||
}
|
||||
|
||||
void write_basic_header() {
|
||||
if (has_accel)
|
||||
out.printf(Locale.ROOT,"acceleration,");
|
||||
if (has_baro)
|
||||
out.printf(Locale.ROOT,"pressure,altitude,");
|
||||
out.printf(Locale.ROOT,"height,speed");
|
||||
if (has_baro)
|
||||
out.printf(Locale.ROOT,",temperature");
|
||||
if (has_pyro)
|
||||
out.printf(Locale.ROOT,",drogue_voltage,main_voltage");
|
||||
}
|
||||
|
||||
double acceleration() { return series.value(AltosFlightSeries.accel_name, indices); }
|
||||
double pressure() { return series.value(AltosFlightSeries.pressure_name, indices); }
|
||||
double altitude() { return series.value(AltosFlightSeries.altitude_name, indices); }
|
||||
double height() { return series.value(AltosFlightSeries.height_name, indices); }
|
||||
double speed() { return series.value(AltosFlightSeries.speed_name, indices); }
|
||||
double temperature() { return series.value(AltosFlightSeries.temperature_name, indices); }
|
||||
double apogee_voltage() { return series.value(AltosFlightSeries.apogee_voltage_name, indices); }
|
||||
double main_voltage() { return series.value(AltosFlightSeries.main_voltage_name, indices); }
|
||||
|
||||
void write_basic() {
|
||||
if (has_accel)
|
||||
out.printf(Locale.ROOT,"%8.2f,", acceleration());
|
||||
if (has_baro)
|
||||
out.printf(Locale.ROOT,"%10.2f,%8.2f,",
|
||||
pressure(), altitude());
|
||||
out.printf(Locale.ROOT,"%8.2f,%8.2f",
|
||||
height(), speed());
|
||||
if (has_baro)
|
||||
out.printf(Locale.ROOT,",%5.1f", temperature());
|
||||
if (has_pyro)
|
||||
out.printf(Locale.ROOT,",%5.2f,%5.2f",
|
||||
apogee_voltage(),
|
||||
main_voltage());
|
||||
}
|
||||
|
||||
void write_battery_header() {
|
||||
out.printf(Locale.ROOT,"battery_voltage");
|
||||
}
|
||||
|
||||
double battery_voltage() { return series.value(AltosFlightSeries.battery_voltage_name, indices); }
|
||||
|
||||
void write_battery() {
|
||||
out.printf(Locale.ROOT,"%5.2f", battery_voltage());
|
||||
}
|
||||
|
||||
void write_motor_pressure_header() {
|
||||
out.printf(Locale.ROOT,"motor_pressure");
|
||||
}
|
||||
|
||||
double motor_pressure() { return series.value(AltosFlightSeries.motor_pressure_name, indices); }
|
||||
|
||||
void write_motor_pressure() {
|
||||
out.printf(Locale.ROOT,"%10.1f", motor_pressure());
|
||||
}
|
||||
|
||||
void write_3d_accel_header() {
|
||||
out.printf(Locale.ROOT,"accel_x,accel_y,accel_z");
|
||||
}
|
||||
|
||||
double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); }
|
||||
double accel_across() { return series.value(AltosFlightSeries.accel_across_name, indices); }
|
||||
double accel_through() { return series.value(AltosFlightSeries.accel_through_name, indices); }
|
||||
|
||||
void write_3d_accel() {
|
||||
out.printf(Locale.ROOT,"%7.2f,%7.2f,%7.2f",
|
||||
accel_along(), accel_across(), accel_through());
|
||||
}
|
||||
|
||||
void write_imu_header() {
|
||||
out.printf(Locale.ROOT,"gyro_roll,gyro_pitch,gyro_yaw,mag_x,mag_y,mag_z,tilt");
|
||||
}
|
||||
|
||||
double gyro_roll() { return series.value(AltosFlightSeries.gyro_roll_name, indices); }
|
||||
double gyro_pitch() { return series.value(AltosFlightSeries.gyro_pitch_name, indices); }
|
||||
double gyro_yaw() { return series.value(AltosFlightSeries.gyro_yaw_name, indices); }
|
||||
|
||||
double mag_along() { return series.value(AltosFlightSeries.mag_along_name, indices); }
|
||||
double mag_across() { return series.value(AltosFlightSeries.mag_across_name, indices); }
|
||||
double mag_through() { return series.value(AltosFlightSeries.mag_through_name, indices); }
|
||||
|
||||
double tilt() { return series.value(AltosFlightSeries.orient_name, indices); }
|
||||
|
||||
void write_imu() {
|
||||
out.printf(Locale.ROOT,"%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
|
||||
gyro_roll(), gyro_pitch(), gyro_yaw(),
|
||||
mag_along(), mag_across(), mag_through(),
|
||||
tilt());
|
||||
}
|
||||
|
||||
void write_igniter_header() {
|
||||
out.printf(Locale.ROOT,"pyro");
|
||||
for (int i = 0; i < series.igniter_voltage.length; i++)
|
||||
out.printf(Locale.ROOT,",%s", AltosLib.igniter_short_name(i));
|
||||
}
|
||||
|
||||
double pyro() { return series.value(AltosFlightSeries.pyro_voltage_name, indices); }
|
||||
|
||||
double igniter_value(int channel) { return series.value(series.igniter_voltage_name(channel), indices); }
|
||||
|
||||
void write_igniter() {
|
||||
out.printf(Locale.ROOT,"%5.2f", pyro());
|
||||
for (int i = 0; i < series.igniter_voltage.length; i++)
|
||||
out.printf(Locale.ROOT,",%5.2f", igniter_value(i));
|
||||
}
|
||||
|
||||
void write_gps_header() {
|
||||
out.printf(Locale.ROOT,"connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,pdop,hdop,vdop");
|
||||
}
|
||||
|
||||
void write_gps() {
|
||||
AltosGPS gps = series.gps_before(series.time(indices));
|
||||
|
||||
AltosGreatCircle from_pad;
|
||||
|
||||
if (series.cal_data().gps_pad != null && gps != null)
|
||||
from_pad = new AltosGreatCircle(series.cal_data().gps_pad, gps);
|
||||
else
|
||||
from_pad = new AltosGreatCircle();
|
||||
|
||||
if (gps == null)
|
||||
gps = new AltosGPS();
|
||||
|
||||
out.printf(Locale.ROOT,"%2d,%2d,%3d,%12.7f,%12.7f,%8.1f,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f,%6.1f,%6.1f",
|
||||
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.range,
|
||||
from_pad.bearing,
|
||||
from_pad.elevation,
|
||||
gps.pdop,
|
||||
gps.hdop,
|
||||
gps.vdop);
|
||||
}
|
||||
|
||||
void write_gps_sat_header() {
|
||||
for(int i = 1; i <= 32; i++) {
|
||||
out.printf(Locale.ROOT,"sat%02d", i);
|
||||
if (i != 32)
|
||||
out.printf(Locale.ROOT,",");
|
||||
}
|
||||
}
|
||||
|
||||
void write_gps_sat() {
|
||||
AltosGPS gps = series.gps_before(series.time(indices));
|
||||
for(int i = 1; i <= 32; i++) {
|
||||
int c_n0 = 0;
|
||||
if (gps != null && gps.cc_gps_sat != null) {
|
||||
for(int j = 0; j < gps.cc_gps_sat.length; j++)
|
||||
if (gps.cc_gps_sat[j].svid == i) {
|
||||
c_n0 = gps.cc_gps_sat[j].c_n0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
out.printf ("%3d", c_n0);
|
||||
if (i != 32)
|
||||
out.printf(Locale.ROOT,",");
|
||||
}
|
||||
}
|
||||
|
||||
void write_companion_header() {
|
||||
/*
|
||||
out.printf(Locale.ROOT,"companion_id,companion_time,companion_update,companion_channels");
|
||||
for (int i = 0; i < 12; i++)
|
||||
out.printf(Locale.ROOT,",companion_%02d", i);
|
||||
*/
|
||||
}
|
||||
|
||||
void write_companion() {
|
||||
/*
|
||||
AltosCompanion companion = state.companion;
|
||||
|
||||
int channels_written = 0;
|
||||
if (companion == null) {
|
||||
out.printf(Locale.ROOT,"0,0,0,0");
|
||||
} else {
|
||||
out.printf(Locale.ROOT,"%3d,%5.2f,%5.2f,%2d",
|
||||
companion.board_id,
|
||||
(companion.tick - boost_tick) / 100.0,
|
||||
companion.update_period / 100.0,
|
||||
companion.channels);
|
||||
for (; channels_written < companion.channels; channels_written++)
|
||||
out.printf(Locale.ROOT,",%5d", companion.companion_data[channels_written]);
|
||||
}
|
||||
for (; channels_written < 12; channels_written++)
|
||||
out.printf(Locale.ROOT,",0");
|
||||
*/
|
||||
}
|
||||
|
||||
void write_header() {
|
||||
out.printf(Locale.ROOT,"#"); write_general_header();
|
||||
if (has_radio) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_radio_header();
|
||||
}
|
||||
if (has_flight_state) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_flight_header();
|
||||
}
|
||||
if (has_basic) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_basic_header();
|
||||
}
|
||||
if (has_battery) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_battery_header();
|
||||
}
|
||||
if (has_motor_pressure) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_motor_pressure_header();
|
||||
}
|
||||
if (has_3d_accel) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_3d_accel_header();
|
||||
}
|
||||
if (has_imu) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_imu_header();
|
||||
}
|
||||
if (has_igniter) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_igniter_header();
|
||||
}
|
||||
if (has_gps) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_gps_header();
|
||||
}
|
||||
if (has_gps_sat) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_gps_sat_header();
|
||||
}
|
||||
if (has_companion) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_companion_header();
|
||||
}
|
||||
out.printf ("\n");
|
||||
}
|
||||
|
||||
void write_one() {
|
||||
write_general();
|
||||
if (has_radio) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_radio();
|
||||
}
|
||||
if (has_flight_state) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_flight();
|
||||
}
|
||||
if (has_basic) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_basic();
|
||||
}
|
||||
if (has_battery) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_battery();
|
||||
}
|
||||
if (has_motor_pressure) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_motor_pressure();
|
||||
}
|
||||
if (has_3d_accel) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_3d_accel();
|
||||
}
|
||||
if (has_imu) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_imu();
|
||||
}
|
||||
if (has_igniter) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_igniter();
|
||||
}
|
||||
if (has_gps) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_gps();
|
||||
}
|
||||
if (has_gps_sat) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_gps_sat();
|
||||
}
|
||||
if (has_companion) {
|
||||
out.printf(Locale.ROOT,",");
|
||||
write_companion();
|
||||
}
|
||||
out.printf ("\n");
|
||||
}
|
||||
|
||||
private void write() {
|
||||
if (state() == AltosLib.ao_flight_startup)
|
||||
return;
|
||||
if (!header_written) {
|
||||
write_header();
|
||||
header_written = true;
|
||||
}
|
||||
write_one();
|
||||
}
|
||||
|
||||
private PrintStream out() {
|
||||
return out;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
out.close();
|
||||
}
|
||||
|
||||
public void write(AltosFlightSeries series) {
|
||||
// series.write_comments(out());
|
||||
|
||||
this.series = series;
|
||||
|
||||
series.finish();
|
||||
|
||||
has_radio = false;
|
||||
has_flight_state = false;
|
||||
has_basic = false;
|
||||
has_accel = false;
|
||||
has_baro = false;
|
||||
has_pyro = false;
|
||||
has_battery = false;
|
||||
has_motor_pressure = false;
|
||||
has_3d_accel = false;
|
||||
has_imu = false;
|
||||
has_igniter = false;
|
||||
has_gps = false;
|
||||
has_gps_sat = false;
|
||||
has_companion = false;
|
||||
|
||||
if (series.has_series(AltosFlightSeries.rssi_name))
|
||||
has_radio = true;
|
||||
if (series.has_series(AltosFlightSeries.state_name))
|
||||
has_flight_state = true;
|
||||
if (series.has_series(AltosFlightSeries.accel_name)) {
|
||||
has_basic = true;
|
||||
has_accel = true;
|
||||
}
|
||||
if (series.has_series(AltosFlightSeries.pressure_name)) {
|
||||
has_basic = true;
|
||||
has_baro = true;
|
||||
}
|
||||
if (series.has_series(AltosFlightSeries.apogee_voltage_name))
|
||||
has_pyro = true;
|
||||
if (series.has_series(AltosFlightSeries.battery_voltage_name))
|
||||
has_battery = true;
|
||||
if (series.has_series(AltosFlightSeries.motor_pressure_name))
|
||||
has_motor_pressure = true;
|
||||
if (series.has_series(AltosFlightSeries.accel_across_name))
|
||||
has_3d_accel = true;
|
||||
if (series.has_series(AltosFlightSeries.gyro_roll_name))
|
||||
has_imu = true;
|
||||
if (series.has_series(AltosFlightSeries.pyro_voltage_name))
|
||||
has_igniter = true;
|
||||
|
||||
if (series.gps_series != null)
|
||||
has_gps = true;
|
||||
if (series.sats_in_view != null)
|
||||
has_gps_sat = true;
|
||||
/*
|
||||
if (state.companion != null)
|
||||
has_companion = true;
|
||||
*/
|
||||
|
||||
indices = series.indices();
|
||||
|
||||
for (;;) {
|
||||
write();
|
||||
if (!series.step_indices(indices))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosCSV(PrintStream in_out, File in_name) {
|
||||
name = in_name;
|
||||
out = in_out;
|
||||
}
|
||||
|
||||
public AltosCSV(File in_name) throws FileNotFoundException {
|
||||
this(new PrintStream(in_name), in_name);
|
||||
}
|
||||
|
||||
public AltosCSV(String in_string) throws FileNotFoundException {
|
||||
this(new File(in_string));
|
||||
}
|
||||
}
|
||||
462
altoslib/AltosCalData.java
Normal file
462
altoslib/AltosCalData.java
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
/*
|
||||
* Calibration and other data needed to construct 'real' values from various data
|
||||
* sources.
|
||||
*/
|
||||
|
||||
public class AltosCalData {
|
||||
public int flight = AltosLib.MISSING;
|
||||
|
||||
public void set_flight(int flight) {
|
||||
if (flight != AltosLib.MISSING)
|
||||
this.flight = flight;
|
||||
}
|
||||
|
||||
public String callsign = null;
|
||||
|
||||
public void set_callsign(String callsign) {
|
||||
if (callsign != null)
|
||||
this.callsign = callsign;
|
||||
}
|
||||
|
||||
public String firmware_version = null;
|
||||
|
||||
public void set_firmware_version(String firmware_version) {
|
||||
if (firmware_version != null)
|
||||
this.firmware_version = firmware_version;
|
||||
}
|
||||
|
||||
public String product = null;
|
||||
|
||||
public void set_product(String product) {
|
||||
if (product != null)
|
||||
this.product = product;
|
||||
}
|
||||
|
||||
public int serial = AltosLib.MISSING;
|
||||
|
||||
public void set_serial(int serial) {
|
||||
if (serial != AltosLib.MISSING)
|
||||
this.serial = serial;
|
||||
}
|
||||
|
||||
public int receiver_serial = AltosLib.MISSING;
|
||||
|
||||
public void set_receiver_serial(int receiver_serial) {
|
||||
if (receiver_serial != AltosLib.MISSING)
|
||||
this.receiver_serial = receiver_serial;
|
||||
}
|
||||
|
||||
public int device_type = AltosLib.MISSING;
|
||||
|
||||
public void set_device_type(int device_type) {
|
||||
if (device_type != AltosLib.MISSING) {
|
||||
this.device_type = device_type;
|
||||
if (product == null)
|
||||
set_product(AltosLib.product_name(device_type));
|
||||
}
|
||||
}
|
||||
|
||||
public int log_format = AltosLib.MISSING;
|
||||
|
||||
public void set_log_format(int log_format) {
|
||||
if (log_format != AltosLib.MISSING)
|
||||
this.log_format = log_format;
|
||||
}
|
||||
|
||||
public int config_major = AltosLib.MISSING;
|
||||
public int config_minor = AltosLib.MISSING;
|
||||
public int flight_log_max = AltosLib.MISSING;
|
||||
|
||||
public void set_config(int major, int minor, int log_max) {
|
||||
if (major != AltosLib.MISSING)
|
||||
config_major = major;
|
||||
if (minor != AltosLib.MISSING)
|
||||
config_minor = minor;
|
||||
if (log_max != AltosLib.MISSING)
|
||||
flight_log_max = log_max;
|
||||
}
|
||||
|
||||
public double apogee_delay = AltosLib.MISSING;
|
||||
public double main_deploy = AltosLib.MISSING;
|
||||
|
||||
public void set_flight_params(double apogee_delay, double main_deploy) {
|
||||
if (apogee_delay != AltosLib.MISSING)
|
||||
this.apogee_delay = apogee_delay;
|
||||
if (main_deploy != AltosLib.MISSING)
|
||||
this.main_deploy = main_deploy;
|
||||
}
|
||||
|
||||
public double accel_plus_g = AltosLib.MISSING;
|
||||
public double accel_minus_g = AltosLib.MISSING;
|
||||
public double ground_accel = AltosLib.MISSING;
|
||||
|
||||
public void set_accel_plus_minus(double plus, double minus) {
|
||||
if (plus != AltosLib.MISSING && minus != AltosLib.MISSING) {
|
||||
if (plus == minus)
|
||||
return;
|
||||
accel_plus_g = plus;
|
||||
accel_minus_g = minus;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_ground_accel(double ground_accel) {
|
||||
if (ground_accel != AltosLib.MISSING)
|
||||
this.ground_accel = ground_accel;
|
||||
}
|
||||
|
||||
public double ground_motor_pressure = AltosLib.MISSING;
|
||||
|
||||
public void set_ground_motor_pressure(double ground_motor_pressure) {
|
||||
if (ground_motor_pressure != AltosLib.MISSING)
|
||||
this.ground_motor_pressure = ground_motor_pressure;
|
||||
}
|
||||
|
||||
/* Raw acceleration value */
|
||||
public double accel = AltosLib.MISSING;
|
||||
|
||||
public void set_accel(double accel) {
|
||||
this.accel = accel;
|
||||
}
|
||||
|
||||
public boolean mma655x_inverted = false;
|
||||
|
||||
public void set_mma655x_inverted(boolean inverted) {
|
||||
mma655x_inverted = inverted;
|
||||
}
|
||||
|
||||
public boolean adxl375_inverted = false;
|
||||
|
||||
public void set_adxl375_inverted(boolean inverted) {
|
||||
adxl375_inverted = inverted;
|
||||
}
|
||||
|
||||
public int adxl375_axis = AltosLib.MISSING;
|
||||
|
||||
public void set_adxl375_axis(int axis) {
|
||||
adxl375_axis = axis;
|
||||
}
|
||||
|
||||
public int pad_orientation = AltosLib.MISSING;
|
||||
|
||||
public void set_pad_orientation(int orientation) {
|
||||
if (orientation != AltosLib.MISSING)
|
||||
pad_orientation = orientation;
|
||||
}
|
||||
|
||||
/* Compute acceleration */
|
||||
public double acceleration(double sensor) {
|
||||
double accel;
|
||||
accel = AltosConvert.acceleration_from_sensor(sensor, accel_plus_g, accel_minus_g, ground_accel);
|
||||
return accel;
|
||||
}
|
||||
|
||||
public AltosMs5607 ms5607 = null;
|
||||
|
||||
public void set_ms5607(AltosMs5607 ms5607) {
|
||||
this.ms5607 = ms5607;
|
||||
}
|
||||
|
||||
public double ground_pressure = AltosLib.MISSING;
|
||||
public double ground_altitude = AltosLib.MISSING;
|
||||
|
||||
public void set_ground_pressure(double ground_pressure) {
|
||||
if (ground_pressure != AltosLib.MISSING) {
|
||||
this.ground_pressure = ground_pressure;
|
||||
this.ground_altitude = AltosConvert.pressure_to_altitude(ground_pressure);
|
||||
}
|
||||
}
|
||||
|
||||
public void set_ground_altitude(double ground_altitude) {
|
||||
if (ground_altitude != AltosLib.MISSING)
|
||||
this.ground_altitude = ground_altitude;
|
||||
}
|
||||
|
||||
/* Compute pressure */
|
||||
|
||||
public AltosPresTemp pressure_ms5607(int raw_pres, int raw_temp) {
|
||||
if (ms5607 == null)
|
||||
return new AltosPresTemp(AltosLib.MISSING, AltosLib.MISSING);
|
||||
return ms5607.pres_temp(raw_pres, raw_temp);
|
||||
}
|
||||
|
||||
public int tick = AltosLib.MISSING;
|
||||
private int first_tick = AltosLib.MISSING;
|
||||
private int prev_tick = AltosLib.MISSING;
|
||||
|
||||
public void set_tick(int tick) {
|
||||
if (tick != AltosLib.MISSING) {
|
||||
if (prev_tick != AltosLib.MISSING) {
|
||||
while (tick < prev_tick - 1000) {
|
||||
tick += 65536;
|
||||
}
|
||||
}
|
||||
if (first_tick == AltosLib.MISSING)
|
||||
first_tick = tick;
|
||||
prev_tick = tick;
|
||||
this.tick = tick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset all values which change during flight
|
||||
*/
|
||||
public void reset() {
|
||||
state = AltosLib.MISSING;
|
||||
tick = AltosLib.MISSING;
|
||||
prev_tick = AltosLib.MISSING;
|
||||
temp_gps = null;
|
||||
temp_gps_sat_tick = AltosLib.MISSING;
|
||||
accel = AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public int boost_tick = AltosLib.MISSING;
|
||||
|
||||
public void set_boost_tick() {
|
||||
boost_tick = tick;
|
||||
}
|
||||
|
||||
public double ticks_per_sec = 100.0;
|
||||
|
||||
public void set_ticks_per_sec(double ticks_per_sec) {
|
||||
this.ticks_per_sec = ticks_per_sec;
|
||||
}
|
||||
|
||||
public double time() {
|
||||
if (tick == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (boost_tick != AltosLib.MISSING)
|
||||
return (tick - boost_tick) / ticks_per_sec;
|
||||
if (first_tick != AltosLib.MISSING)
|
||||
return (tick - first_tick) / ticks_per_sec;
|
||||
return tick / ticks_per_sec;
|
||||
}
|
||||
|
||||
public double boost_time() {
|
||||
if (boost_tick == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return boost_tick / ticks_per_sec;
|
||||
}
|
||||
|
||||
public int state = AltosLib.MISSING;
|
||||
|
||||
public String state_name() {
|
||||
return AltosLib.state_name(state);
|
||||
}
|
||||
|
||||
public void set_state(int state) {
|
||||
if (state >= AltosLib.ao_flight_boost && boost_tick == AltosLib.MISSING)
|
||||
set_boost_tick();
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public AltosGPS gps_pad = null;
|
||||
|
||||
public AltosGPS prev_gps = null;
|
||||
|
||||
public double gps_pad_altitude = AltosLib.MISSING;
|
||||
|
||||
public void set_cal_gps(AltosGPS gps) {
|
||||
if (gps.locked && gps.nsat >= 4) {
|
||||
if ((state != AltosLib.MISSING && state < AltosLib.ao_flight_boost) || gps_pad == null)
|
||||
gps_pad = gps;
|
||||
if (gps_pad_altitude == AltosLib.MISSING && gps.alt != AltosLib.MISSING)
|
||||
gps_pad_altitude = gps.alt;
|
||||
}
|
||||
temp_gps = null;
|
||||
prev_gps = gps;
|
||||
}
|
||||
|
||||
/*
|
||||
* While receiving GPS data, we construct a temporary GPS state
|
||||
* object and then deliver the result atomically to the listener
|
||||
*/
|
||||
AltosGPS temp_gps = null;
|
||||
int temp_gps_sat_tick = AltosLib.MISSING;
|
||||
|
||||
public AltosGPS temp_cal_gps() {
|
||||
return temp_gps;
|
||||
}
|
||||
|
||||
public void reset_temp_cal_gps() {
|
||||
if (temp_gps != null)
|
||||
set_cal_gps(temp_gps);
|
||||
}
|
||||
|
||||
public boolean cal_gps_pending() {
|
||||
return temp_gps != null;
|
||||
}
|
||||
|
||||
public AltosGPS make_temp_cal_gps(int tick, boolean sats) {
|
||||
if (temp_gps == null)
|
||||
temp_gps = new AltosGPS(prev_gps);
|
||||
if (sats) {
|
||||
if (tick != temp_gps_sat_tick)
|
||||
temp_gps.cc_gps_sat = null;
|
||||
temp_gps_sat_tick = tick;
|
||||
}
|
||||
return temp_gps;
|
||||
}
|
||||
|
||||
public int imu_type = AltosLib.MISSING;
|
||||
|
||||
public int imu_model = AltosLib.MISSING;
|
||||
|
||||
public int mag_model = AltosLib.MISSING;
|
||||
|
||||
public void set_imu_type(int imu_type) {
|
||||
this.imu_type = imu_type;
|
||||
}
|
||||
|
||||
public void set_imu_model(int imu_model) {
|
||||
this.imu_model = imu_model;
|
||||
}
|
||||
|
||||
public void set_mag_model(int mag_model) {
|
||||
this.mag_model = mag_model;
|
||||
}
|
||||
|
||||
public double accel_zero_along, accel_zero_across, accel_zero_through;
|
||||
|
||||
public void set_accel_zero(double zero_along, double zero_across, double zero_through) {
|
||||
if (zero_along != AltosLib.MISSING) {
|
||||
accel_zero_along = zero_along;
|
||||
accel_zero_across = zero_across;
|
||||
accel_zero_through = zero_through;
|
||||
}
|
||||
}
|
||||
|
||||
public double accel_along(double counts) {
|
||||
return AltosIMU.convert_accel(counts - accel_zero_along, imu_type, imu_model);
|
||||
}
|
||||
|
||||
public double accel_across(double counts) {
|
||||
return AltosIMU.convert_accel(counts - accel_zero_across, imu_type, imu_model);
|
||||
}
|
||||
|
||||
public double accel_through(double counts) {
|
||||
return AltosIMU.convert_accel(counts - accel_zero_through, imu_type, imu_model);
|
||||
}
|
||||
|
||||
public double gyro_zero_roll = AltosLib.MISSING;
|
||||
public double gyro_zero_pitch = AltosLib.MISSING;
|
||||
public double gyro_zero_yaw = AltosLib.MISSING;
|
||||
|
||||
public void set_gyro_zero(double roll, double pitch, double yaw) {
|
||||
if (roll != AltosLib.MISSING) {
|
||||
gyro_zero_roll = roll;
|
||||
gyro_zero_pitch = pitch;
|
||||
gyro_zero_yaw = yaw;
|
||||
imu_wrap_checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public double gyro_roll(double counts) {
|
||||
if (gyro_zero_roll == AltosLib.MISSING || counts == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
return AltosIMU.gyro_degrees_per_second(counts - gyro_zero_roll, imu_type, imu_model);
|
||||
}
|
||||
|
||||
public double gyro_pitch(double counts) {
|
||||
if (gyro_zero_pitch == AltosLib.MISSING || counts == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return AltosIMU.gyro_degrees_per_second(counts - gyro_zero_pitch, imu_type, imu_model);
|
||||
}
|
||||
|
||||
public double gyro_yaw(double counts) {
|
||||
if (gyro_zero_yaw == AltosLib.MISSING || counts == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return AltosIMU.gyro_degrees_per_second(counts - gyro_zero_yaw, imu_type, imu_model);
|
||||
}
|
||||
|
||||
private double gyro_zero_overflow(double first) {
|
||||
double v = first / 128.0;
|
||||
if (v < 0)
|
||||
v = Math.ceil(v);
|
||||
else
|
||||
v = Math.floor(v);
|
||||
// if (v != 0)
|
||||
// System.out.printf("Adjusting gyro axis by %g steps\n", v);
|
||||
return v * 128.0;
|
||||
}
|
||||
|
||||
/* Initial TeleMega log format had only 16 bits for gyro cal, so the top 9 bits got lost as the
|
||||
* cal data are scaled by 512. Use the first sample to adjust the cal value, assuming that it is
|
||||
* from a time of fairly low rotation speed. Fixed in later TeleMega firmware by storing 32 bits
|
||||
* of cal values.
|
||||
*/
|
||||
private boolean imu_wrap_checked = false;
|
||||
|
||||
public void check_imu_wrap(double roll, double pitch, double yaw) {
|
||||
if (!imu_wrap_checked) {
|
||||
gyro_zero_roll += gyro_zero_overflow(roll);
|
||||
gyro_zero_pitch += gyro_zero_overflow(pitch);
|
||||
gyro_zero_yaw += gyro_zero_overflow(yaw);
|
||||
imu_wrap_checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
public double mag_along(double along) {
|
||||
if (along == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return AltosMag.convert_gauss(along, imu_type, mag_model);
|
||||
}
|
||||
|
||||
public double mag_across(double across) {
|
||||
if (across == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return AltosMag.convert_gauss(across, imu_type, mag_model);
|
||||
}
|
||||
|
||||
public double mag_through(double through) {
|
||||
if (through == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return AltosMag.convert_gauss(through, imu_type, mag_model);
|
||||
}
|
||||
|
||||
public AltosCalData() {
|
||||
}
|
||||
|
||||
public AltosCalData(AltosConfigData config_data) {
|
||||
set_serial(config_data.serial);
|
||||
set_ticks_per_sec(100.0);
|
||||
set_flight(config_data.flight);
|
||||
set_callsign(config_data.callsign);
|
||||
set_config(config_data.config_major, config_data.config_minor, config_data.flight_log_max);
|
||||
set_firmware_version(config_data.version);
|
||||
set_flight_params(config_data.apogee_delay / ticks_per_sec, config_data.apogee_lockout / ticks_per_sec);
|
||||
set_pad_orientation(config_data.pad_orientation);
|
||||
set_product(config_data.product);
|
||||
set_accel_plus_minus(config_data.accel_cal_plus(config_data.pad_orientation), config_data.accel_cal_minus(config_data.pad_orientation));
|
||||
set_accel_zero(config_data.accel_zero_along, config_data.accel_zero_across, config_data.accel_zero_through);
|
||||
set_ms5607(config_data.ms5607);
|
||||
try {
|
||||
set_mma655x_inverted(config_data.mma655x_inverted());
|
||||
} catch (AltosUnknownProduct up) {
|
||||
}
|
||||
try {
|
||||
set_adxl375_inverted(config_data.adxl375_inverted());
|
||||
} catch (AltosUnknownProduct up) {
|
||||
}
|
||||
try {
|
||||
set_adxl375_axis(config_data.adxl375_axis());
|
||||
} catch (AltosUnknownProduct up) {
|
||||
}
|
||||
set_pad_orientation(config_data.pad_orientation);
|
||||
}
|
||||
}
|
||||
46
altoslib/AltosCompanion.java
Normal file
46
altoslib/AltosCompanion.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosCompanion {
|
||||
public final static int board_id_telescience = 0x0a;
|
||||
public final static int MAX_CHANNELS = 12;
|
||||
|
||||
public int tick;
|
||||
public int board_id;
|
||||
public int update_period;
|
||||
public int channels;
|
||||
public int[] companion_data;
|
||||
|
||||
public AltosCompanion(int in_channels) {
|
||||
channels = in_channels;
|
||||
if (channels < 0)
|
||||
channels = 0;
|
||||
if (channels > MAX_CHANNELS)
|
||||
channels = MAX_CHANNELS;
|
||||
companion_data = new int[channels];
|
||||
}
|
||||
|
||||
public AltosCompanion() {
|
||||
channels = 0;
|
||||
companion_data = new int[0];
|
||||
}
|
||||
}
|
||||
1004
altoslib/AltosConfigData.java
Normal file
1004
altoslib/AltosConfigData.java
Normal file
File diff suppressed because it is too large
Load Diff
27
altoslib/AltosConfigDataException.java
Normal file
27
altoslib/AltosConfigDataException.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosConfigDataException extends Exception {
|
||||
|
||||
public AltosConfigDataException(String format, Object... args) {
|
||||
super(String.format(format, args));
|
||||
}
|
||||
|
||||
}
|
||||
136
altoslib/AltosConfigValues.java
Normal file
136
altoslib/AltosConfigValues.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosConfigValues {
|
||||
/* set and get all of the dialog values */
|
||||
public abstract void set_product(String product);
|
||||
|
||||
public abstract void set_version(String version);
|
||||
|
||||
public abstract void set_serial(int serial);
|
||||
|
||||
public abstract void set_altitude_32(int altitude_32);
|
||||
|
||||
public abstract void set_main_deploy(int new_main_deploy);
|
||||
|
||||
public abstract int main_deploy() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_apogee_delay(int new_apogee_delay);
|
||||
|
||||
public abstract int apogee_delay() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_apogee_lockout(int new_apogee_lockout);
|
||||
|
||||
public abstract int apogee_lockout() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_radio_frequency(double new_radio_frequency);
|
||||
|
||||
public abstract double radio_frequency() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_radio_calibration(int new_radio_calibration);
|
||||
|
||||
public abstract void set_radio_enable(int new_radio_enable);
|
||||
|
||||
public abstract int radio_enable();
|
||||
|
||||
public abstract void set_callsign(String new_callsign);
|
||||
|
||||
public abstract String callsign();
|
||||
|
||||
public abstract void set_telemetry_rate(int new_telemetry_rate);
|
||||
|
||||
public abstract int telemetry_rate() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_flight_log_max(int new_flight_log_max);
|
||||
|
||||
public abstract void set_flight_log_max_enabled(boolean enable);
|
||||
|
||||
public abstract int flight_log_max() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_flight_log_max_limit(int flight_log_max_limit, int storage_erase_unit);
|
||||
|
||||
public abstract void set_ignite_mode(int new_ignite_mode);
|
||||
|
||||
public abstract int ignite_mode();
|
||||
|
||||
public abstract void set_pad_orientation(int new_pad_orientation);
|
||||
|
||||
public abstract int pad_orientation();
|
||||
|
||||
public abstract void set_accel_cal(int accel_cal_plus, int accel_cal_minus);
|
||||
|
||||
public abstract int accel_cal_plus();
|
||||
|
||||
public abstract int accel_cal_minus();
|
||||
|
||||
public abstract void set_dirty();
|
||||
|
||||
public abstract void set_clean();
|
||||
|
||||
public abstract void set_pyros(AltosPyro[] new_pyros);
|
||||
|
||||
public abstract AltosPyro[] pyros() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_pyro_firing_time(double new_pyro_firing_time);
|
||||
|
||||
public abstract double pyro_firing_time() throws AltosConfigDataException;
|
||||
|
||||
public abstract int aprs_interval() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_aprs_interval(int new_aprs_interval);
|
||||
|
||||
public abstract int aprs_ssid() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_aprs_ssid(int new_aprs_ssid);
|
||||
|
||||
public abstract int aprs_format() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_aprs_format(int new_aprs_format);
|
||||
|
||||
public abstract int aprs_offset() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_aprs_offset(int new_aprs_offset);
|
||||
|
||||
public abstract int beep() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_beep(int new_beep);
|
||||
|
||||
public abstract int tracker_motion() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_tracker_motion(int tracker_motion);
|
||||
|
||||
public abstract int tracker_interval() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_tracker_interval(int tracker_motion);
|
||||
|
||||
public abstract int radio_10mw() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_radio_10mw(int radio_10mw);
|
||||
|
||||
public abstract boolean has_radio();
|
||||
|
||||
public abstract int report_feet() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_report_feet(int report_feet);
|
||||
|
||||
public abstract int gps_receiver() throws AltosConfigDataException;
|
||||
|
||||
public abstract void set_gps_receiver(int gps_receiver);
|
||||
}
|
||||
591
altoslib/AltosConvert.java
Normal file
591
altoslib/AltosConvert.java
Normal file
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sensor data conversion functions
|
||||
*/
|
||||
package org.altusmetrum.altoslib_14;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class AltosConvert {
|
||||
|
||||
public static final double gravity = 9.80665;
|
||||
|
||||
/*
|
||||
* Pressure Sensor Model, version 1.1
|
||||
*
|
||||
* written by Holly Grimes
|
||||
*
|
||||
* Uses the International Standard Atmosphere as described in
|
||||
* "A Quick Derivation relating altitude to air pressure" (version 1.03)
|
||||
* from the Portland State Aerospace Society, except that the atmosphere
|
||||
* is divided into layers with each layer having a different lapse rate.
|
||||
*
|
||||
* Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
|
||||
* at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
|
||||
*
|
||||
* Height measurements use the local tangent plane. The postive z-direction is up.
|
||||
*
|
||||
* All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
|
||||
* The lapse rate is given in Kelvin/meter, the gas constant for air is given
|
||||
* in Joules/(kilogram-Kelvin).
|
||||
*/
|
||||
|
||||
private static final double GRAVITATIONAL_ACCELERATION = -gravity;
|
||||
private static final double AIR_GAS_CONSTANT = 287.053;
|
||||
private static final double NUMBER_OF_LAYERS = 7;
|
||||
private static final double MAXIMUM_ALTITUDE = 84852.0;
|
||||
private static final double MINIMUM_PRESSURE = 0.3734;
|
||||
private static final double LAYER0_BASE_TEMPERATURE = 288.15;
|
||||
private static final double LAYER0_BASE_PRESSURE = 101325;
|
||||
|
||||
/* lapse rate and base altitude for each layer in the atmosphere */
|
||||
private static final double[] lapse_rate = {
|
||||
-0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
|
||||
};
|
||||
|
||||
private static final int[] base_altitude = {
|
||||
0, 11000, 20000, 32000, 47000, 51000, 71000
|
||||
};
|
||||
|
||||
/* outputs atmospheric pressure associated with the given altitude.
|
||||
* altitudes are measured with respect to the mean sea level
|
||||
*/
|
||||
public static double
|
||||
altitude_to_pressure(double altitude)
|
||||
{
|
||||
double base_temperature = LAYER0_BASE_TEMPERATURE;
|
||||
double base_pressure = LAYER0_BASE_PRESSURE;
|
||||
|
||||
double pressure;
|
||||
double base; /* base for function to determine pressure */
|
||||
double exponent; /* exponent for function to determine pressure */
|
||||
int layer_number; /* identifies layer in the atmosphere */
|
||||
double delta_z; /* difference between two altitudes */
|
||||
|
||||
if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
|
||||
return 0;
|
||||
|
||||
/* calculate the base temperature and pressure for the atmospheric layer
|
||||
associated with the inputted altitude */
|
||||
for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
|
||||
delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
|
||||
if (lapse_rate[layer_number] == 0.0) {
|
||||
exponent = GRAVITATIONAL_ACCELERATION * delta_z
|
||||
/ AIR_GAS_CONSTANT / base_temperature;
|
||||
base_pressure *= Math.exp(exponent);
|
||||
}
|
||||
else {
|
||||
base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
|
||||
exponent = GRAVITATIONAL_ACCELERATION /
|
||||
(AIR_GAS_CONSTANT * lapse_rate[layer_number]);
|
||||
base_pressure *= Math.pow(base, exponent);
|
||||
}
|
||||
base_temperature += delta_z * lapse_rate[layer_number];
|
||||
}
|
||||
|
||||
/* calculate the pressure at the inputted altitude */
|
||||
delta_z = altitude - base_altitude[layer_number];
|
||||
if (lapse_rate[layer_number] == 0.0) {
|
||||
exponent = GRAVITATIONAL_ACCELERATION * delta_z
|
||||
/ AIR_GAS_CONSTANT / base_temperature;
|
||||
pressure = base_pressure * Math.exp(exponent);
|
||||
}
|
||||
else {
|
||||
base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
|
||||
exponent = GRAVITATIONAL_ACCELERATION /
|
||||
(AIR_GAS_CONSTANT * lapse_rate[layer_number]);
|
||||
pressure = base_pressure * Math.pow(base, exponent);
|
||||
}
|
||||
|
||||
return pressure;
|
||||
}
|
||||
|
||||
|
||||
/* outputs the altitude associated with the given pressure. the altitude
|
||||
returned is measured with respect to the mean sea level */
|
||||
public static double
|
||||
pressure_to_altitude(double pressure)
|
||||
{
|
||||
|
||||
double next_base_temperature = LAYER0_BASE_TEMPERATURE;
|
||||
double next_base_pressure = LAYER0_BASE_PRESSURE;
|
||||
|
||||
double altitude;
|
||||
double base_pressure;
|
||||
double base_temperature;
|
||||
double base; /* base for function to determine base pressure of next layer */
|
||||
double exponent; /* exponent for function to determine base pressure
|
||||
of next layer */
|
||||
double coefficient;
|
||||
int layer_number; /* identifies layer in the atmosphere */
|
||||
int delta_z; /* difference between two altitudes */
|
||||
|
||||
if (pressure < 0) /* illegal pressure */
|
||||
return -1;
|
||||
if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
|
||||
return MAXIMUM_ALTITUDE;
|
||||
|
||||
/* calculate the base temperature and pressure for the atmospheric layer
|
||||
associated with the inputted pressure. */
|
||||
layer_number = -1;
|
||||
do {
|
||||
layer_number++;
|
||||
base_pressure = next_base_pressure;
|
||||
base_temperature = next_base_temperature;
|
||||
delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
|
||||
if (lapse_rate[layer_number] == 0.0) {
|
||||
exponent = GRAVITATIONAL_ACCELERATION * delta_z
|
||||
/ AIR_GAS_CONSTANT / base_temperature;
|
||||
next_base_pressure *= Math.exp(exponent);
|
||||
}
|
||||
else {
|
||||
base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
|
||||
exponent = GRAVITATIONAL_ACCELERATION /
|
||||
(AIR_GAS_CONSTANT * lapse_rate[layer_number]);
|
||||
next_base_pressure *= Math.pow(base, exponent);
|
||||
}
|
||||
next_base_temperature += delta_z * lapse_rate[layer_number];
|
||||
}
|
||||
while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
|
||||
|
||||
/* calculate the altitude associated with the inputted pressure */
|
||||
if (lapse_rate[layer_number] == 0.0) {
|
||||
coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
|
||||
* base_temperature;
|
||||
altitude = base_altitude[layer_number]
|
||||
+ coefficient * Math.log(pressure / base_pressure);
|
||||
}
|
||||
else {
|
||||
base = pressure / base_pressure;
|
||||
exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
|
||||
/ GRAVITATIONAL_ACCELERATION;
|
||||
coefficient = base_temperature / lapse_rate[layer_number];
|
||||
altitude = base_altitude[layer_number]
|
||||
+ coefficient * (Math.pow(base, exponent) - 1);
|
||||
}
|
||||
|
||||
return altitude;
|
||||
}
|
||||
|
||||
public static double degrees_to_radians(double degrees) {
|
||||
if (degrees == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return degrees * (Math.PI / 180.0);
|
||||
}
|
||||
|
||||
public static double radians_to_degrees(double radians) {
|
||||
if (radians == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return radians * (180.0 / Math.PI);
|
||||
}
|
||||
|
||||
public static double
|
||||
cc_battery_to_voltage(double battery)
|
||||
{
|
||||
return battery / 32767.0 * 5.0;
|
||||
}
|
||||
|
||||
public static double
|
||||
cc_igniter_to_voltage(double ignite)
|
||||
{
|
||||
return ignite / 32767 * 15.0;
|
||||
}
|
||||
|
||||
public static double
|
||||
barometer_to_pressure(double count)
|
||||
{
|
||||
return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
|
||||
}
|
||||
|
||||
static double
|
||||
thermometer_to_temperature(double thermo)
|
||||
{
|
||||
return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
|
||||
}
|
||||
|
||||
static double mega_adc(int raw) {
|
||||
return raw / 4095.0;
|
||||
}
|
||||
|
||||
static public double mega_battery_voltage(int v_batt) {
|
||||
if (v_batt != AltosLib.MISSING)
|
||||
return 3.3 * mega_adc(v_batt) * (5.6 + 10.0) / 10.0;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
static double mega_pyro_voltage(int raw) {
|
||||
if (raw != AltosLib.MISSING)
|
||||
return 3.3 * mega_adc(raw) * (100.0 + 27.0) / 27.0;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
static double tele_mini_3_adc(int raw) {
|
||||
return raw / 4095.0;
|
||||
}
|
||||
|
||||
static public double tele_mini_3_battery_voltage(int v_batt) {
|
||||
if (v_batt != AltosLib.MISSING)
|
||||
return 3.3 * tele_mini_3_adc(v_batt) * (5.6 + 10.0) / 10.0;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
static double tele_mini_3_pyro_voltage(int raw) {
|
||||
if (raw != AltosLib.MISSING)
|
||||
return 3.3 * tele_mini_3_adc(raw) * (100.0 + 27.0) / 27.0;
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
static double tele_mini_2_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return sensor / 32767.0 * supply * 127/27;
|
||||
}
|
||||
|
||||
static double tele_gps_1_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0;
|
||||
}
|
||||
|
||||
static double tele_gps_2_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return sensor / 4095.0 * supply * (5.6 + 10.0) / 10.0;
|
||||
}
|
||||
|
||||
static double tele_gps_3_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0;
|
||||
}
|
||||
|
||||
static double tele_bt_3_battery(int raw) {
|
||||
if (raw == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return 3.3 * mega_adc(raw) * (5.1 + 10.0) / 10.0;
|
||||
}
|
||||
|
||||
static double easy_timer_voltage(int sensor) {
|
||||
return 3.3 * mega_adc(sensor) * (100.0 + 27.0) / 27.0;
|
||||
}
|
||||
|
||||
static double easy_mini_2_adc(double raw) {
|
||||
return raw / 4095.0;
|
||||
}
|
||||
|
||||
static double easy_mini_1_adc(double raw) {
|
||||
return raw / 32767.0;
|
||||
}
|
||||
|
||||
static double easy_mini_1_voltage(int sensor, int serial) {
|
||||
double supply = 3.3;
|
||||
double diode_offset = 0.0;
|
||||
|
||||
/* early prototypes had a 3.0V regulator */
|
||||
if (serial < 1000)
|
||||
supply = 3.0;
|
||||
|
||||
/* Purple v1.0 boards had the sensor after the
|
||||
* blocking diode, which drops about 150mV
|
||||
*/
|
||||
if (serial < 1665)
|
||||
diode_offset = 0.150;
|
||||
|
||||
return easy_mini_1_adc(sensor) * supply * 127/27 + diode_offset;
|
||||
}
|
||||
|
||||
static double easy_mini_2_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return easy_mini_2_adc(sensor) * supply * 127/27;
|
||||
}
|
||||
|
||||
static double easy_mini_3_voltage(int sensor) {
|
||||
return easy_mini_1_voltage(sensor, 10000);
|
||||
}
|
||||
|
||||
static double motor_pressure(double voltage) {
|
||||
double base = 0.5;
|
||||
double max = 4.5;
|
||||
double full_scale_pressure = psi_to_pa(1600);
|
||||
|
||||
if (voltage < base)
|
||||
voltage = base;
|
||||
if (voltage > max)
|
||||
voltage = max;
|
||||
return (voltage - base) / (max - base) * full_scale_pressure;
|
||||
}
|
||||
|
||||
static double easy_motor_3_adc(double raw) {
|
||||
return raw / 32767.0;
|
||||
}
|
||||
|
||||
static double easy_motor_3_voltage(int sensor) {
|
||||
double supply = 3.3;
|
||||
|
||||
return easy_motor_3_adc(sensor) * supply * 15.6 / 10.0;
|
||||
}
|
||||
|
||||
static double easy_motor_2_motor_pressure(int sensor, double ground_sensor) {
|
||||
double supply = 3.3;
|
||||
double ground_voltage = easy_mini_2_adc(ground_sensor) * supply * 15.6 / 10.0;
|
||||
double voltage = easy_mini_2_adc(sensor) * supply * 15.6 / 10.0;
|
||||
|
||||
return motor_pressure(voltage) - motor_pressure(ground_voltage);
|
||||
}
|
||||
|
||||
static double easy_motor_3_motor_pressure(int sensor, double ground_sensor) {
|
||||
double supply = 3.3;
|
||||
double ground_voltage = easy_motor_3_adc(ground_sensor) * supply * 15.6 / 10.0;
|
||||
double voltage = easy_motor_3_adc(sensor) * supply * 15.6 / 10.0;
|
||||
|
||||
return motor_pressure(voltage) - motor_pressure(ground_voltage);
|
||||
}
|
||||
|
||||
public static double radio_to_frequency(int freq, int setting, int cal, int channel) {
|
||||
double f;
|
||||
|
||||
if (freq > 0)
|
||||
f = freq / 1000.0;
|
||||
else {
|
||||
if (setting <= 0)
|
||||
setting = cal;
|
||||
f = 434.550 * setting / cal;
|
||||
/* Round to nearest 50KHz */
|
||||
f = Math.floor (20.0 * f + 0.5) / 20.0;
|
||||
}
|
||||
return f + channel * 0.100;
|
||||
}
|
||||
|
||||
public static int radio_frequency_to_setting(double frequency, int cal) {
|
||||
double set = frequency / 434.550 * cal;
|
||||
|
||||
return (int) Math.floor (set + 0.5);
|
||||
}
|
||||
|
||||
public static int radio_frequency_to_channel(double frequency) {
|
||||
int channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5);
|
||||
|
||||
if (channel < 0)
|
||||
channel = 0;
|
||||
if (channel > 9)
|
||||
channel = 9;
|
||||
return channel;
|
||||
}
|
||||
|
||||
public static double radio_channel_to_frequency(int channel) {
|
||||
return 434.550 + channel * 0.100;
|
||||
}
|
||||
|
||||
public static int telem_to_rssi(int telem) {
|
||||
return telem / 2 - 74;
|
||||
}
|
||||
|
||||
public static int[] ParseHex(String line) {
|
||||
String[] tokens = line.split("\\s+");
|
||||
int[] array = new int[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; i++)
|
||||
try {
|
||||
array[i] = Integer.parseInt(tokens[i], 16);
|
||||
} catch (NumberFormatException ne) {
|
||||
return null;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static double meters_to_feet(double meters) {
|
||||
return meters * (100 / (2.54 * 12));
|
||||
}
|
||||
|
||||
public static double feet_to_meters(double feet) {
|
||||
return feet * 12 * 2.54 / 100.0;
|
||||
}
|
||||
|
||||
public static double meters_to_miles(double meters) {
|
||||
return meters_to_feet(meters) / 5280;
|
||||
}
|
||||
|
||||
public static double miles_to_meters(double miles) {
|
||||
return feet_to_meters(miles * 5280);
|
||||
}
|
||||
|
||||
public static double meters_to_mph(double mps) {
|
||||
return meters_to_miles(mps) * 3600;
|
||||
}
|
||||
|
||||
public static double mph_to_meters(double mps) {
|
||||
return miles_to_meters(mps) / 3600;
|
||||
}
|
||||
|
||||
public static double mps_to_fps(double mps) {
|
||||
return meters_to_miles(mps) * 5280;
|
||||
}
|
||||
|
||||
public static double fps_to_mps(double mps) {
|
||||
return miles_to_meters(mps) / 5280;
|
||||
}
|
||||
|
||||
public static double meters_to_mach(double meters) {
|
||||
return meters / 343; /* something close to mach at usual rocket sites */
|
||||
}
|
||||
|
||||
public static double meters_to_g(double meters) {
|
||||
return meters / 9.80665;
|
||||
}
|
||||
|
||||
public static double c_to_f(double c) {
|
||||
return c * 9/5 + 32;
|
||||
}
|
||||
|
||||
public static double f_to_c(double c) {
|
||||
return (c - 32) * 5/9;
|
||||
}
|
||||
|
||||
public static double psi_to_pa(double psi) {
|
||||
return psi * 6894.76;
|
||||
}
|
||||
|
||||
public static double pa_to_psi(double pa) {
|
||||
return pa / 6894.76;
|
||||
}
|
||||
|
||||
public static double n_to_lb(double n) {
|
||||
return n * 0.22480894;
|
||||
}
|
||||
|
||||
public static double lb_to_n(double lb) {
|
||||
return lb / 0.22480894;
|
||||
}
|
||||
|
||||
public static double acceleration_from_sensor(double sensor, double plus_g, double minus_g, double ground) {
|
||||
|
||||
if (sensor == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
if (plus_g == AltosLib.MISSING || minus_g == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
if (ground == AltosLib.MISSING)
|
||||
ground = plus_g;
|
||||
|
||||
double counts_per_g = (plus_g - minus_g) / 2.0;
|
||||
double counts_per_mss = counts_per_g / gravity;
|
||||
|
||||
if (counts_per_mss == 0)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
return (sensor - ground) / counts_per_mss;
|
||||
}
|
||||
|
||||
public static boolean imperial_units = false;
|
||||
|
||||
public static AltosDistance distance = new AltosDistance();
|
||||
|
||||
public static AltosHeight height = new AltosHeight();
|
||||
|
||||
public static AltosPressure pressure = new AltosPressure();
|
||||
|
||||
public static AltosForce force = new AltosForce();
|
||||
|
||||
public static AltosSpeed speed = new AltosSpeed();
|
||||
|
||||
public static AltosAccel accel = new AltosAccel();
|
||||
|
||||
public static AltosTemperature temperature = new AltosTemperature();
|
||||
|
||||
public static AltosOrient orient = new AltosOrient();
|
||||
|
||||
public static AltosVoltage voltage = new AltosVoltage();
|
||||
|
||||
public static AltosLatitude latitude = new AltosLatitude();
|
||||
|
||||
public static AltosLongitude longitude = new AltosLongitude();
|
||||
|
||||
public static AltosRotationRate rotation_rate = new AltosRotationRate();
|
||||
|
||||
public static AltosStateName state_name = new AltosStateName();
|
||||
|
||||
public static AltosPyroName pyro_name = new AltosPyroName();
|
||||
|
||||
public static AltosUnits magnetic_field = new AltosGauss();
|
||||
|
||||
public static String show_gs(String format, double a) {
|
||||
a = meters_to_g(a);
|
||||
format = format.concat(" g");
|
||||
return String.format(format, a);
|
||||
}
|
||||
|
||||
public static String say_gs(double a) {
|
||||
return String.format("%6.0 gees", meters_to_g(a));
|
||||
}
|
||||
|
||||
public static int checksum(int[] data, int start, int length) {
|
||||
int csum = 0x5a;
|
||||
for (int i = 0; i < length; i++)
|
||||
csum += data[i + start];
|
||||
return csum & 0xff;
|
||||
}
|
||||
|
||||
public static int checksum(List<Byte> data, int start, int length) {
|
||||
int csum = 0x5a;
|
||||
for (int i = 0; i < length; i++)
|
||||
csum += data.get(i+start);
|
||||
return csum & 0xff;
|
||||
}
|
||||
|
||||
public static double beep_value_to_freq(int value) {
|
||||
if (value == 0)
|
||||
return 4000;
|
||||
return 1.0/2.0 * (24.0e6/32.0) / (double) value;
|
||||
}
|
||||
|
||||
public static int beep_freq_to_value(double freq) {
|
||||
if (freq == 0)
|
||||
return 0;
|
||||
return (int) Math.floor (1.0/2.0 * (24.0e6/32.0) / freq + 0.5);
|
||||
}
|
||||
|
||||
public static final int BEARING_LONG = 0;
|
||||
public static final int BEARING_SHORT = 1;
|
||||
public static final int BEARING_VOICE = 2;
|
||||
|
||||
public static String bearing_to_words(int length, double bearing) {
|
||||
String [][] bearing_string = {
|
||||
{
|
||||
"North", "North North East", "North East", "East North East",
|
||||
"East", "East South East", "South East", "South South East",
|
||||
"South", "South South West", "South West", "West South West",
|
||||
"West", "West North West", "North West", "North North West"
|
||||
}, {
|
||||
"N", "NNE", "NE", "ENE",
|
||||
"E", "ESE", "SE", "SSE",
|
||||
"S", "SSW", "SW", "WSW",
|
||||
"W", "WNW", "NW", "NNW"
|
||||
}, {
|
||||
"north", "nor nor east", "north east", "east nor east",
|
||||
"east", "east sow east", "south east", "sow sow east",
|
||||
"south", "sow sow west", "south west", "west sow west",
|
||||
"west", "west nor west", "north west", "nor nor west "
|
||||
}
|
||||
};
|
||||
return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16];
|
||||
}
|
||||
}
|
||||
153
altoslib/AltosDataListener.java
Normal file
153
altoslib/AltosDataListener.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public abstract class AltosDataListener {
|
||||
|
||||
private AltosCalData cal_data = null;
|
||||
|
||||
public double time = AltosLib.MISSING;
|
||||
public double frequency = AltosLib.MISSING;
|
||||
|
||||
public int raw_tick = AltosLib.MISSING;
|
||||
|
||||
public int tick() {
|
||||
return raw_tick;
|
||||
}
|
||||
|
||||
public void set_tick(int tick) {
|
||||
raw_tick = tick;
|
||||
cal_data.set_tick(tick);
|
||||
set_time(cal_data.time());
|
||||
}
|
||||
|
||||
public AltosCalData cal_data() {
|
||||
if (cal_data == null)
|
||||
cal_data = new AltosCalData();
|
||||
return cal_data;
|
||||
}
|
||||
|
||||
public void set_time(double time) {
|
||||
if (time != AltosLib.MISSING)
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public void set_serial(int serial) {
|
||||
cal_data().set_serial(serial);
|
||||
}
|
||||
|
||||
public void set_device_type(int device_type) {
|
||||
cal_data().set_device_type(device_type);
|
||||
switch (device_type) {
|
||||
case AltosLib.product_telegps:
|
||||
set_state(AltosLib.ao_flight_stateless);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_log_format(int log_format) {
|
||||
cal_data().set_log_format(log_format);
|
||||
if (cal_data().device_type == AltosLib.MISSING)
|
||||
cal_data().set_device_type(AltosLib.product_id_from_log_format(log_format));
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEGPS:
|
||||
set_state(AltosLib.ao_flight_stateless);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public double time() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public String state_name() {
|
||||
return cal_data().state_name();
|
||||
}
|
||||
|
||||
public void set_state(int state) {
|
||||
cal_data().set_state(state);
|
||||
}
|
||||
|
||||
public int state() {
|
||||
return cal_data().state;
|
||||
}
|
||||
|
||||
public void set_flight(int flight) {
|
||||
cal_data().set_flight(flight);
|
||||
}
|
||||
|
||||
public void set_frequency(double frequency) {
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public void set_avoid_duplicate_files() {
|
||||
}
|
||||
|
||||
/* Called after all records are captured */
|
||||
public void finish() {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
set_state(AltosLib.ao_flight_invalid);
|
||||
time = AltosLib.MISSING;
|
||||
frequency = AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public abstract void set_rssi(int rssi, int status);
|
||||
public abstract void set_received_time(long received_time);
|
||||
|
||||
public abstract void set_acceleration(double accel);
|
||||
public abstract void set_pressure(double pa);
|
||||
public abstract void set_thrust(double N);
|
||||
|
||||
public abstract void set_kalman(double height, double speed, double accel);
|
||||
|
||||
public abstract void set_temperature(double deg_c);
|
||||
public abstract void set_battery_voltage(double volts);
|
||||
|
||||
public abstract void set_apogee_voltage(double volts);
|
||||
public abstract void set_main_voltage(double volts);
|
||||
|
||||
public void set_gps(AltosGPS gps, boolean set_location, boolean set_sats) {
|
||||
AltosCalData cal_data = cal_data();
|
||||
cal_data.set_cal_gps(gps);
|
||||
}
|
||||
|
||||
public AltosGPS make_temp_gps(boolean sats) {
|
||||
return cal_data().make_temp_cal_gps(tick(), sats);
|
||||
}
|
||||
|
||||
public AltosGPS temp_gps() {
|
||||
return cal_data().temp_cal_gps();
|
||||
}
|
||||
|
||||
public abstract void set_orient(double orient);
|
||||
public abstract void set_gyro(double roll, double pitch, double yaw);
|
||||
public abstract void set_accel_ground(double along, double across, double through);
|
||||
public abstract void set_accel(double along, double across, double through);
|
||||
public abstract void set_mag(double along, double across, double through);
|
||||
public abstract void set_pyro_voltage(double volts);
|
||||
public abstract void set_igniter_voltage(double[] voltage);
|
||||
public abstract void set_pyro_fired(int pyro_mask);
|
||||
public abstract void set_companion(AltosCompanion companion);
|
||||
public abstract void set_motor_pressure(double motor_pressure);
|
||||
|
||||
public AltosDataListener() {
|
||||
}
|
||||
|
||||
public AltosDataListener(AltosCalData cal_data) {
|
||||
this.cal_data = cal_data;
|
||||
}
|
||||
}
|
||||
23
altoslib/AltosDataProvider.java
Normal file
23
altoslib/AltosDataProvider.java
Normal 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.altoslib_14;
|
||||
|
||||
public interface AltosDataProvider {
|
||||
public void provide_data(AltosDataListener listener) throws InterruptedException, AltosUnknownProduct;
|
||||
}
|
||||
285
altoslib/AltosDebug.java
Normal file
285
altoslib/AltosDebug.java
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosDebug {
|
||||
|
||||
public static final byte WR_CONFIG = 0x1d;
|
||||
public static final byte RD_CONFIG = 0x24;
|
||||
public static final byte CONFIG_TIMERS_OFF = (1 << 3);
|
||||
public static final byte CONFIG_DMA_PAUSE = (1 << 2);
|
||||
public static final byte CONFIG_TIMER_SUSPEND = (1 << 1);
|
||||
public static final byte SET_FLASH_INFO_PAGE = (1 << 0);
|
||||
|
||||
public static final byte GET_PC = 0x28;
|
||||
public static final byte READ_STATUS = 0x34;
|
||||
public static final byte STATUS_CHIP_ERASE_DONE = (byte) (1 << 7);
|
||||
public static final byte STATUS_PCON_IDLE = (1 << 6);
|
||||
public static final byte STATUS_CPU_HALTED = (1 << 5);
|
||||
public static final byte STATUS_POWER_MODE_0 = (1 << 4);
|
||||
public static final byte STATUS_HALT_STATUS = (1 << 3);
|
||||
public static final byte STATUS_DEBUG_LOCKED = (1 << 2);
|
||||
public static final byte STATUS_OSCILLATOR_STABLE = (1 << 1);
|
||||
public static final byte STATUS_STACK_OVERFLOW = (1 << 0);
|
||||
|
||||
public static final byte SET_HW_BRKPNT = 0x3b;
|
||||
public static byte HW_BRKPNT_N(byte n) { return (byte) ((n) << 3); }
|
||||
public static final byte HW_BRKPNT_N_MASK = (0x3 << 3);
|
||||
public static final byte HW_BRKPNT_ENABLE = (1 << 2);
|
||||
|
||||
public static final byte HALT = 0x44;
|
||||
public static final byte RESUME = 0x4c;
|
||||
public static byte DEBUG_INSTR(byte n) { return (byte) (0x54|(n)); }
|
||||
public static final byte STEP_INSTR = 0x5c;
|
||||
public static byte STEP_REPLACE(byte n) { return (byte) (0x64|(n)); }
|
||||
public static final byte GET_CHIP_ID = 0x68;
|
||||
|
||||
|
||||
private AltosLink link;
|
||||
|
||||
boolean debug_mode;
|
||||
|
||||
void ensure_debug_mode() throws InterruptedException {
|
||||
if (!debug_mode) {
|
||||
link.printf("D\n");
|
||||
link.flush_input();
|
||||
debug_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_memory(String header, int address, byte[] bytes, int start, int len) {
|
||||
System.out.printf("%s\n", header);
|
||||
for (int j = 0; j < len; j++) {
|
||||
if ((j & 15) == 0) {
|
||||
if (j != 0)
|
||||
System.out.printf("\n");
|
||||
System.out.printf ("%04x:", address + j);
|
||||
}
|
||||
System.out.printf(" %02x", bytes[start + j]);
|
||||
}
|
||||
System.out.printf("\n");
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
link.close();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write target memory
|
||||
*/
|
||||
public void write_memory(int address, byte[] bytes, int start, int len) throws InterruptedException {
|
||||
ensure_debug_mode();
|
||||
// dump_memory("write_memory", address, bytes, start, len);
|
||||
link.printf("O %x %x\n", len, address);
|
||||
for (int i = 0; i < len; i++)
|
||||
link.printf("%02x", bytes[start + i]);
|
||||
}
|
||||
|
||||
public void write_memory(int address, byte[] bytes) throws InterruptedException {
|
||||
write_memory(address, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read target memory
|
||||
*/
|
||||
public byte[] read_memory(int address, int length)
|
||||
throws IOException, InterruptedException {
|
||||
byte[] data = new byte[length];
|
||||
|
||||
link.flush_input();
|
||||
ensure_debug_mode();
|
||||
link.printf("I %x %x\n", length, address);
|
||||
int i = 0;
|
||||
int start = 0;
|
||||
while (i < length) {
|
||||
String line = link.get_reply();
|
||||
if (line == null)
|
||||
throw new IOException("No reply");
|
||||
|
||||
line = line.trim();
|
||||
if (!AltosLib.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) ((AltosLib.fromhex(line.charAt(j*2)) << 4) +
|
||||
AltosLib.fromhex(line.charAt(j*2+1)));
|
||||
start += this_time;
|
||||
i += this_time;
|
||||
}
|
||||
// dump_memory("read_memory", address, data, 0, length);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write raw bytes to the debug link using the 'P' command
|
||||
*/
|
||||
public void write_bytes(byte[] bytes) throws IOException, InterruptedException {
|
||||
int i = 0;
|
||||
ensure_debug_mode();
|
||||
while (i < bytes.length) {
|
||||
int this_time = bytes.length - i;
|
||||
if (this_time > 8)
|
||||
this_time = 0;
|
||||
link.printf("P");
|
||||
for (int j = 0; j < this_time; j++)
|
||||
link.printf(" %02x", bytes[i+j]);
|
||||
link.printf("\n");
|
||||
i += this_time;
|
||||
}
|
||||
}
|
||||
|
||||
public void write_byte(byte b) throws IOException, InterruptedException {
|
||||
byte[] bytes = { b };
|
||||
write_bytes(bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read raw bytes from the debug link using the 'G' command
|
||||
*/
|
||||
public byte[] read_bytes(int length)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
link.flush_input();
|
||||
ensure_debug_mode();
|
||||
link.printf("G %x\n", length);
|
||||
int i = 0;
|
||||
byte[] data = new byte[length];
|
||||
while (i < length) {
|
||||
String line = link.get_reply();
|
||||
|
||||
if (line == null)
|
||||
throw new IOException("Timeout in read_bytes");
|
||||
line = line.trim();
|
||||
String tokens[] = line.split("\\s+");
|
||||
for (int j = 0; j < tokens.length; j++) {
|
||||
if (!AltosLib.ishex(tokens[j]) ||
|
||||
tokens[j].length() != 2)
|
||||
throw new IOException(
|
||||
String.format
|
||||
("Invalid read_bytes reply \"%s\"", line));
|
||||
try {
|
||||
if (i + j >= length)
|
||||
throw new IOException(
|
||||
String.format
|
||||
("Invalid read_bytes reply \"%s\"", line));
|
||||
else
|
||||
data[i + j] = (byte) Integer.parseInt(tokens[j], 16);
|
||||
} catch (NumberFormatException ne) {
|
||||
throw new IOException(
|
||||
String.format
|
||||
("Invalid read_bytes reply \"%s\"", line));
|
||||
}
|
||||
}
|
||||
i += tokens.length;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public byte read_byte() throws IOException, InterruptedException {
|
||||
return read_bytes(1)[0];
|
||||
}
|
||||
|
||||
public byte debug_instr(byte[] instruction) throws IOException, InterruptedException {
|
||||
byte[] command = new byte[1 + instruction.length];
|
||||
command[0] = DEBUG_INSTR((byte) instruction.length);
|
||||
for (int i = 0; i < instruction.length; i++)
|
||||
command[i+1] = instruction[i];
|
||||
write_bytes(command);
|
||||
return read_byte();
|
||||
}
|
||||
|
||||
public byte resume() throws IOException, InterruptedException {
|
||||
write_byte(RESUME);
|
||||
return read_byte();
|
||||
}
|
||||
|
||||
public int read_uint16() throws IOException, InterruptedException {
|
||||
byte[] d = read_bytes(2);
|
||||
return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff);
|
||||
}
|
||||
|
||||
public int read_uint8() throws IOException, InterruptedException {
|
||||
byte[] d = read_bytes(1);
|
||||
return (int) (d[0] & 0xff);
|
||||
}
|
||||
|
||||
public int get_chip_id() throws IOException, InterruptedException {
|
||||
write_byte(GET_CHIP_ID);
|
||||
return read_uint16();
|
||||
}
|
||||
|
||||
public int get_pc() throws IOException, InterruptedException {
|
||||
write_byte(GET_PC);
|
||||
return read_uint16();
|
||||
}
|
||||
|
||||
public byte read_status() throws IOException, InterruptedException {
|
||||
write_byte(READ_STATUS);
|
||||
return read_byte();
|
||||
}
|
||||
|
||||
static final byte LJMP = 0x02;
|
||||
|
||||
public void set_pc(int pc) throws IOException, InterruptedException {
|
||||
byte high = (byte) (pc >> 8);
|
||||
byte low = (byte) pc;
|
||||
byte[] jump_mem = { LJMP, high, low };
|
||||
debug_instr(jump_mem);
|
||||
}
|
||||
|
||||
public boolean check_connection() throws IOException, InterruptedException {
|
||||
byte reply = read_status();
|
||||
if ((reply & STATUS_CHIP_ERASE_DONE) == 0)
|
||||
return false;
|
||||
if ((reply & STATUS_PCON_IDLE) != 0)
|
||||
return false;
|
||||
if ((reply & STATUS_POWER_MODE_0) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public AltosRomconfig romconfig() throws InterruptedException {
|
||||
try {
|
||||
byte[] bytes = read_memory(0x00, 0x200);
|
||||
AltosHexfile hexfile = new AltosHexfile (bytes, 0x00);
|
||||
return new AltosRomconfig(hexfile);
|
||||
} catch (IOException ie) {
|
||||
}
|
||||
return new AltosRomconfig();
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset target
|
||||
*/
|
||||
public void reset() {
|
||||
link.printf ("R\n");
|
||||
}
|
||||
|
||||
public AltosDebug (AltosLink link) {
|
||||
this.link = link;
|
||||
}
|
||||
}
|
||||
112
altoslib/AltosDistance.java
Normal file
112
altoslib/AltosDistance.java
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosDistance extends AltosUnits {
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.meters_to_miles(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.miles_to_meters(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "miles";
|
||||
return "m";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "miles";
|
||||
return "meters";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return width / 3;
|
||||
return width / 9;
|
||||
}
|
||||
|
||||
public int say_fraction(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public AltosDistance() {
|
||||
range_metric = new AltosUnitsRange[2];
|
||||
|
||||
range_metric[0] = new AltosUnitsRange(0, "m", "meters") {
|
||||
double value(double v) {
|
||||
return v;
|
||||
}
|
||||
int show_fraction(int width) {
|
||||
return width / 9;
|
||||
}
|
||||
int say_fraction() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
range_metric[1] = new AltosUnitsRange(2000, "km", "kilometers") {
|
||||
double value(double v) {
|
||||
return v / 1000;
|
||||
}
|
||||
int show_fraction(int width) {
|
||||
return width / 5;
|
||||
}
|
||||
int say_fraction() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
range_imperial = new AltosUnitsRange[2];
|
||||
|
||||
range_imperial[0] = new AltosUnitsRange(0, "ft", "feet") {
|
||||
double value(double v) {
|
||||
return AltosConvert.meters_to_feet(v);
|
||||
}
|
||||
int show_fraction(int width) {
|
||||
return width / 9;
|
||||
}
|
||||
int say_fraction() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
range_imperial[1] = new AltosUnitsRange(AltosConvert.feet_to_meters(5280),
|
||||
"mi", "miles") {
|
||||
double value(double v) {
|
||||
return AltosConvert.meters_to_miles(v);
|
||||
}
|
||||
int show_fraction(int width) {
|
||||
return width / 5;
|
||||
}
|
||||
int say_fraction() {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
292
altoslib/AltosEeprom.java
Normal file
292
altoslib/AltosEeprom.java
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
public class AltosEeprom {
|
||||
|
||||
private AltosJson config;
|
||||
ArrayList<Byte> data;
|
||||
private AltosConfigData config_data;
|
||||
int errors = 0;
|
||||
|
||||
/*
|
||||
* Public accessor APIs
|
||||
*/
|
||||
public int data8(int offset) {
|
||||
return ((int) data.get(offset)) & 0xff;
|
||||
}
|
||||
|
||||
public int data16(int offset) {
|
||||
return data8(offset) | (data8(offset+1) << 8);
|
||||
}
|
||||
|
||||
public int data24(int offset) {
|
||||
return (data8(offset) |
|
||||
(data8(offset+1) << 8) |
|
||||
(data8(offset+2) << 16));
|
||||
}
|
||||
|
||||
public int data32(int offset) {
|
||||
return (data8(offset) |
|
||||
(data8(offset+1) << 8) |
|
||||
(data8(offset+2) << 16) |
|
||||
(data8(offset+3) << 24));
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
public AltosConfigData config_data() {
|
||||
if (config_data == null) {
|
||||
config_data = (AltosConfigData) config.make(AltosConfigData.class);
|
||||
if (config_data == null)
|
||||
config_data = new AltosConfigData();
|
||||
|
||||
if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN) {
|
||||
config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
|
||||
if (config_data.product != null) {
|
||||
if (config_data.product.startsWith("TeleMetrum"))
|
||||
config_data.log_format = AltosLib.AO_LOG_FORMAT_FULL;
|
||||
else if (config_data.product.startsWith("TeleMini"))
|
||||
config_data.log_format = AltosLib.AO_LOG_FORMAT_TINY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return config_data;
|
||||
}
|
||||
|
||||
private void write_config(Writer w) throws IOException {
|
||||
config.write(w, 0, true);
|
||||
w.append('\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* Private I/O APIs
|
||||
*/
|
||||
private void write_data(Writer w) throws IOException {
|
||||
PrintWriter pw = new PrintWriter(w);
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
if (i > 0) {
|
||||
if ((i & 0x1f) == 0)
|
||||
pw.printf("\n");
|
||||
else
|
||||
pw.printf(" ");
|
||||
}
|
||||
pw.printf("%02x", data.get(i));
|
||||
}
|
||||
w.append('\n');
|
||||
}
|
||||
|
||||
private boolean read_config(InputStream stream) throws IOException {
|
||||
config = AltosJson.fromInputStream(stream);
|
||||
if (config == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private String read_line(InputStream stream) throws IOException {
|
||||
StringBuffer buffer = null;
|
||||
int c;
|
||||
|
||||
for (;;) {
|
||||
c = stream.read();
|
||||
if (c == -1 && buffer == null)
|
||||
return null;
|
||||
if (buffer == null)
|
||||
buffer = new StringBuffer();
|
||||
if (c == -1 || c == '\n')
|
||||
return buffer.toString();
|
||||
buffer.append((char) c);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean read_data(InputStream stream) throws IOException {
|
||||
String s;
|
||||
|
||||
data = new ArrayList<Byte>();
|
||||
while ((s = read_line(stream)) != null) {
|
||||
|
||||
String[] tokens = s.split("\\s+");
|
||||
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
if (tokens[i].length() > 0) {
|
||||
try {
|
||||
data.add((byte) AltosLib.fromhex(tokens[i]));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IOException(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean read_old_config(InputStream stream) throws IOException {
|
||||
AltosConfigData cfg = new AltosConfigData();
|
||||
for (;;) {
|
||||
boolean done = false;
|
||||
|
||||
/* The data starts with an upper case F character followed by a space */
|
||||
stream.mark(2);
|
||||
int first = stream.read();
|
||||
if (first == 'F') {
|
||||
int second = stream.read();
|
||||
if (second == ' ')
|
||||
done = true;
|
||||
}
|
||||
stream.reset();
|
||||
if (done)
|
||||
break;
|
||||
|
||||
String line = read_line(stream);
|
||||
if (line == null)
|
||||
return false;
|
||||
cfg.parse_line(line);
|
||||
}
|
||||
config = new AltosJson(cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean read_old_data(InputStream stream) throws IOException {
|
||||
String line;
|
||||
|
||||
data = new ArrayList<Byte>();
|
||||
while ((line = read_line(stream)) != null) {
|
||||
String[] tokens = line.split("\\s+");
|
||||
|
||||
/* Make sure there's at least a type and time */
|
||||
if (tokens.length < 2)
|
||||
break;
|
||||
|
||||
/* packet type */
|
||||
if (tokens[0].length() != 1)
|
||||
break;
|
||||
int start = data.size();
|
||||
|
||||
if (config_data().log_format != AltosLib.AO_LOG_FORMAT_TINY) {
|
||||
byte cmd = (byte) tokens[0].codePointAt(0);
|
||||
data.add(cmd);
|
||||
|
||||
int time = AltosLib.fromhex(tokens[1]);
|
||||
|
||||
data.add((byte) 0);
|
||||
data.add((byte) (time & 0xff));
|
||||
data.add((byte) (time >> 8));
|
||||
}
|
||||
if (tokens.length == 4) {
|
||||
/* Handle ancient log files */
|
||||
if (config_data().log_format == AltosLib.AO_LOG_FORMAT_TINY) {
|
||||
/*
|
||||
* Ancient TeleMini log files stored "extra" data to pretend
|
||||
* that it was a TeleMetrum device. Throw that away and
|
||||
* just save the actual log data.
|
||||
*/
|
||||
int a = AltosLib.fromhex(tokens[2]);
|
||||
int b = AltosLib.fromhex(tokens[3]);
|
||||
if (a != 0)
|
||||
b = 0x8000 | a;
|
||||
data.add((byte) (b & 0xff));
|
||||
data.add((byte) ((b >> 8)));
|
||||
} else {
|
||||
for (int i = 2; i < tokens.length; i++) {
|
||||
int v = AltosLib.fromhex(tokens[i]);
|
||||
data.add((byte) (v & 0xff));
|
||||
data.add((byte) ((v >> 8)));
|
||||
}
|
||||
/* Re-compute the checksum byte */
|
||||
data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
|
||||
}
|
||||
} else {
|
||||
for (int i = 2; i < tokens.length; i++)
|
||||
data.add((byte) AltosLib.fromhex(tokens[i]));
|
||||
/* Re-compute the checksum byte */
|
||||
data.set(start + 1, (byte) (256 - AltosConvert.checksum(data, start, data.size() - start)));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void read(InputStream stream) throws IOException {
|
||||
BufferedInputStream bis = new BufferedInputStream(stream);
|
||||
|
||||
bis.mark(1);
|
||||
int c = bis.read();
|
||||
bis.reset();
|
||||
|
||||
if (c == '{') {
|
||||
if (!read_config(bis))
|
||||
throw new IOException("failed to read config");
|
||||
if (!read_data(bis))
|
||||
throw new IOException("failed to read data");
|
||||
} else {
|
||||
if (!read_old_config(bis))
|
||||
throw new IOException("failed to read old config");
|
||||
if (!read_old_data(bis))
|
||||
throw new IOException("failed to read old data");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Public APIs for I/O
|
||||
*/
|
||||
public void write(Writer w) throws IOException {
|
||||
write_config(w);
|
||||
write_data(w);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
try {
|
||||
Writer w = new StringWriter();
|
||||
|
||||
write(w);
|
||||
return w.toString();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void print() throws IOException {
|
||||
System.out.printf("%s", toString());
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructors
|
||||
*/
|
||||
public AltosEeprom(InputStream stream) throws IOException {
|
||||
read(stream);
|
||||
}
|
||||
|
||||
public AltosEeprom(String s) throws IOException {
|
||||
read(new AltosStringInputStream(s));
|
||||
}
|
||||
|
||||
public AltosEeprom(AltosJson config, ArrayList<Byte> data) {
|
||||
this.config = config;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public AltosEeprom(AltosConfigData config_data, ArrayList<Byte> data) {
|
||||
this.config = new AltosJson(config_data);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public AltosEeprom() {
|
||||
}
|
||||
}
|
||||
105
altoslib/AltosEepromChunk.java
Normal file
105
altoslib/AltosEepromChunk.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosEepromChunk {
|
||||
|
||||
public static final int chunk_size = 256;
|
||||
public static final int per_line = 8;
|
||||
|
||||
public int data[];
|
||||
public int address;
|
||||
public ParseException parse_exception = null;
|
||||
|
||||
int[] ParseHex(String line) {
|
||||
String[] tokens = line.split("\\s+");
|
||||
int[] array = new int[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; i++)
|
||||
try {
|
||||
array[i] = Integer.parseInt(tokens[i], 16);
|
||||
} catch (NumberFormatException ne) {
|
||||
return null;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public int data(int offset) {
|
||||
return data[offset];
|
||||
}
|
||||
|
||||
public int data16(int offset) {
|
||||
return data[offset] | (data[offset + 1] << 8);
|
||||
}
|
||||
|
||||
public int data32(int offset) {
|
||||
return data[offset] | (data[offset + 1] << 8) |
|
||||
(data[offset+2] << 16) | (data[offset+3] << 24);
|
||||
}
|
||||
|
||||
public boolean erased(int start, int len) {
|
||||
for (int i = 0; i < len; i++)
|
||||
if (data[start+i] != 0xff)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean erased() {
|
||||
return erased(0, chunk_size);
|
||||
}
|
||||
|
||||
public AltosEepromChunk(AltosLink link, int block, boolean flush)
|
||||
throws TimeoutException, InterruptedException {
|
||||
|
||||
int offset;
|
||||
|
||||
data = new int[chunk_size];
|
||||
address = block * chunk_size;
|
||||
if (flush)
|
||||
link.flush_input();
|
||||
link.printf("e %x\n", block);
|
||||
|
||||
for (offset = 0; offset < chunk_size; offset += per_line) {
|
||||
try {
|
||||
String line = link.get_reply(5000);
|
||||
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
|
||||
int[] values = ParseHex(line);
|
||||
|
||||
if (values == null || values.length != per_line + 1)
|
||||
throw new ParseException(String.format("invalid line %s", line), 0);
|
||||
if (values[0] != offset)
|
||||
throw new ParseException(String.format("data address out of sync at 0x%x",
|
||||
address + offset), 0);
|
||||
for (int i = 0; i < per_line; i++)
|
||||
data[offset + i] = values[1 + i];
|
||||
} catch (ParseException pe) {
|
||||
for (int i = 0; i < per_line; i++)
|
||||
data[offset + i] = 0xff;
|
||||
if (parse_exception == null)
|
||||
parse_exception = pe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
319
altoslib/AltosEepromDownload.java
Normal file
319
altoslib/AltosEepromDownload.java
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
class AltosEepromNameData extends AltosDataListener {
|
||||
AltosGPS gps = null;
|
||||
|
||||
boolean avoid_duplicate_files = false;
|
||||
|
||||
public void set_rssi(int rssi, int status) { }
|
||||
public void set_received_time(long received_time) { }
|
||||
|
||||
public void set_acceleration(double accel) { }
|
||||
public void set_pressure(double pa) { }
|
||||
public void set_thrust(double N) { }
|
||||
|
||||
public void set_temperature(double deg_c) { }
|
||||
public void set_battery_voltage(double volts) { }
|
||||
|
||||
public void set_apogee_voltage(double volts) { }
|
||||
public void set_main_voltage(double volts) { }
|
||||
|
||||
public void set_avoid_duplicate_files() {
|
||||
avoid_duplicate_files = true;
|
||||
}
|
||||
|
||||
public void set_gps(AltosGPS gps, boolean set_location, boolean set_sats) {
|
||||
super.set_gps(gps, set_location, set_sats);
|
||||
if (gps != null &&
|
||||
gps.year != AltosLib.MISSING &&
|
||||
gps.month != AltosLib.MISSING &&
|
||||
gps.day != AltosLib.MISSING) {
|
||||
this.gps = gps;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean done() {
|
||||
if (gps == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void set_gyro(double roll, double pitch, double yaw) { }
|
||||
public void set_accel_ground(double along, double across, double through) { }
|
||||
public void set_accel(double along, double across, double through) { }
|
||||
public void set_mag(double along, double across, double through) { }
|
||||
public void set_pyro_voltage(double volts) { }
|
||||
public void set_igniter_voltage(double[] voltage) { }
|
||||
public void set_pyro_fired(int pyro_mask) { }
|
||||
public void set_companion(AltosCompanion companion) { }
|
||||
public void set_kalman(double height, double speed, double acceleration) { }
|
||||
public void set_orient(double new_orient) { }
|
||||
public void set_motor_pressure(double motor_pressure) { }
|
||||
|
||||
public AltosEepromNameData(AltosCalData cal_data) {
|
||||
super(cal_data);
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosEepromDownload implements Runnable {
|
||||
|
||||
AltosLink link;
|
||||
boolean remote;
|
||||
Thread eeprom_thread;
|
||||
AltosEepromMonitor monitor;
|
||||
|
||||
AltosEepromList flights;
|
||||
String parse_errors;
|
||||
|
||||
private boolean has_gps_date(AltosState state) {
|
||||
if (state == null)
|
||||
return false;
|
||||
|
||||
AltosGPS gps = state.gps;
|
||||
|
||||
return gps != null &&
|
||||
gps.year != AltosLib.MISSING &&
|
||||
gps.month != AltosLib.MISSING &&
|
||||
gps.day != AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private AltosFile MakeFile(int serial, int flight, AltosEepromNameData name_data) throws IOException {
|
||||
AltosFile eeprom_name;
|
||||
|
||||
for (;;) {
|
||||
if (name_data.gps != null) {
|
||||
AltosGPS gps = name_data.gps;
|
||||
eeprom_name = new AltosFile(gps.year, gps.month, gps.day,
|
||||
serial, flight, "eeprom");
|
||||
} else
|
||||
eeprom_name = new AltosFile(serial, flight, "eeprom");
|
||||
if (!name_data.avoid_duplicate_files)
|
||||
break;
|
||||
if (!eeprom_name.exists())
|
||||
break;
|
||||
flight++;
|
||||
}
|
||||
|
||||
return eeprom_name;
|
||||
}
|
||||
|
||||
boolean done;
|
||||
int prev_state;
|
||||
int state_block;
|
||||
|
||||
void LogError(String error) {
|
||||
if (parse_errors != null)
|
||||
parse_errors.concat(error.concat("\n"));
|
||||
else
|
||||
parse_errors = error;
|
||||
}
|
||||
|
||||
class BlockCache extends Hashtable<Integer,AltosEepromChunk> {
|
||||
AltosEepromLog log;
|
||||
|
||||
AltosEepromChunk get(int start, boolean add) throws TimeoutException, InterruptedException {
|
||||
if (contains(start))
|
||||
return super.get(start);
|
||||
AltosEepromChunk eechunk = new AltosEepromChunk(link, start, start == log.start_block);
|
||||
if (add)
|
||||
put(start, eechunk);
|
||||
return eechunk;
|
||||
}
|
||||
|
||||
public BlockCache(AltosEepromLog log) {
|
||||
this.log = log;
|
||||
}
|
||||
}
|
||||
|
||||
int FindLastLog(AltosEepromLog log, BlockCache cache) throws TimeoutException, InterruptedException {
|
||||
int low = log.start_block;
|
||||
int high = log.end_block - 1;
|
||||
|
||||
while (low <= high) {
|
||||
int mid = (high + low) / 2;
|
||||
|
||||
if (!cache.get(mid, true).erased())
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid - 1;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
void CaptureLog(AltosEepromLog log) throws IOException, InterruptedException, TimeoutException, ParseException {
|
||||
int block, state_block = 0;
|
||||
int log_format = flights.config_data.log_format;
|
||||
BlockCache cache = new BlockCache(log);
|
||||
|
||||
done = false;
|
||||
|
||||
if (flights.config_data.serial < 0)
|
||||
throw new IOException("no serial number found");
|
||||
|
||||
/* Set serial number in the monitor dialog window */
|
||||
monitor.set_serial(log.serial);
|
||||
monitor.set_flight(log.flight);
|
||||
|
||||
int start_block = log.start_block;
|
||||
int end_block = FindLastLog(log, cache);
|
||||
|
||||
monitor.set_max(end_block - start_block - 1);
|
||||
|
||||
ArrayList<Byte> data = new ArrayList<Byte>();
|
||||
|
||||
/* Now scan the eeprom, reading blocks of data to create a byte array of data */
|
||||
|
||||
for (block = start_block; block < end_block; block++) {
|
||||
monitor.set_block(block - start_block);
|
||||
|
||||
AltosEepromChunk eechunk = cache.get(block, false);
|
||||
|
||||
for (int i = 0; i < eechunk.data.length; i++)
|
||||
data.add((byte) eechunk.data[i]);
|
||||
}
|
||||
|
||||
/* Construct our internal representation of the eeprom data */
|
||||
AltosEeprom eeprom = new AltosEeprom(flights.config_data, data);
|
||||
|
||||
/* Now see if we can't actually parse the resulting
|
||||
* file to generate a better filename. Note that this
|
||||
* doesn't need to work; we'll still save the data using
|
||||
* a less accurate name.
|
||||
*/
|
||||
AltosEepromRecordSet set = new AltosEepromRecordSet(eeprom);
|
||||
AltosEepromNameData name_data = new AltosEepromNameData(set.cal_data());
|
||||
|
||||
for (AltosEepromRecord record : set.ordered) {
|
||||
record.provide_data(name_data, set.cal_data());
|
||||
if (name_data.done())
|
||||
break;
|
||||
}
|
||||
|
||||
AltosFile f = MakeFile(flights.config_data.serial, log.flight, name_data);
|
||||
|
||||
log.set_file(f);
|
||||
|
||||
boolean do_write = true;
|
||||
|
||||
if (f.exists())
|
||||
do_write = monitor.check_overwrite(f);
|
||||
|
||||
if (do_write) {
|
||||
FileWriter w = new FileWriter(f);
|
||||
|
||||
eeprom.write(w);
|
||||
w.close();
|
||||
}
|
||||
|
||||
if (eeprom.errors != 0)
|
||||
throw new ParseException(String.format("%d CRC Errors", eeprom.errors), 0);
|
||||
}
|
||||
|
||||
static String label(int flight) {
|
||||
if (flight < 0)
|
||||
return "Corrupt";
|
||||
else
|
||||
return "Flight";
|
||||
}
|
||||
|
||||
static int flight(int flight) {
|
||||
if (flight < 0)
|
||||
return -flight;
|
||||
return flight;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
boolean success = false;
|
||||
|
||||
try {
|
||||
if (remote)
|
||||
link.start_remote();
|
||||
|
||||
for (AltosEepromLog log : flights) {
|
||||
parse_errors = null;
|
||||
if (log.download_selected) {
|
||||
monitor.reset();
|
||||
try {
|
||||
CaptureLog(log);
|
||||
} catch (ParseException e) {
|
||||
LogError(e.getMessage());
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
if (parse_errors != null) {
|
||||
monitor.show_message(String.format("%s %d download error. Valid log data saved\n%s",
|
||||
label(log.flight),
|
||||
flight(log.flight),
|
||||
parse_errors),
|
||||
link.name,
|
||||
AltosEepromMonitor.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
} catch (IOException ee) {
|
||||
monitor.show_message(ee.getLocalizedMessage(),
|
||||
link.name,
|
||||
AltosEepromMonitor.ERROR_MESSAGE);
|
||||
} catch (InterruptedException ie) {
|
||||
monitor.show_message(String.format("Connection to \"%s\" interrupted",
|
||||
link.name),
|
||||
"Connection Interrupted",
|
||||
AltosEepromMonitor.ERROR_MESSAGE);
|
||||
} catch (TimeoutException te) {
|
||||
monitor.show_message(String.format("Connection to \"%s\" failed",
|
||||
link.name),
|
||||
"Connection Failed",
|
||||
AltosEepromMonitor.ERROR_MESSAGE);
|
||||
} finally {
|
||||
if (remote) {
|
||||
try {
|
||||
link.stop_remote();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
link.flush_output();
|
||||
}
|
||||
monitor.done(success);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
eeprom_thread = new Thread(this);
|
||||
monitor.set_thread(eeprom_thread);
|
||||
eeprom_thread.start();
|
||||
}
|
||||
|
||||
public AltosEepromDownload(AltosEepromMonitor given_monitor,
|
||||
AltosLink given_link,
|
||||
boolean given_remote,
|
||||
AltosEepromList given_flights) {
|
||||
|
||||
monitor = given_monitor;
|
||||
link = given_link;
|
||||
remote = given_remote;
|
||||
flights = given_flights;
|
||||
|
||||
monitor.start();
|
||||
}
|
||||
}
|
||||
55
altoslib/AltosEepromFile.java
Normal file
55
altoslib/AltosEepromFile.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
|
||||
public class AltosEepromFile implements AltosRecordSet {
|
||||
|
||||
AltosEepromRecordSet set;
|
||||
|
||||
public void write_comments(PrintStream out) {
|
||||
}
|
||||
|
||||
public void write(PrintStream out) {
|
||||
out.printf("%s\n", set.eeprom.toString());
|
||||
}
|
||||
|
||||
public AltosEepromFile(InputStream input) throws IOException {
|
||||
set = new AltosEepromRecordSet(input);
|
||||
}
|
||||
|
||||
public AltosConfigData config_data() {
|
||||
return set.config_data();
|
||||
}
|
||||
|
||||
public AltosCalData cal_data() {
|
||||
return set.cal_data();
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return set.valid();
|
||||
}
|
||||
|
||||
public void capture_series(AltosDataListener series) {
|
||||
set.capture_series(series);
|
||||
}
|
||||
}
|
||||
82
altoslib/AltosEepromList.java
Normal file
82
altoslib/AltosEepromList.java
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/*
|
||||
* Temporary structure to hold the list of stored flights;
|
||||
* each of these will be queried in turn to generate more
|
||||
* complete information
|
||||
*/
|
||||
|
||||
class AltosEepromFlight {
|
||||
int flight;
|
||||
int start;
|
||||
int end;
|
||||
|
||||
public AltosEepromFlight(int in_flight, int in_start, int in_end) {
|
||||
flight = in_flight;
|
||||
start = in_start;
|
||||
end = in_end;
|
||||
}
|
||||
|
||||
public AltosEepromFlight() {
|
||||
flight = 0;
|
||||
start = 0;
|
||||
end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a list of flights available in a connected device
|
||||
*/
|
||||
|
||||
public class AltosEepromList extends ArrayList<AltosEepromLog> {
|
||||
public AltosConfigData config_data;
|
||||
|
||||
public AltosEepromList (AltosLink link, boolean remote)
|
||||
throws IOException, InterruptedException, TimeoutException
|
||||
{
|
||||
try {
|
||||
if (remote)
|
||||
link.start_remote();
|
||||
config_data = new AltosConfigData (link);
|
||||
|
||||
/* With the list of flights collected, collect more complete
|
||||
* information on them by reading the first block or two of
|
||||
* data. This will add GPS coordinates and a date. For older
|
||||
* firmware, this will also extract the flight number.
|
||||
*/
|
||||
if (config_data.flights != null) {
|
||||
for (AltosEepromFlight flight : config_data.flights) {
|
||||
add(new AltosEepromLog(config_data, link,
|
||||
flight.flight, flight.start, flight.end));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (remote)
|
||||
link.stop_remote();
|
||||
link.flush_output();
|
||||
}
|
||||
}
|
||||
}
|
||||
69
altoslib/AltosEepromLog.java
Normal file
69
altoslib/AltosEepromLog.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/*
|
||||
* Extract a bit of information from an eeprom-stored flight log.
|
||||
*/
|
||||
|
||||
public class AltosEepromLog {
|
||||
public int serial;
|
||||
public boolean has_flight;
|
||||
public int flight;
|
||||
public int start_block;
|
||||
public int end_block;
|
||||
|
||||
public boolean download_selected;
|
||||
public boolean delete_selected;
|
||||
public boolean graph_selected;
|
||||
|
||||
public File file;
|
||||
|
||||
public void set_file(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public AltosEepromLog(AltosConfigData config_data,
|
||||
AltosLink link,
|
||||
int in_flight, int in_start_block,
|
||||
int in_end_block)
|
||||
throws InterruptedException, TimeoutException {
|
||||
|
||||
int block;
|
||||
|
||||
flight = in_flight;
|
||||
if (flight != 0)
|
||||
has_flight = true;
|
||||
start_block = in_start_block;
|
||||
end_block = in_end_block;
|
||||
serial = config_data.serial;
|
||||
|
||||
/*
|
||||
* Select all flights for download and graph, but not
|
||||
* for delete
|
||||
*/
|
||||
download_selected = true;
|
||||
delete_selected = false;
|
||||
graph_selected = true;
|
||||
}
|
||||
}
|
||||
48
altoslib/AltosEepromMonitor.java
Normal file
48
altoslib/AltosEepromMonitor.java
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface AltosEepromMonitor {
|
||||
|
||||
public void set_block(int in_block);
|
||||
|
||||
public void set_max(int in_max);
|
||||
|
||||
public void set_serial(int in_serial);
|
||||
|
||||
public void set_flight(int in_flight);
|
||||
|
||||
public void set_thread(Thread eeprom_thread);
|
||||
|
||||
final static int INFO_MESSAGE = 0;
|
||||
final static int WARNING_MESSAGE = 1;
|
||||
final static int ERROR_MESSAGE = 2;
|
||||
|
||||
public void show_message(String message, String title, int message_type);
|
||||
|
||||
public Boolean check_overwrite(File file);
|
||||
|
||||
public void start();
|
||||
|
||||
public void done(boolean success);
|
||||
|
||||
public void reset();
|
||||
}
|
||||
131
altoslib/AltosEepromRecord.java
Normal file
131
altoslib/AltosEepromRecord.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public abstract class AltosEepromRecord implements Comparable<AltosEepromRecord> {
|
||||
|
||||
AltosEeprom eeprom;
|
||||
|
||||
int wide_tick;
|
||||
|
||||
final int start;
|
||||
final int length;
|
||||
|
||||
public final static int header_length = 4;
|
||||
|
||||
public int cmd() {
|
||||
return eeprom.data8(start);
|
||||
}
|
||||
|
||||
public int tick() {
|
||||
return eeprom.data16(start+2);
|
||||
}
|
||||
|
||||
public int data8(int i) {
|
||||
i += start + header_length;
|
||||
return eeprom.data8(i);
|
||||
}
|
||||
|
||||
public int data16(int i) {
|
||||
return ((data8(i) | (data8(i+1) << 8)) << 16) >> 16;
|
||||
}
|
||||
|
||||
public int data24(int i) {
|
||||
return data8(i) | (data8(i+1) << 8) | (data8(i+2) << 16);
|
||||
}
|
||||
|
||||
public int data32(int i) {
|
||||
return data8(i) | (data8(i+1) << 8) | (data8(i+2) << 16) | (data8(i+3) << 24);
|
||||
}
|
||||
|
||||
public boolean empty(int s) {
|
||||
for (int i = 0; i < length; i++)
|
||||
if (eeprom.data8(s + i) != 0xff)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean valid(int s) {
|
||||
int ck = AltosConvert.checksum(eeprom.data, s, length);
|
||||
|
||||
if (ck != 0) {
|
||||
++eeprom.errors;
|
||||
System.out.printf("invalid checksum 0x%x at 0x%x\n", ck, s);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return valid(start);
|
||||
}
|
||||
|
||||
private int cmdi() {
|
||||
if (cmd() == AltosLib.AO_LOG_FLIGHT)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public AltosConfigData config_data() {
|
||||
return eeprom.config_data();
|
||||
}
|
||||
|
||||
public int compareTo(AltosEepromRecord o) {
|
||||
int cmd_diff = cmdi() - o.cmdi();
|
||||
|
||||
if (cmd_diff != 0)
|
||||
return cmd_diff;
|
||||
|
||||
int tick_diff = wide_tick - o.wide_tick;
|
||||
|
||||
if (tick_diff != 0)
|
||||
return tick_diff;
|
||||
return start - o.start;
|
||||
}
|
||||
|
||||
/* AltosDataProvider */
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
listener.set_tick(tick());
|
||||
if (cmd() == AltosLib.AO_LOG_FLIGHT)
|
||||
cal_data.set_boost_tick();
|
||||
listener.set_time(cal_data.time());
|
||||
|
||||
/* Flush any pending GPS changes */
|
||||
if (!AltosLib.is_gps_cmd(cmd())) {
|
||||
AltosGPS gps = listener.temp_gps();
|
||||
if (gps != null)
|
||||
listener.set_gps(gps, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public int next_start() {
|
||||
int s = start + length;
|
||||
|
||||
while (s + length <= eeprom.data.size()) {
|
||||
if (!empty(s) && valid(s))
|
||||
return s;
|
||||
s += length;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public abstract AltosEepromRecord next();
|
||||
|
||||
public AltosEepromRecord(AltosEeprom eeprom, int start, int length) {
|
||||
this.eeprom = eeprom;
|
||||
this.start = start;
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
102
altoslib/AltosEepromRecordFireTwo.java
Normal file
102
altoslib/AltosEepromRecordFireTwo.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
|
||||
public class AltosEepromRecordFireTwo extends AltosEepromRecord {
|
||||
public static final int record_length = 32;
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
public int flight() { return data16(0); }
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
public int state() { return data16(0); }
|
||||
public int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
public int pres() { return data16(0); }
|
||||
public int thrust() { return data16(2); }
|
||||
public int temp(int i) { return data16(4+i*2); }
|
||||
|
||||
private static final double r_above = 5600.0;
|
||||
private static final double r_below = 10000.0;
|
||||
private static final double v_adc = 3.3;
|
||||
|
||||
private static double firetwo_adc(int raw) {
|
||||
return raw / 4095.0;
|
||||
}
|
||||
|
||||
public static double adc_to_pa(int adc) {
|
||||
|
||||
/* raw adc to processor voltage, then back through the
|
||||
* voltage divider to the sensor voltage
|
||||
*/
|
||||
|
||||
double v = firetwo_adc(adc) * v_adc * (r_above + r_below) / r_below;
|
||||
|
||||
/* Bound to ranges provided in sensor */
|
||||
if (v < 0.5) v = 0.5;
|
||||
if (v > 4.5) v = 4.5;
|
||||
|
||||
double psi = (v - 0.5) / 4.0 * 2500.0;
|
||||
return AltosConvert.psi_to_pa(psi);
|
||||
}
|
||||
|
||||
public static double adc_to_n(int adc) {
|
||||
double v = firetwo_adc(adc);
|
||||
|
||||
/* this is a total guess */
|
||||
return AltosConvert.lb_to_n(v * 298 * 9.807);
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
listener.set_pressure(adc_to_pa(pres()));
|
||||
listener.set_thrust(adc_to_n(thrust()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordFireTwo(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordFireTwo(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordFireTwo(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
114
altoslib/AltosEepromRecordFull.java
Normal file
114
altoslib/AltosEepromRecordFull.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordFull extends AltosEepromRecord {
|
||||
public static final int record_length = 8;
|
||||
|
||||
public static final int max_sat = 12;
|
||||
|
||||
public static final int two_g_default = 16294 - 15758;
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
|
||||
super.provide_data(listener, cal_data);
|
||||
AltosGPS gps;
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
listener.set_state(AltosLib.ao_flight_pad);
|
||||
cal_data.set_ground_accel(data16(0));
|
||||
cal_data.set_flight(data16(2));
|
||||
if (cal_data.accel_plus_g == AltosLib.MISSING)
|
||||
cal_data.set_accel_plus_minus(data16(0), data16(0) + two_g_default);
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
listener.set_acceleration(cal_data.acceleration(data16(0)));
|
||||
listener.set_pressure(AltosConvert.barometer_to_pressure(data16(2)));
|
||||
break;
|
||||
case AltosLib.AO_LOG_PRESSURE:
|
||||
listener.set_pressure(AltosConvert.barometer_to_pressure(data16(2)));
|
||||
break;
|
||||
case AltosLib.AO_LOG_TEMP_VOLT:
|
||||
listener.set_temperature(AltosConvert.thermometer_to_temperature(data16(0)));
|
||||
listener.set_battery_voltage(AltosConvert.cc_battery_to_voltage(data16(2)));
|
||||
break;
|
||||
case AltosLib.AO_LOG_DEPLOY:
|
||||
listener.set_apogee_voltage(AltosConvert.cc_igniter_to_voltage(data16(0)));
|
||||
listener.set_main_voltage(AltosConvert.cc_igniter_to_voltage(data16(2)));
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(data16(0));
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
gps = listener.make_temp_gps(false);
|
||||
|
||||
gps.hour = data8(0);
|
||||
gps.minute = data8(1);
|
||||
gps.second = data8(2);
|
||||
|
||||
int flags = data8(3);
|
||||
|
||||
gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
|
||||
gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
|
||||
gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
|
||||
AltosLib.AO_GPS_NUM_SAT_SHIFT;
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_LAT:
|
||||
gps = listener.make_temp_gps(false);
|
||||
|
||||
int lat32 = data32(0);
|
||||
gps.lat = (double) lat32 / 1e7;
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_LON:
|
||||
gps = listener.make_temp_gps(false);
|
||||
|
||||
int lon32 = data32(0);
|
||||
gps.lon = (double) lon32 / 1e7;
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_ALT:
|
||||
gps = listener.make_temp_gps(false);
|
||||
gps.alt = data16(0);
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_SAT:
|
||||
gps = listener.make_temp_gps(true);
|
||||
int svid = data16(0);
|
||||
int c_n0 = data16(2);
|
||||
gps.add_sat(svid, c_n0);
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_DATE:
|
||||
gps = listener.make_temp_gps(false);
|
||||
gps.year = data8(0) + 2000;
|
||||
gps.month = data8(1);
|
||||
gps.day = data8(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordFull(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordFull(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordFull(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
145
altoslib/AltosEepromRecordGps.java
Normal file
145
altoslib/AltosEepromRecordGps.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
|
||||
public class AltosEepromRecordGps extends AltosEepromRecord {
|
||||
public static final int record_length = 32;
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
public int flight() { return data16(0); }
|
||||
public int start_altitude() { return data16(2); }
|
||||
public int start_latitude() { return data32(4); }
|
||||
public int start_longitude() { return data32(8); }
|
||||
|
||||
/* AO_LOG_GPS_TIME elements */
|
||||
public int latitude() { return data32(0); }
|
||||
public int longitude() { return data32(4); }
|
||||
public int altitude_low() { return data16(8); }
|
||||
public int hour() { return data8(10); }
|
||||
public int minute() { return data8(11); }
|
||||
public int second() { return data8(12); }
|
||||
public int flags() { return data8(13); }
|
||||
public int year() { return data8(14); }
|
||||
public int month() { return data8(15); }
|
||||
public int day() { return data8(16); }
|
||||
public int course() { return data8(17); }
|
||||
public int ground_speed() { return data16(18); }
|
||||
public int climb_rate() { return data16(20); }
|
||||
public int pdop() { return data8(22); }
|
||||
public int hdop() { return data8(23); }
|
||||
public int vdop() { return data8(24); }
|
||||
public int mode() { return data8(25); }
|
||||
public int altitude_high() { return data16(26); }
|
||||
|
||||
private int seconds() {
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
return second() + 60 * (minute() + 60 * (hour() + 24 * (day() + 31 * month())));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareTo(AltosEepromRecord o) {
|
||||
AltosEepromRecordGps og = (AltosEepromRecordGps) o;
|
||||
|
||||
int seconds_diff = seconds() - og.seconds();
|
||||
|
||||
if (seconds_diff != 0)
|
||||
return seconds_diff;
|
||||
|
||||
return start - o.start;
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
if (cal_data.flight == AltosLib.MISSING) {
|
||||
cal_data.set_boost_tick();
|
||||
cal_data.set_flight(flight());
|
||||
}
|
||||
/* no place to log start lat/lon yet */
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
AltosGPS gps = new AltosGPS();
|
||||
gps.lat = latitude() / 1e7;
|
||||
gps.lon = longitude() / 1e7;
|
||||
if (eeprom.config_data().altitude_32 == 1)
|
||||
gps.alt = (altitude_low() & 0xffff) | (altitude_high() << 16);
|
||||
else
|
||||
gps.alt = altitude_low();
|
||||
|
||||
gps.hour = hour();
|
||||
gps.minute = minute();
|
||||
gps.second = second();
|
||||
|
||||
int flags = flags();
|
||||
|
||||
gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
|
||||
gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
|
||||
gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
|
||||
AltosLib.AO_GPS_NUM_SAT_SHIFT;
|
||||
|
||||
gps.year = 2000 + year();
|
||||
gps.month = month();
|
||||
gps.day = day();
|
||||
gps.ground_speed = ground_speed() * 1.0e-2;
|
||||
gps.course = course() * 2;
|
||||
gps.climb_rate = climb_rate() * 1.0e-2;
|
||||
if (eeprom.config_data().compare_version("1.4.9") >= 0) {
|
||||
gps.pdop = pdop() / 10.0;
|
||||
gps.hdop = hdop() / 10.0;
|
||||
gps.vdop = vdop() / 10.0;
|
||||
} else {
|
||||
gps.pdop = pdop() / 100.0;
|
||||
if (gps.pdop < 0.8)
|
||||
gps.pdop += 2.56;
|
||||
gps.hdop = hdop() / 100.0;
|
||||
if (gps.hdop < 0.8)
|
||||
gps.hdop += 2.56;
|
||||
gps.vdop = vdop() / 100.0;
|
||||
if (gps.vdop < 0.8)
|
||||
gps.vdop += 2.56;
|
||||
}
|
||||
listener.set_gps(gps, true, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordGps(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordGps(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordGps(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
461
altoslib/AltosEepromRecordMega.java
Normal file
461
altoslib/AltosEepromRecordMega.java
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordMega extends AltosEepromRecord {
|
||||
public static final int record_length = 32;
|
||||
|
||||
public static final int max_sat = 12;
|
||||
|
||||
private int log_format;
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
private int flight() { return data16(0); }
|
||||
private int ground_accel() { return data16(2); }
|
||||
private int ground_pres() { return data32(4); }
|
||||
private int ground_accel_along() { return data16(8); }
|
||||
private int ground_accel_across() { return data16(10); }
|
||||
private int ground_accel_through() { return data16(12); }
|
||||
private int ground_roll() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return data32(16);
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return data16(14);
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
private int ground_pitch() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return data32(20);
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return data16(16);
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
private int ground_yaw() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return data32(24);
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return data16(18);
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
private int state() { return data16(0); }
|
||||
private int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
private int pres() { return data32(0); }
|
||||
private int temp() { return data32(4); }
|
||||
private int accel_x() { return data16(8); }
|
||||
private int accel_y() { return data16(10); }
|
||||
private int accel_z() { return data16(12); }
|
||||
private int gyro_x() { return data16(14); }
|
||||
private int gyro_y() { return data16(16); }
|
||||
private int gyro_z() { return data16(18); }
|
||||
private int mag_x() { return data16(20); }
|
||||
private int mag_z() { return data16(22); }
|
||||
private int mag_y() { return data16(24); }
|
||||
|
||||
/* normalized log data */
|
||||
private int norm_accel_along() { return data16(8); }
|
||||
private int norm_accel_across() { return data16(10); }
|
||||
private int norm_accel_through() { return data16(12); }
|
||||
private int norm_gyro_roll() { return data16(14); }
|
||||
private int norm_gyro_pitch() { return data16(16); }
|
||||
private int norm_gyro_yaw() { return data16(18); }
|
||||
private int norm_mag_along() { return data16(20); }
|
||||
private int norm_mag_across() { return data16(22); }
|
||||
private int norm_mag_through() { return data16(24); }
|
||||
|
||||
private int imu_type() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return AltosIMU.imu_type_telemega_v1_v2;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
return AltosIMU.imu_type_telemega_v3;
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
return AltosIMU.imu_type_easymega_v2;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return AltosIMU.imu_type_telemega_v4;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int imu_model() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
return AltosLib.model_mpu6000;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return AltosLib.model_bmi088;
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private boolean sensor_normalized() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int mag_model() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return AltosLib.model_mmc5983;
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private int accel_across() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_accel_across();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return accel_x();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return -accel_y();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int accel_along(){
|
||||
if (sensor_normalized()) {
|
||||
return norm_accel_along();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return accel_y();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return accel_x();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int accel_through() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_accel_through();
|
||||
}
|
||||
|
||||
return accel_z();
|
||||
}
|
||||
|
||||
private int gyro_pitch() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_gyro_pitch();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return gyro_x();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
return -gyro_y();
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return -gyro_y();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int gyro_roll() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_gyro_roll();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return gyro_y();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return gyro_x();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int gyro_yaw() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_gyro_yaw();
|
||||
}
|
||||
|
||||
return gyro_z();
|
||||
}
|
||||
|
||||
private int mag_across() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_mag_across();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return mag_x();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
return -mag_y();
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return mag_y();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int mag_along() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_mag_along();
|
||||
}
|
||||
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return mag_y();
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return mag_x();
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int mag_through() {
|
||||
if (sensor_normalized()) {
|
||||
return norm_mag_through();
|
||||
}
|
||||
|
||||
return mag_z();
|
||||
}
|
||||
|
||||
|
||||
private int accel() { return data16(26); }
|
||||
|
||||
/* AO_LOG_TEMP_VOLT elements */
|
||||
private int v_batt() { return data16(0); }
|
||||
private int v_pbatt() { return data16(2); }
|
||||
private int nsense() { return data16(4); }
|
||||
private int sense(int i) { return data16(6 + i * 2); }
|
||||
private int pyro() { return data16(26); }
|
||||
|
||||
/* AO_LOG_GPS_TIME elements */
|
||||
private int latitude() { return data32(0); }
|
||||
private int longitude() { return data32(4); }
|
||||
private int altitude_low() { return data16(8); }
|
||||
private int hour() { return data8(10); }
|
||||
private int minute() { return data8(11); }
|
||||
private int second() { return data8(12); }
|
||||
private int flags() { return data8(13); }
|
||||
private int year() { return data8(14); }
|
||||
private int month() { return data8(15); }
|
||||
private int day() { return data8(16); }
|
||||
private int course() { return data8(17); }
|
||||
private int ground_speed() { return data16(18); }
|
||||
private int climb_rate() { return data16(20); }
|
||||
private int pdop() { return data8(22); }
|
||||
private int hdop() { return data8(23); }
|
||||
private int vdop() { return data8(24); }
|
||||
private int mode() { return data8(25); }
|
||||
private int altitude_high() { return data16(26); }
|
||||
|
||||
/* AO_LOG_GPS_SAT elements */
|
||||
private int nsat() { return data16(0); }
|
||||
private int svid(int n) { return data8(2 + n * 2); }
|
||||
private int c_n(int n) { return data8(2 + n * 2 + 1); }
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
AltosGPS gps;
|
||||
|
||||
cal_data.set_imu_type(imu_type());
|
||||
cal_data.set_imu_model(imu_model());
|
||||
cal_data.set_mag_model(mag_model());
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
cal_data.set_ground_accel(ground_accel());
|
||||
cal_data.set_ground_pressure(ground_pres());
|
||||
listener.set_accel_ground(cal_data.accel_along(ground_accel_along()),
|
||||
cal_data.accel_across(ground_accel_across()),
|
||||
cal_data.accel_through(ground_accel_through()));
|
||||
cal_data.set_gyro_zero(ground_roll() / 512.0,
|
||||
ground_pitch() / 512.0,
|
||||
ground_yaw() / 512.0);
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
AltosConfigData config_data = eeprom.config_data();
|
||||
AltosPresTemp pt = config_data.ms5607().pres_temp(pres(), temp());;
|
||||
listener.set_pressure(pt.pres);
|
||||
listener.set_temperature(pt.temp);
|
||||
|
||||
int accel_along = accel_along();
|
||||
int accel_across = accel_across();
|
||||
int accel_through = accel_through();
|
||||
int gyro_roll = gyro_roll();
|
||||
int gyro_pitch = gyro_pitch();
|
||||
int gyro_yaw = gyro_yaw();
|
||||
|
||||
int mag_along = mag_along();
|
||||
int mag_across = mag_across();
|
||||
int mag_through = mag_through();
|
||||
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD)
|
||||
cal_data.check_imu_wrap(gyro_roll, gyro_pitch, gyro_yaw);
|
||||
|
||||
listener.set_accel(cal_data.accel_along(accel_along),
|
||||
cal_data.accel_across(accel_across),
|
||||
cal_data.accel_through(accel_through));
|
||||
listener.set_gyro(cal_data.gyro_roll(gyro_roll),
|
||||
cal_data.gyro_pitch(gyro_pitch),
|
||||
cal_data.gyro_yaw(gyro_yaw));
|
||||
|
||||
listener.set_mag(cal_data.mag_along(mag_along),
|
||||
cal_data.mag_across(mag_across),
|
||||
cal_data.mag_through(mag_through));
|
||||
|
||||
listener.set_acceleration(cal_data.acceleration(accel()));
|
||||
break;
|
||||
case AltosLib.AO_LOG_TEMP_VOLT:
|
||||
listener.set_battery_voltage(AltosConvert.mega_battery_voltage(v_batt()));
|
||||
listener.set_pyro_voltage(AltosConvert.mega_pyro_voltage(v_pbatt()));
|
||||
|
||||
int nsense = nsense();
|
||||
|
||||
listener.set_apogee_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-2)));
|
||||
listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-1)));
|
||||
|
||||
double voltages[] = new double[nsense-2];
|
||||
for (int i = 0; i < nsense-2; i++)
|
||||
voltages[i] = AltosConvert.mega_pyro_voltage(sense(i));
|
||||
|
||||
listener.set_igniter_voltage(voltages);
|
||||
listener.set_pyro_fired(pyro());
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
gps = listener.make_temp_gps(false);
|
||||
gps.lat = latitude() / 1e7;
|
||||
gps.lon = longitude() / 1e7;
|
||||
|
||||
if (config_data().altitude_32())
|
||||
gps.alt = (altitude_low() & 0xffff) | (altitude_high() << 16);
|
||||
else
|
||||
gps.alt = altitude_low();
|
||||
|
||||
gps.hour = hour();
|
||||
gps.minute = minute();
|
||||
gps.second = second();
|
||||
|
||||
int flags = flags();
|
||||
|
||||
gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
|
||||
gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
|
||||
gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
|
||||
AltosLib.AO_GPS_NUM_SAT_SHIFT;
|
||||
|
||||
gps.year = 2000 + year();
|
||||
gps.month = month();
|
||||
gps.day = day();
|
||||
gps.ground_speed = ground_speed() * 1.0e-2;
|
||||
gps.course = course() * 2;
|
||||
gps.climb_rate = climb_rate() * 1.0e-2;
|
||||
if (config_data().compare_version("1.4.9") >= 0) {
|
||||
gps.pdop = pdop() / 10.0;
|
||||
gps.hdop = hdop() / 10.0;
|
||||
gps.vdop = vdop() / 10.0;
|
||||
} else {
|
||||
gps.pdop = pdop() / 100.0;
|
||||
if (gps.pdop < 0.8)
|
||||
gps.pdop += 2.56;
|
||||
gps.hdop = hdop() / 100.0;
|
||||
if (gps.hdop < 0.8)
|
||||
gps.hdop += 2.56;
|
||||
gps.vdop = vdop() / 100.0;
|
||||
if (gps.vdop < 0.8)
|
||||
gps.vdop += 2.56;
|
||||
}
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_SAT:
|
||||
gps = listener.make_temp_gps(true);
|
||||
|
||||
int n = nsat();
|
||||
if (n > max_sat)
|
||||
n = max_sat;
|
||||
for (int i = 0; i < n; i++)
|
||||
gps.add_sat(svid(i), c_n(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordMega(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMega(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
log_format = eeprom.config_data().log_format;
|
||||
}
|
||||
|
||||
public AltosEepromRecordMega(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
145
altoslib/AltosEepromRecordMetrum.java
Normal file
145
altoslib/AltosEepromRecordMetrum.java
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordMetrum extends AltosEepromRecord {
|
||||
public static final int record_length = 16;
|
||||
|
||||
public int record_length() { return record_length; }
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
public int flight() { return data16(0); }
|
||||
public int ground_accel() { return data16(2); }
|
||||
public int ground_pres() { return data32(4); }
|
||||
public int ground_temp() { return data32(8); }
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
public int state() { return data16(0); }
|
||||
public int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
public int pres() { return data32(0); }
|
||||
public int temp() { return data32(4); }
|
||||
public int accel() { return data16(8); }
|
||||
|
||||
/* AO_LOG_TEMP_VOLT elements */
|
||||
public int v_batt() { return data16(0); }
|
||||
public int sense_a() { return data16(2); }
|
||||
public int sense_m() { return data16(4); }
|
||||
|
||||
/* AO_LOG_GPS_POS elements */
|
||||
public int latitude() { return data32(0); }
|
||||
public int longitude() { return data32(4); }
|
||||
public int altitude_low() { return data16(8); }
|
||||
public int altitude_high() { return data16(10); }
|
||||
|
||||
/* AO_LOG_GPS_TIME elements */
|
||||
public int hour() { return data8(0); }
|
||||
public int minute() { return data8(1); }
|
||||
public int second() { return data8(2); }
|
||||
public int flags() { return data8(3); }
|
||||
public int year() { return data8(4); }
|
||||
public int month() { return data8(5); }
|
||||
public int day() { return data8(6); }
|
||||
public int pdop() { return data8(7); }
|
||||
|
||||
/* AO_LOG_GPS_SAT elements */
|
||||
public int nsat() { return data8(0); }
|
||||
public int more() { return data8(1); }
|
||||
public int svid(int n) { return data8(2 + n * 2); }
|
||||
public int c_n(int n) { return data8(2 + n * 2 + 1); }
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
AltosGPS gps;
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
cal_data.set_ground_accel(ground_accel());
|
||||
cal_data.set_ground_pressure(ground_pres());
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
AltosPresTemp pt = eeprom.config_data().ms5607().pres_temp(pres(), temp());
|
||||
listener.set_pressure(pt.pres);
|
||||
listener.set_temperature(pt.temp);
|
||||
listener.set_acceleration(cal_data.acceleration(accel()));
|
||||
break;
|
||||
case AltosLib.AO_LOG_TEMP_VOLT:
|
||||
listener.set_battery_voltage(AltosConvert.mega_battery_voltage(v_batt()));
|
||||
listener.set_apogee_voltage(AltosConvert.mega_pyro_voltage(sense_a()));
|
||||
listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense_m()));
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_POS:
|
||||
gps = listener.make_temp_gps(false);
|
||||
gps.lat = latitude() / 1e7;
|
||||
gps.lon = longitude() / 1e7;
|
||||
if (config_data().altitude_32())
|
||||
gps.alt = (altitude_low() & 0xffff) | (altitude_high() << 16);
|
||||
else
|
||||
gps.alt = altitude_low();
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
gps = listener.make_temp_gps(false);
|
||||
|
||||
gps.hour = hour();
|
||||
gps.minute = minute();
|
||||
gps.second = second();
|
||||
|
||||
int flags = flags();
|
||||
|
||||
gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
|
||||
gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
|
||||
gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
|
||||
AltosLib.AO_GPS_NUM_SAT_SHIFT;
|
||||
|
||||
gps.year = 2000 + year();
|
||||
gps.month = month();
|
||||
gps.day = day();
|
||||
gps.pdop = pdop() / 10.0;
|
||||
break;
|
||||
case AltosLib.AO_LOG_GPS_SAT:
|
||||
gps = listener.make_temp_gps(true);
|
||||
|
||||
int n = nsat();
|
||||
for (int i = 0; i < n; i++)
|
||||
gps.add_sat(svid(i), c_n(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordMetrum(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMetrum(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMetrum(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
163
altoslib/AltosEepromRecordMicroPeak2.java
Normal file
163
altoslib/AltosEepromRecordMicroPeak2.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright © 2019 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordMicroPeak2 extends AltosEepromRecord {
|
||||
public static final int record_length = 2;
|
||||
|
||||
private static final int PA_GROUND_OFFSET = 0;
|
||||
private static final int PA_MIN_OFFSET = 4;
|
||||
private static final int N_SAMPLES_OFFSET = 8;
|
||||
private static final int STARTING_LOG_OFFSET = 10;
|
||||
|
||||
private static final int LOG_ID_MICROPEAK = 0;
|
||||
private static final int LOG_ID_MICROKITE = 1;
|
||||
private static final int LOG_ID_MICROPEAK2 = 2;
|
||||
|
||||
private int value16(int o) {
|
||||
return eeprom.data16(o);
|
||||
}
|
||||
|
||||
private int value32(int o) {
|
||||
return eeprom.data32(o);
|
||||
}
|
||||
|
||||
public int cmd() {
|
||||
if (start == 0)
|
||||
return AltosLib.AO_LOG_FLIGHT;
|
||||
return AltosLib.AO_LOG_SENSOR;
|
||||
}
|
||||
|
||||
private int pa_ground() {
|
||||
return value32(PA_GROUND_OFFSET);
|
||||
}
|
||||
|
||||
private int pa_min() {
|
||||
return value32(PA_MIN_OFFSET);
|
||||
}
|
||||
|
||||
private int log_id() {
|
||||
return value16(N_SAMPLES_OFFSET) >> 12;
|
||||
}
|
||||
|
||||
private int n_samples() {
|
||||
return value16(N_SAMPLES_OFFSET) & 0xfff;
|
||||
}
|
||||
|
||||
private int ticks_per_sample() {
|
||||
int log_id = log_id();
|
||||
|
||||
if (log_id == LOG_ID_MICROPEAK)
|
||||
return 2;
|
||||
if (log_id == LOG_ID_MICROKITE)
|
||||
return 200;
|
||||
if (log_id == LOG_ID_MICROPEAK2)
|
||||
return 10;
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int tick() {
|
||||
if (start <= STARTING_LOG_OFFSET)
|
||||
return 0;
|
||||
return ((start - STARTING_LOG_OFFSET) / 2) * ticks_per_sample();
|
||||
}
|
||||
|
||||
public double ticks_per_sec() {
|
||||
int log_id = log_id();
|
||||
|
||||
if (log_id == LOG_ID_MICROPEAK)
|
||||
return 1000.0/96.0;
|
||||
if (log_id == LOG_ID_MICROKITE)
|
||||
return 1000 / 96.0;
|
||||
if (log_id == LOG_ID_MICROPEAK2)
|
||||
return 100.0;
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
int mix_in (int high, int low) {
|
||||
return high - (high & 0xffff) + low;
|
||||
}
|
||||
|
||||
boolean closer (int target, int a, int b) {
|
||||
return Math.abs (target - a) < Math.abs(target - b);
|
||||
}
|
||||
|
||||
private int pressure() {
|
||||
int cur = value32(PA_GROUND_OFFSET);
|
||||
for (int s = STARTING_LOG_OFFSET; s <= start; s += 2) {
|
||||
int k = value16(s);
|
||||
int same = mix_in(cur, k);
|
||||
int up = mix_in(cur + 0x10000, k);
|
||||
int down = mix_in(cur - 0x10000, k);
|
||||
|
||||
if (closer (cur, same, up)) {
|
||||
if (closer (cur, same, down))
|
||||
cur = same;
|
||||
else
|
||||
cur = down;
|
||||
} else {
|
||||
if (closer (cur, up, down))
|
||||
cur = up;
|
||||
else
|
||||
cur = down;
|
||||
}
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
listener.set_tick(tick());
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
int pa_ground = pa_ground();
|
||||
int pa_min = pa_min();
|
||||
int n_samples = n_samples();
|
||||
int log_id = log_id();
|
||||
listener.set_state(AltosLib.ao_flight_pad);
|
||||
listener.cal_data().set_ground_pressure(pa_ground);
|
||||
listener.cal_data().set_ticks_per_sec(ticks_per_sec());
|
||||
listener.cal_data().set_boost_tick();
|
||||
listener.set_avoid_duplicate_files();
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
listener.set_state(AltosLib.ao_flight_boost);
|
||||
listener.set_pressure(pressure());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public int next_start() {
|
||||
if (start == 0)
|
||||
return STARTING_LOG_OFFSET;
|
||||
if (start + 2 >= STARTING_LOG_OFFSET + 2 * n_samples())
|
||||
return -1;
|
||||
return start + 2;
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordMicroPeak2(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMicroPeak2(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMicroPeak2(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
105
altoslib/AltosEepromRecordMini.java
Normal file
105
altoslib/AltosEepromRecordMini.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordMini extends AltosEepromRecord {
|
||||
public static final int record_length = 16;
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
public int flight() { return data16(0); }
|
||||
public int ground_pres() { return data32(4); }
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
public int state() { return data16(0); }
|
||||
public int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
public int pres() { return data24(0); }
|
||||
public int temp() { return data24(3); }
|
||||
public int sense_a() { return data16(6); }
|
||||
public int sense_m() { return data16(8); }
|
||||
public int v_batt() { return data16(10); }
|
||||
|
||||
private int log_format() {
|
||||
return eeprom.config_data().log_format;
|
||||
}
|
||||
|
||||
private double battery_voltage(int sensor) {
|
||||
int log_format = log_format();
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_EASYMINI1)
|
||||
return AltosConvert.easy_mini_1_voltage(sensor, eeprom.config_data().serial);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_EASYMINI2)
|
||||
return AltosConvert.easy_mini_2_voltage(sensor);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_TELEMINI2)
|
||||
return AltosConvert.tele_mini_2_voltage(sensor);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_TELEMINI3)
|
||||
return AltosConvert.tele_mini_3_battery_voltage(sensor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
private double pyro_voltage(int sensor) {
|
||||
int log_format = log_format();
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_EASYMINI1)
|
||||
return AltosConvert.easy_mini_1_voltage(sensor, eeprom.config_data().serial);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_EASYMINI2)
|
||||
return AltosConvert.easy_mini_2_voltage(sensor);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_TELEMINI2)
|
||||
return AltosConvert.tele_mini_2_voltage(sensor);
|
||||
if (log_format == AltosLib.AO_LOG_FORMAT_TELEMINI3)
|
||||
return AltosConvert.tele_mini_3_pyro_voltage(sensor);
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
cal_data.set_ground_pressure(ground_pres());
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
AltosPresTemp pt = eeprom.config_data().ms5607().pres_temp(pres(), temp());
|
||||
listener.set_pressure(pt.pres);
|
||||
listener.set_temperature(pt.temp);
|
||||
listener.set_apogee_voltage(pyro_voltage(sense_a()));
|
||||
listener.set_main_voltage(pyro_voltage(sense_m()));
|
||||
listener.set_battery_voltage(battery_voltage(v_batt()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordMini(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMini(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMini(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
103
altoslib/AltosEepromRecordMotor.java
Normal file
103
altoslib/AltosEepromRecordMotor.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright © 2020 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordMotor extends AltosEepromRecord {
|
||||
public static final int record_length = 16;
|
||||
|
||||
private int log_format;
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
private int flight() { return data16(0); }
|
||||
private int ground_accel() { return data16(2); }
|
||||
private int ground_accel_along() { return data16(4); }
|
||||
private int ground_accel_across() { return data16(6); }
|
||||
private int ground_accel_through() { return data16(8); }
|
||||
private int ground_motor_pressure() { return data16(10); }
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
private int state() { return data16(0); }
|
||||
private int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
private int motor_pres() { return data16(0); }
|
||||
private int v_batt() { return data16(2); }
|
||||
private int accel() { return data16(4); }
|
||||
private int accel_across() { return data16(6); }
|
||||
private int accel_along() { return -data16(8); }
|
||||
private int accel_through() { return data16(10); }
|
||||
|
||||
private int imu_type() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
|
||||
return AltosIMU.imu_type_easymotor_v2;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
cal_data.set_imu_type(imu_type());
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
cal_data.set_ground_accel(ground_accel());
|
||||
listener.set_accel_ground(cal_data.accel_along(ground_accel_along()),
|
||||
cal_data.accel_across(ground_accel_across()),
|
||||
cal_data.accel_through(ground_accel_through()));
|
||||
cal_data.set_ground_motor_pressure(ground_motor_pressure());
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
AltosConfigData config_data = eeprom.config_data();
|
||||
|
||||
listener.set_battery_voltage(AltosConvert.easy_motor_3_voltage(v_batt()));
|
||||
double pa = AltosConvert.easy_motor_3_motor_pressure(motor_pres(), cal_data.ground_motor_pressure);
|
||||
listener.set_motor_pressure(pa);
|
||||
|
||||
int accel_along = accel_along();
|
||||
int accel_across = accel_across();
|
||||
int accel_through = accel_through();
|
||||
|
||||
listener.set_accel(cal_data.accel_along(accel_along),
|
||||
cal_data.accel_across(accel_across),
|
||||
cal_data.accel_through(accel_through));
|
||||
|
||||
listener.set_acceleration(cal_data.acceleration(accel()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordMotor(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordMotor(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
log_format = eeprom.config_data().log_format;
|
||||
}
|
||||
|
||||
public AltosEepromRecordMotor(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
152
altoslib/AltosEepromRecordSet.java
Normal file
152
altoslib/AltosEepromRecordSet.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class AltosEepromRecordSet implements AltosRecordSet {
|
||||
AltosEeprom eeprom;
|
||||
TreeSet<AltosEepromRecord> ordered;
|
||||
AltosCalData cal_data;
|
||||
boolean valid;
|
||||
|
||||
public AltosConfigData config_data() {
|
||||
return eeprom.config_data();
|
||||
}
|
||||
|
||||
private void init_cal_data() {
|
||||
for (AltosEepromRecord record : ordered) {
|
||||
if (record.cmd() == AltosLib.AO_LOG_FLIGHT) {
|
||||
cal_data.set_tick(record.tick());
|
||||
cal_data.set_boost_tick();
|
||||
cal_data.set_state(AltosLib.ao_flight_pad);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AltosCalData cal_data() {
|
||||
if (cal_data == null) {
|
||||
cal_data = new AltosCalData(config_data());
|
||||
init_cal_data();
|
||||
}
|
||||
return cal_data;
|
||||
}
|
||||
|
||||
public void capture_series(AltosDataListener listener) {
|
||||
if (cal_data == null) {
|
||||
cal_data();
|
||||
} else {
|
||||
cal_data.reset();
|
||||
init_cal_data();
|
||||
}
|
||||
listener.set_log_format(config_data().log_format);
|
||||
|
||||
for (AltosEepromRecord record : ordered) {
|
||||
record.provide_data(listener, cal_data);
|
||||
}
|
||||
listener.finish();
|
||||
}
|
||||
|
||||
public boolean valid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public AltosEepromRecordSet(AltosEeprom eeprom) {
|
||||
this.eeprom = eeprom;
|
||||
|
||||
AltosConfigData config_data = eeprom.config_data();
|
||||
|
||||
AltosEepromRecord record = null;
|
||||
|
||||
switch (config_data.log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_FULL:
|
||||
record = new AltosEepromRecordFull(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TINY:
|
||||
record = new AltosEepromRecordTiny(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMETRY:
|
||||
case AltosLib.AO_LOG_FORMAT_TELESCIENCE:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_3:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_4:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_5:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMEGA_6:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMEGA_3:
|
||||
record = new AltosEepromRecordMega(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMETRUM:
|
||||
record = new AltosEepromRecordMetrum(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMINI2:
|
||||
case AltosLib.AO_LOG_FORMAT_TELEMINI3:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMINI1:
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMINI2:
|
||||
record = new AltosEepromRecordMini(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEGPS:
|
||||
record = new AltosEepromRecordGps(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_TELEFIRETWO:
|
||||
record = new AltosEepromRecordFireTwo(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_MICROPEAK2:
|
||||
record = new AltosEepromRecordMicroPeak2(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_EASYMOTOR:
|
||||
record = new AltosEepromRecordMotor(eeprom);
|
||||
break;
|
||||
case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
|
||||
record = new AltosEepromRecordTimer(eeprom);
|
||||
break;
|
||||
}
|
||||
|
||||
ordered = new TreeSet<AltosEepromRecord>();
|
||||
|
||||
if (record == null) {
|
||||
System.out.printf("failed to parse log format %d\n", config_data.log_format);
|
||||
valid = false;
|
||||
return;
|
||||
}
|
||||
valid = true;
|
||||
|
||||
int tick = 0;
|
||||
boolean first = true;
|
||||
|
||||
do {
|
||||
int t = record.tick();
|
||||
|
||||
if (first) {
|
||||
tick = t;
|
||||
first = false;
|
||||
} else {
|
||||
while (t < tick - 32767)
|
||||
t += 65536;
|
||||
tick = t;
|
||||
}
|
||||
record.wide_tick = tick;
|
||||
ordered.add(record);
|
||||
record = record.next();
|
||||
} while (record != null);
|
||||
}
|
||||
|
||||
public AltosEepromRecordSet(InputStream input) throws IOException {
|
||||
this(new AltosEeprom(input));
|
||||
}
|
||||
}
|
||||
171
altoslib/AltosEepromRecordTimer.java
Normal file
171
altoslib/AltosEepromRecordTimer.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright © 2024 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordTimer extends AltosEepromRecord {
|
||||
public static final int record_length = 32;
|
||||
|
||||
private int log_format;
|
||||
|
||||
private int imu_type() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
|
||||
return AltosIMU.imu_type_easytimer_v2;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int imu_model() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
|
||||
return AltosLib.model_bmi088;
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private boolean sensor_normalized() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int mag_model() {
|
||||
switch (log_format) {
|
||||
case AltosLib.AO_LOG_FORMAT_EASYTIMER_2:
|
||||
return AltosLib.model_mmc5983;
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
/* AO_LOG_FLIGHT elements */
|
||||
private int flight() { return data16(0); }
|
||||
private int ground_accel() { return data16(2); }
|
||||
private int ground_pres() { return AltosLib.MISSING; }
|
||||
private int ground_accel_along() { return data16(4); }
|
||||
private int ground_accel_across() { return data16(6); }
|
||||
private int ground_accel_through() { return data16(8); }
|
||||
private int ground_roll() { return data32(12); }
|
||||
private int ground_pitch() { return data32(16); }
|
||||
private int ground_yaw() { return data32(20); }
|
||||
|
||||
/* AO_LOG_STATE elements */
|
||||
private int state() { return data16(0); }
|
||||
private int reason() { return data16(2); }
|
||||
|
||||
/* AO_LOG_SENSOR elements */
|
||||
|
||||
private int accel_along() { return data16(0); }
|
||||
private int accel_across() { return data16(2); }
|
||||
private int accel_through() { return data16(4); }
|
||||
private int gyro_roll() { return data16(6); }
|
||||
private int gyro_pitch() { return data16(8); }
|
||||
private int gyro_yaw() { return data16(10); }
|
||||
private int mag_along() { return data16(12); }
|
||||
private int mag_across() { return data16(14); }
|
||||
private int mag_through() { return data16(16); }
|
||||
|
||||
private int accel() { return -accel_along(); }
|
||||
|
||||
private int v_batt() { return data16(18); }
|
||||
private int v_pbatt() { return data16(20); }
|
||||
private int nsense() { return 2; }
|
||||
private int sense(int i) { return data16(22 + i * 2); }
|
||||
private int pyro() { return data16(26); }
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosCalData cal_data) {
|
||||
super.provide_data(listener, cal_data);
|
||||
|
||||
cal_data.set_imu_type(imu_type());
|
||||
cal_data.set_imu_model(imu_model());
|
||||
cal_data.set_mag_model(mag_model());
|
||||
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
cal_data.set_flight(flight());
|
||||
cal_data.set_ground_accel(ground_accel());
|
||||
listener.set_accel_ground(cal_data.accel_along(ground_accel_along()),
|
||||
cal_data.accel_across(ground_accel_across()),
|
||||
cal_data.accel_through(ground_accel_through()));
|
||||
cal_data.set_gyro_zero(ground_roll() / 512.0,
|
||||
ground_pitch() / 512.0,
|
||||
ground_yaw() / 512.0);
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(state());
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
AltosConfigData config_data = eeprom.config_data();
|
||||
|
||||
int accel_along = accel_along();
|
||||
int accel_across = accel_across();
|
||||
int accel_through = accel_through();
|
||||
int gyro_roll = gyro_roll();
|
||||
int gyro_pitch = gyro_pitch();
|
||||
int gyro_yaw = gyro_yaw();
|
||||
|
||||
int mag_along = mag_along();
|
||||
int mag_across = mag_across();
|
||||
int mag_through = mag_through();
|
||||
|
||||
listener.set_accel(cal_data.accel_along(accel_along),
|
||||
cal_data.accel_across(accel_across),
|
||||
cal_data.accel_through(accel_through));
|
||||
listener.set_gyro(cal_data.gyro_roll(gyro_roll),
|
||||
cal_data.gyro_pitch(gyro_pitch),
|
||||
cal_data.gyro_yaw(gyro_yaw));
|
||||
|
||||
listener.set_mag(cal_data.mag_along(mag_along),
|
||||
cal_data.mag_across(mag_across),
|
||||
cal_data.mag_through(mag_through));
|
||||
|
||||
listener.set_acceleration(cal_data.acceleration(accel()));
|
||||
|
||||
listener.set_battery_voltage(AltosConvert.mega_battery_voltage(v_batt()));
|
||||
listener.set_pyro_voltage(AltosConvert.mega_pyro_voltage(v_pbatt()));
|
||||
|
||||
int nsense = nsense();
|
||||
|
||||
listener.set_apogee_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-2)));
|
||||
listener.set_main_voltage(AltosConvert.mega_pyro_voltage(sense(nsense-1)));
|
||||
|
||||
double voltages[] = new double[nsense-2];
|
||||
for (int i = 0; i < nsense-2; i++)
|
||||
voltages[i] = AltosConvert.mega_pyro_voltage(sense(i));
|
||||
|
||||
listener.set_igniter_voltage(voltages);
|
||||
listener.set_pyro_fired(pyro());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordTimer(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordTimer(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
log_format = eeprom.config_data().log_format;
|
||||
}
|
||||
|
||||
public AltosEepromRecordTimer(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
86
altoslib/AltosEepromRecordTiny.java
Normal file
86
altoslib/AltosEepromRecordTiny.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosEepromRecordTiny extends AltosEepromRecord implements AltosDataProvider {
|
||||
public static final int record_length = 2;
|
||||
|
||||
private int value() {
|
||||
return eeprom.data16(start);
|
||||
}
|
||||
|
||||
public boolean valid(int s) {
|
||||
return eeprom.data16(s) != 0xffff;
|
||||
}
|
||||
|
||||
public int cmd() {
|
||||
if (start == 0)
|
||||
return AltosLib.AO_LOG_FLIGHT;
|
||||
if ((value() & 0x8000) != 0)
|
||||
return AltosLib.AO_LOG_STATE;
|
||||
return AltosLib.AO_LOG_SENSOR;
|
||||
}
|
||||
|
||||
public int tick() {
|
||||
int tick = 0;
|
||||
int step = 10;
|
||||
for (int i = 2; i < start; i += 2)
|
||||
{
|
||||
int v = eeprom.data16(i);
|
||||
|
||||
if ((v & 0x8000) != 0) {
|
||||
if ((v & 0x7fff) >= AltosLib.ao_flight_drogue)
|
||||
step = 100;
|
||||
} else {
|
||||
tick += step;
|
||||
}
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
public void provide_data(AltosDataListener listener) {
|
||||
int value = data16(-header_length);
|
||||
|
||||
listener.set_tick(tick());
|
||||
switch (cmd()) {
|
||||
case AltosLib.AO_LOG_FLIGHT:
|
||||
listener.set_state(AltosLib.ao_flight_pad);
|
||||
listener.cal_data().set_flight(value);
|
||||
listener.cal_data().set_boost_tick();
|
||||
break;
|
||||
case AltosLib.AO_LOG_STATE:
|
||||
listener.set_state(value & 0x7fff);
|
||||
break;
|
||||
case AltosLib.AO_LOG_SENSOR:
|
||||
listener.set_pressure(AltosConvert.barometer_to_pressure(value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosEepromRecord next() {
|
||||
int s = next_start();
|
||||
if (s < 0)
|
||||
return null;
|
||||
return new AltosEepromRecordTiny(eeprom, s);
|
||||
}
|
||||
|
||||
public AltosEepromRecordTiny(AltosEeprom eeprom, int start) {
|
||||
super(eeprom, start, record_length);
|
||||
}
|
||||
|
||||
public AltosEepromRecordTiny(AltosEeprom eeprom) {
|
||||
this(eeprom, 0);
|
||||
}
|
||||
}
|
||||
85
altoslib/AltosFile.java
Normal file
85
altoslib/AltosFile.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class AltosFile extends File {
|
||||
|
||||
static String number(int n) {
|
||||
if (n == AltosLib.MISSING)
|
||||
return "unkn";
|
||||
else
|
||||
return String.format("%04d", n);
|
||||
}
|
||||
|
||||
static String receiver(int receiver) {
|
||||
if (receiver == AltosLib.MISSING)
|
||||
return "";
|
||||
return String.format("-via-%04d", receiver);
|
||||
}
|
||||
|
||||
static private String label(int flight) {
|
||||
if (flight < 0)
|
||||
return "corrupt";
|
||||
else
|
||||
return "flight";
|
||||
}
|
||||
|
||||
static private int flight(int flight) {
|
||||
if (flight < 0)
|
||||
return -flight;
|
||||
return flight;
|
||||
}
|
||||
|
||||
public AltosFile(int year, int month, int day, int serial, int flight, int receiver, String extension) {
|
||||
super (AltosPreferences.logdir(),
|
||||
String.format("%04d-%02d-%02d-serial-%s-%s-%s%s.%s",
|
||||
year, month, day, number(serial), label(flight), number(flight(flight)), receiver(receiver), extension));
|
||||
}
|
||||
|
||||
public AltosFile(int year, int month, int day, int serial, int flight, String extension) {
|
||||
this(year, month, day, serial, flight, AltosLib.MISSING, extension);
|
||||
}
|
||||
|
||||
public AltosFile(int serial, int flight, int receiver, String extension) {
|
||||
this(Calendar.getInstance().get(Calendar.YEAR),
|
||||
Calendar.getInstance().get(Calendar.MONTH) + 1,
|
||||
Calendar.getInstance().get(Calendar.DAY_OF_MONTH),
|
||||
serial,
|
||||
flight,
|
||||
receiver,
|
||||
extension);
|
||||
}
|
||||
|
||||
public AltosFile(int serial, int flight, String extension) {
|
||||
this(Calendar.getInstance().get(Calendar.YEAR),
|
||||
Calendar.getInstance().get(Calendar.MONTH) + 1,
|
||||
Calendar.getInstance().get(Calendar.DAY_OF_MONTH),
|
||||
serial,
|
||||
flight,
|
||||
AltosLib.MISSING,
|
||||
extension);
|
||||
}
|
||||
|
||||
public AltosFile(AltosCalData cal_data) {
|
||||
this(cal_data.serial, cal_data.flight, cal_data.receiver_serial, "telem");
|
||||
}
|
||||
}
|
||||
22
altoslib/AltosFilterListener.java
Normal file
22
altoslib/AltosFilterListener.java
Normal 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.altoslib_14;
|
||||
|
||||
public interface AltosFilterListener {
|
||||
void filter_changed(double speed_filter, double accel_filter);
|
||||
|
||||
double speed_filter();
|
||||
double accel_filter();
|
||||
}
|
||||
376
altoslib/AltosFlash.java
Normal file
376
altoslib/AltosFlash.java
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosFlash extends AltosProgrammer {
|
||||
File file;
|
||||
FileInputStream input;
|
||||
AltosHexfile image;
|
||||
AltosLink link;
|
||||
AltosDebug debug;
|
||||
AltosRomconfig rom_config;
|
||||
boolean aborted;
|
||||
AltosFlashListener listener;
|
||||
|
||||
static final byte MOV_direct_data = (byte) 0x75;
|
||||
static final byte MOV_DPTR_data16 = (byte) 0x90;
|
||||
static final byte MOV_A_data = (byte) 0x74;
|
||||
static final byte MOVX_atDPTR_A = (byte) 0xf0;
|
||||
static final byte MOVX_A_atDPTR = (byte) 0xe0;
|
||||
static final byte INC_DPTR = (byte) 0xa3;
|
||||
static final byte TRAP = (byte) 0xa5;
|
||||
|
||||
static final byte JB = (byte) 0x20;
|
||||
|
||||
static final byte MOV_A_direct = (byte) 0xe5;
|
||||
static final byte MOV_direct1_direct2 = (byte) 0x85;
|
||||
static final byte MOV_direct_A = (byte) 0xf5;
|
||||
static final byte MOV_R0_data = (byte) (0x78 | 0);
|
||||
static final byte MOV_R1_data = (byte) (0x78 | 1);
|
||||
static final byte MOV_R2_data = (byte) (0x78 | 2);
|
||||
static final byte MOV_R3_data = (byte) (0x78 | 3);
|
||||
static final byte MOV_R4_data = (byte) (0x78 | 4);
|
||||
static final byte MOV_R5_data = (byte) (0x78 | 5);
|
||||
static final byte MOV_R6_data = (byte) (0x78 | 6);
|
||||
static final byte MOV_R7_data = (byte) (0x78 | 7);
|
||||
static final byte DJNZ_R0_rel = (byte) (0xd8 | 0);
|
||||
static final byte DJNZ_R1_rel = (byte) (0xd8 | 1);
|
||||
static final byte DJNZ_R2_rel = (byte) (0xd8 | 2);
|
||||
static final byte DJNZ_R3_rel = (byte) (0xd8 | 3);
|
||||
static final byte DJNZ_R4_rel = (byte) (0xd8 | 4);
|
||||
static final byte DJNZ_R5_rel = (byte) (0xd8 | 5);
|
||||
static final byte DJNZ_R6_rel = (byte) (0xd8 | 6);
|
||||
static final byte DJNZ_R7_rel = (byte) (0xd8 | 7);
|
||||
|
||||
static final byte P1DIR = (byte) 0xFE;
|
||||
static final byte P1 = (byte) 0x90;
|
||||
|
||||
/* flash controller */
|
||||
static final byte FWT = (byte) 0xAB;
|
||||
static final byte FADDRL = (byte) 0xAC;
|
||||
static final byte FADDRH = (byte) 0xAD;
|
||||
static final byte FCTL = (byte) 0xAE;
|
||||
static final byte FCTL_BUSY = (byte) 0x80;
|
||||
static final byte FCTL_BUSY_BIT = (byte) 7;
|
||||
static final byte FCTL_SWBSY = (byte) 0x40;
|
||||
static final byte FCTL_SWBSY_BIT = (byte) 6;
|
||||
static final byte FCTL_CONTRD = (byte) 0x10;
|
||||
static final byte FCTL_WRITE = (byte) 0x02;
|
||||
static final byte FCTL_ERASE = (byte) 0x01;
|
||||
static final byte FWDATA = (byte) 0xAF;
|
||||
|
||||
static final byte ACC = (byte) 0xE0;
|
||||
|
||||
/* offsets within the flash_page program */
|
||||
static final int FLASH_ADDR_HIGH = 8;
|
||||
static final int FLASH_ADDR_LOW = 11;
|
||||
static final int RAM_ADDR_HIGH = 13;
|
||||
static final int RAM_ADDR_LOW = 14;
|
||||
static final int FLASH_WORDS_HIGH = 16;
|
||||
static final int FLASH_WORDS_LOW = 18;
|
||||
static final int FLASH_TIMING = 21;
|
||||
|
||||
/* sleep mode control */
|
||||
static final int SLEEP = (byte) 0xbe;
|
||||
static final int SLEEP_USB_EN = (byte) 0x80;
|
||||
static final int SLEEP_XOSC_STB = (byte) 0x40;
|
||||
static final int SLEEP_HFRC_STB = (byte) 0x20;
|
||||
static final int SLEEP_RST_MASK = (byte) 0x18;
|
||||
static final int SLEEP_RST_POWERON = (byte) 0x00;
|
||||
static final int SLEEP_RST_EXTERNAL = (byte) 0x10;
|
||||
static final int SLEEP_RST_WATCHDOG = (byte) 0x08;
|
||||
static final int SLEEP_OSC_PD = (byte) 0x04;
|
||||
static final int SLEEP_MODE_MASK = (byte) 0x03;
|
||||
static final int SLEEP_MODE_PM0 = (byte) 0x00;
|
||||
static final int SLEEP_MODE_PM1 = (byte) 0x01;
|
||||
static final int SLEEP_MODE_PM2 = (byte) 0x02;
|
||||
static final int SLEEP_MODE_PM3 = (byte) 0x03;
|
||||
|
||||
/* clock controller */
|
||||
static final byte CLKCON = (byte) 0xC6;
|
||||
static final byte CLKCON_OSC32K = (byte) 0x80;
|
||||
static final byte CLKCON_OSC = (byte) 0x40;
|
||||
static final byte CLKCON_TICKSPD = (byte) 0x38;
|
||||
static final byte CLKCON_CLKSPD = (byte) 0x07;
|
||||
|
||||
static final byte[] flash_page_proto = {
|
||||
|
||||
MOV_direct_data, P1DIR, (byte) 0x02,
|
||||
MOV_direct_data, P1, (byte) 0xFF,
|
||||
|
||||
MOV_direct_data, FADDRH, 0, /* FLASH_ADDR_HIGH */
|
||||
|
||||
MOV_direct_data, FADDRL, 0, /* FLASH_ADDR_LOW */
|
||||
|
||||
MOV_DPTR_data16, 0, 0, /* RAM_ADDR_HIGH, RAM_ADDR_LOW */
|
||||
|
||||
MOV_R7_data, 0, /* FLASH_WORDS_HIGH */
|
||||
|
||||
MOV_R6_data, 0, /* FLASH_WORDS_LOW */
|
||||
|
||||
|
||||
MOV_direct_data, FWT, 0x20, /* FLASH_TIMING */
|
||||
|
||||
MOV_direct_data, FCTL, FCTL_ERASE,
|
||||
/* eraseWaitLoop: */
|
||||
MOV_A_direct, FCTL,
|
||||
JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb,
|
||||
|
||||
MOV_direct_data, P1, (byte) 0xfd,
|
||||
|
||||
MOV_direct_data, FCTL, FCTL_WRITE,
|
||||
/* writeLoop: */
|
||||
MOV_R5_data, 2,
|
||||
/* writeWordLoop: */
|
||||
MOVX_A_atDPTR,
|
||||
INC_DPTR,
|
||||
MOV_direct_A, FWDATA,
|
||||
DJNZ_R5_rel, (byte) 0xfa, /* writeWordLoop */
|
||||
/* writeWaitLoop: */
|
||||
MOV_A_direct, FCTL,
|
||||
JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb, /* writeWaitLoop */
|
||||
DJNZ_R6_rel, (byte) 0xf1, /* writeLoop */
|
||||
DJNZ_R7_rel, (byte) 0xef, /* writeLoop */
|
||||
|
||||
MOV_direct_data, P1DIR, (byte) 0x00,
|
||||
MOV_direct_data, P1, (byte) 0xFF,
|
||||
TRAP,
|
||||
};
|
||||
|
||||
public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) {
|
||||
int flash_word_addr = flash_addr >> 1;
|
||||
int flash_word_count = ((byte_count + 1) >> 1);
|
||||
|
||||
byte[] flash_page = new byte[flash_page_proto.length];
|
||||
for (int i = 0; i < flash_page.length; i++)
|
||||
flash_page[i] = flash_page_proto[i];
|
||||
|
||||
flash_page[FLASH_ADDR_HIGH] = (byte) (flash_word_addr >> 8);
|
||||
flash_page[FLASH_ADDR_LOW] = (byte) (flash_word_addr);
|
||||
flash_page[RAM_ADDR_HIGH] = (byte) (ram_addr >> 8);
|
||||
flash_page[RAM_ADDR_LOW] = (byte) (ram_addr);
|
||||
|
||||
byte flash_words_low = (byte) (flash_word_count);
|
||||
byte flash_words_high = (byte) (flash_word_count >> 8);
|
||||
/* the flashing code has a minor 'bug' */
|
||||
if (flash_words_low != 0)
|
||||
flash_words_high++;
|
||||
|
||||
flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high;
|
||||
flash_page[FLASH_WORDS_LOW] = (byte) flash_words_low;
|
||||
return flash_page;
|
||||
}
|
||||
|
||||
static byte[] set_clkcon_fast = {
|
||||
MOV_direct_data, CLKCON, 0x00
|
||||
};
|
||||
|
||||
static byte[] get_sleep = {
|
||||
MOV_A_direct, SLEEP
|
||||
};
|
||||
|
||||
public void clock_init() throws IOException, InterruptedException {
|
||||
if (debug != null) {
|
||||
debug.debug_instr(set_clkcon_fast);
|
||||
|
||||
byte status;
|
||||
for (int times = 0; times < 20; times++) {
|
||||
Thread.sleep(1);
|
||||
status = debug.debug_instr(get_sleep);
|
||||
if ((status & SLEEP_XOSC_STB) != 0)
|
||||
return;
|
||||
}
|
||||
throw new IOException("Failed to initialize target clock");
|
||||
}
|
||||
}
|
||||
|
||||
void action(String s, int percent) {
|
||||
if (listener != null && !aborted)
|
||||
listener.position(s, percent);
|
||||
}
|
||||
|
||||
void action(int part, int total) {
|
||||
int percent = 100 * part / total;
|
||||
action(String.format("%d/%d (%d%%)",
|
||||
part, total, percent),
|
||||
percent);
|
||||
}
|
||||
|
||||
void altos_run(int pc) throws IOException, InterruptedException {
|
||||
debug.set_pc(pc);
|
||||
int set_pc = debug.get_pc();
|
||||
if (pc != set_pc)
|
||||
throw new IOException("Failed to set target program counter");
|
||||
debug.resume();
|
||||
|
||||
for (int times = 0; times < 20; times++) {
|
||||
byte status = debug.read_status();
|
||||
if ((status & AltosDebug.STATUS_CPU_HALTED) != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IOException("Failed to execute program on target");
|
||||
}
|
||||
|
||||
public void flash() {
|
||||
try {
|
||||
if (!check_rom_config())
|
||||
throw new IOException("Invalid rom config settings");
|
||||
if (image.address + image.data.length > 0x8000)
|
||||
throw new IOException(String.format("Flash image too long %d",
|
||||
image.address +
|
||||
image.data.length));
|
||||
if ((image.address & 0x3ff) != 0)
|
||||
throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)",
|
||||
image.address));
|
||||
int ram_address = 0xf000;
|
||||
int flash_prog = 0xf400;
|
||||
|
||||
/*
|
||||
* Store desired config values into image
|
||||
*/
|
||||
rom_config.write(image);
|
||||
/*
|
||||
* Bring up the clock
|
||||
*/
|
||||
clock_init();
|
||||
|
||||
int remain = image.data.length;
|
||||
int flash_addr = (int) image.address;
|
||||
int image_start = 0;
|
||||
|
||||
action(AltosFlashListener.flash_start, 0);
|
||||
action(0, image.data.length);
|
||||
while (remain > 0 && !aborted) {
|
||||
int this_time = remain;
|
||||
if (this_time > 0x400)
|
||||
this_time = 0x400;
|
||||
|
||||
if (debug != null) {
|
||||
/* write the data */
|
||||
debug.write_memory(ram_address, image.data,
|
||||
image_start, this_time);
|
||||
|
||||
/* write the flash program */
|
||||
byte[] flash_page = make_flash_page(flash_addr,
|
||||
ram_address,
|
||||
this_time);
|
||||
debug.write_memory(flash_prog, flash_page);
|
||||
|
||||
altos_run(flash_prog);
|
||||
byte[] check = debug.read_memory(flash_addr, this_time);
|
||||
for (int i = 0; i < this_time; i++)
|
||||
if (check[i] != image.data[image_start + i])
|
||||
throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)",
|
||||
image.address + image_start + i,
|
||||
check[i], image.data[image_start + i]));
|
||||
} else {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
|
||||
remain -= this_time;
|
||||
flash_addr += this_time;
|
||||
image_start += this_time;
|
||||
|
||||
action(image.data.length - remain, image.data.length);
|
||||
}
|
||||
if (!aborted) {
|
||||
action(AltosFlashListener.flash_done, 100);
|
||||
if (debug != null) {
|
||||
debug.set_pc((int) image.address);
|
||||
debug.resume();
|
||||
}
|
||||
}
|
||||
if (debug != null)
|
||||
debug.close();
|
||||
} catch (IOException ie) {
|
||||
action(ie.getMessage(), -1);
|
||||
abort();
|
||||
} catch (InterruptedException ie) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (debug != null)
|
||||
debug.close();
|
||||
}
|
||||
|
||||
synchronized public void abort() {
|
||||
aborted = true;
|
||||
close();
|
||||
}
|
||||
|
||||
public boolean check_rom_config() throws InterruptedException {
|
||||
if (debug == null)
|
||||
return true;
|
||||
if (rom_config == null)
|
||||
rom_config = debug.romconfig();
|
||||
return rom_config != null && rom_config.valid();
|
||||
}
|
||||
|
||||
public void set_romconfig (AltosRomconfig romconfig) {
|
||||
rom_config = romconfig;
|
||||
}
|
||||
|
||||
public AltosRomconfig target_romconfig(AltosUsbId usb_id, String usb_product) throws InterruptedException {
|
||||
if (!check_rom_config())
|
||||
return null;
|
||||
if (rom_config.usb_id == null)
|
||||
rom_config.usb_id = usb_id;
|
||||
if (rom_config.usb_product == null)
|
||||
rom_config.usb_product = usb_product;
|
||||
return rom_config;
|
||||
}
|
||||
|
||||
public AltosRomconfig image_romconfig() {
|
||||
return new AltosRomconfig(image);
|
||||
}
|
||||
|
||||
public AltosFlash(File file, AltosLink link, AltosFlashListener listener)
|
||||
throws IOException, FileNotFoundException, InterruptedException {
|
||||
this.file = file;
|
||||
this.link = link;
|
||||
this.listener = listener;
|
||||
if (link != null)
|
||||
debug = new AltosDebug(link);
|
||||
input = new FileInputStream(file);
|
||||
image = new AltosHexfile(input);
|
||||
|
||||
boolean connection_ok = true;
|
||||
|
||||
if (debug != null) {
|
||||
try {
|
||||
connection_ok = debug.check_connection();
|
||||
} catch (IOException ie) {
|
||||
debug.close();
|
||||
throw ie;
|
||||
} catch (InterruptedException ie) {
|
||||
debug.close();
|
||||
throw ie;
|
||||
}
|
||||
}
|
||||
if (!connection_ok) {
|
||||
debug.close();
|
||||
throw new IOException("Debug port not connected");
|
||||
}
|
||||
}
|
||||
}
|
||||
27
altoslib/AltosFlashListener.java
Normal file
27
altoslib/AltosFlashListener.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosFlashListener {
|
||||
|
||||
public final static String flash_start = "start";
|
||||
public final static String flash_done = "done";
|
||||
|
||||
public void position(String label, int percent);
|
||||
}
|
||||
27
altoslib/AltosFlightDisplay.java
Normal file
27
altoslib/AltosFlightDisplay.java
Normal 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.altoslib_14;
|
||||
|
||||
public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
|
||||
void reset();
|
||||
|
||||
void show(AltosState state, AltosListenerState listener_state);
|
||||
|
||||
String getName();
|
||||
}
|
||||
59
altoslib/AltosFlightReader.java
Normal file
59
altoslib/AltosFlightReader.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.text.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AltosFlightReader {
|
||||
public String name;
|
||||
|
||||
public int serial;
|
||||
|
||||
public void init() {}
|
||||
|
||||
public abstract AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException;
|
||||
|
||||
public abstract AltosCalData cal_data();
|
||||
|
||||
public abstract void close(boolean interrupted);
|
||||
|
||||
public void set_frequency(double frequency) throws InterruptedException, TimeoutException { }
|
||||
|
||||
public void save_frequency() { }
|
||||
|
||||
public void set_telemetry(int telemetry) { }
|
||||
|
||||
public void set_telemetry_rate(int telemetry_rate) throws InterruptedException, TimeoutException { }
|
||||
|
||||
public void save_telemetry() { }
|
||||
|
||||
public void save_telemetry_rate() { }
|
||||
|
||||
public boolean supports_telemetry(int telemetry) { return false; }
|
||||
|
||||
public boolean supports_telemetry_rate(int telemetry_rate) { return false; }
|
||||
|
||||
public File backing_file() { return null; }
|
||||
|
||||
public boolean has_monitor_battery() throws InterruptedException { return false; }
|
||||
|
||||
public double monitor_battery() throws InterruptedException { return AltosLib.MISSING; }
|
||||
}
|
||||
808
altoslib/AltosFlightSeries.java
Normal file
808
altoslib/AltosFlightSeries.java
Normal file
@@ -0,0 +1,808 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class AltosFlightSeries extends AltosDataListener {
|
||||
|
||||
public ArrayList<AltosTimeSeries> series = new ArrayList<AltosTimeSeries>();
|
||||
|
||||
public double speed_filter_width = 4.0;
|
||||
public double accel_filter_width = 1.0;
|
||||
|
||||
public int[] indices() {
|
||||
int[] indices = new int[series.size()];
|
||||
for (int i = 0; i < indices.length; i++)
|
||||
indices[i] = -1;
|
||||
step_indices(indices);
|
||||
return indices;
|
||||
}
|
||||
|
||||
private double time(int id, int index) {
|
||||
AltosTimeSeries s = series.get(id);
|
||||
|
||||
if (index < 0)
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
|
||||
if (index < s.values.size())
|
||||
return s.values.get(index).time;
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
public boolean step_indices(int[] indices) {
|
||||
double min_next = time(0, indices[0]+1);
|
||||
|
||||
for (int i = 1; i < indices.length; i++) {
|
||||
double next = time(i, indices[i]+1);
|
||||
if (next < min_next)
|
||||
min_next = next;
|
||||
}
|
||||
|
||||
if (min_next == Double.POSITIVE_INFINITY)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < indices.length; i++) {
|
||||
double t = time(i, indices[i] + 1);
|
||||
|
||||
if (t <= min_next)
|
||||
indices[i]++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public double time(int[] indices) {
|
||||
double max = time(0, indices[0]);
|
||||
|
||||
for (int i = 1; i < indices.length; i++) {
|
||||
double t = time(i, indices[i]);
|
||||
if (t >= max)
|
||||
max = t;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
public double value(String name, int[] indices) {
|
||||
for (int i = 0; i < indices.length; i++) {
|
||||
AltosTimeSeries s = series.get(i);
|
||||
if (s.label.equals(name)) {
|
||||
int index = indices[i];
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
if (index >= s.values.size())
|
||||
index = s.values.size() - 1;
|
||||
return s.values.get(index).value;
|
||||
}
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public double value(String name, double time) {
|
||||
for (AltosTimeSeries s : series) {
|
||||
if (s.label.equals(name))
|
||||
return s.value(time);
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public double value_before(String name, double time) {
|
||||
for (AltosTimeSeries s : series) {
|
||||
if (s.label.equals(name))
|
||||
return s.value_before(time);
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public double value_after(String name, double time) {
|
||||
for (AltosTimeSeries s : series) {
|
||||
if (s.label.equals(name))
|
||||
return s.value_after(time);
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public AltosTimeSeries make_series(String label, AltosUnits units) {
|
||||
return new AltosTimeSeries(label, units);
|
||||
}
|
||||
|
||||
public void add_series(AltosTimeSeries s) {
|
||||
for (int e = 0; e < series.size(); e++) {
|
||||
if (s.compareTo(series.get(e)) < 0){
|
||||
series.add(e, s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
series.add(s);
|
||||
}
|
||||
|
||||
public AltosTimeSeries add_series(String label, AltosUnits units) {
|
||||
AltosTimeSeries s = make_series(label, units);
|
||||
add_series(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public void remove_series(AltosTimeSeries s) {
|
||||
series.remove(s);
|
||||
}
|
||||
|
||||
public boolean has_series(String label) {
|
||||
for (AltosTimeSeries s : series)
|
||||
if (s.label.equals(label))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public AltosTimeSeries state_series;
|
||||
|
||||
public static final String state_name = "State";
|
||||
|
||||
public void set_state(int state) {
|
||||
|
||||
if (state != AltosLib.ao_flight_pad && state != AltosLib.MISSING && state != AltosLib.ao_flight_stateless) {
|
||||
if (state_series == null)
|
||||
state_series = add_series(state_name, AltosConvert.state_name);
|
||||
if (this.state() != state)
|
||||
state_series.add(time(), state);
|
||||
}
|
||||
super.set_state(state);
|
||||
}
|
||||
|
||||
public AltosTimeSeries accel_series;
|
||||
public boolean accel_computed;
|
||||
|
||||
public static final String accel_name = "Accel";
|
||||
|
||||
public AltosTimeSeries vert_accel_series;
|
||||
|
||||
public static final String vert_accel_name = "Vertical Accel";
|
||||
|
||||
public void set_acceleration(double acceleration) {
|
||||
if (acceleration == AltosLib.MISSING)
|
||||
return;
|
||||
if (accel_series == null)
|
||||
accel_series = add_series(accel_name, AltosConvert.accel);
|
||||
|
||||
accel_series.add(time(), acceleration);
|
||||
accel_computed = false;
|
||||
}
|
||||
|
||||
private AltosTimeSeries compute_accel() {
|
||||
AltosTimeSeries new_accel_series = null;
|
||||
|
||||
if (speed_series != null) {
|
||||
AltosTimeSeries temp_series;
|
||||
if (accel_filter_width > 0) {
|
||||
temp_series = make_series(speed_name, AltosConvert.speed);
|
||||
speed_series.filter(temp_series, accel_filter_width);
|
||||
} else
|
||||
temp_series = speed_series;
|
||||
|
||||
new_accel_series = make_series(accel_name, AltosConvert.accel);
|
||||
temp_series.differentiate(new_accel_series);
|
||||
}
|
||||
return new_accel_series;
|
||||
}
|
||||
|
||||
public void set_filter(double speed_filter, double accel_filter) {
|
||||
this.speed_filter_width = speed_filter;
|
||||
this.accel_filter_width = accel_filter;
|
||||
|
||||
AltosTimeSeries new_speed_series = compute_speed();
|
||||
|
||||
if (new_speed_series != null) {
|
||||
speed_series.erase_values();
|
||||
for (AltosTimeValue tv : new_speed_series)
|
||||
speed_series.add(tv);
|
||||
}
|
||||
if (accel_computed) {
|
||||
AltosTimeSeries new_accel_series = compute_accel();
|
||||
if (new_accel_series != null) {
|
||||
accel_series.erase_values();
|
||||
for (AltosTimeValue tv : new_accel_series)
|
||||
accel_series.add(tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_received_time(long received_time) {
|
||||
}
|
||||
|
||||
public AltosTimeSeries tick_series;
|
||||
|
||||
public static final String tick_name = "Tick";
|
||||
|
||||
public void set_tick(int tick) {
|
||||
super.set_tick(tick);
|
||||
if (tick_series == null)
|
||||
tick_series = add_series(tick_name, null);
|
||||
tick_series.add(time(), tick);
|
||||
}
|
||||
|
||||
public AltosTimeSeries rssi_series;
|
||||
|
||||
public static final String rssi_name = "RSSI";
|
||||
|
||||
public AltosTimeSeries status_series;
|
||||
|
||||
public static final String status_name = "Radio Status";
|
||||
|
||||
public void set_rssi(int rssi, int status) {
|
||||
if (rssi_series == null) {
|
||||
rssi_series = add_series(rssi_name, null);
|
||||
status_series = add_series(status_name, null);
|
||||
}
|
||||
rssi_series.add(time(), rssi);
|
||||
status_series.add(time(), status);
|
||||
}
|
||||
|
||||
public AltosTimeSeries pressure_series;
|
||||
|
||||
public static final String pressure_name = "Pressure";
|
||||
|
||||
public AltosTimeSeries altitude_series;
|
||||
|
||||
public static final String altitude_name = "Altitude";
|
||||
|
||||
public AltosTimeSeries height_series;
|
||||
|
||||
public double max_height = AltosLib.MISSING;
|
||||
|
||||
public void set_min_pressure(double pa) {
|
||||
double ground_altitude = cal_data().ground_altitude;
|
||||
if (ground_altitude != AltosLib.MISSING)
|
||||
max_height = AltosConvert.pressure_to_altitude(pa) -
|
||||
ground_altitude;
|
||||
}
|
||||
|
||||
public static final String height_name = "Height";
|
||||
|
||||
public void set_pressure(double pa) {
|
||||
if (pa == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
if (pressure_series == null)
|
||||
pressure_series = add_series(pressure_name, AltosConvert.pressure);
|
||||
pressure_series.add(time(), pa);
|
||||
if (altitude_series == null)
|
||||
altitude_series = add_series(altitude_name, AltosConvert.height);
|
||||
|
||||
if (cal_data().ground_pressure == AltosLib.MISSING)
|
||||
cal_data().set_ground_pressure(pa);
|
||||
|
||||
double altitude = AltosConvert.pressure_to_altitude(pa);
|
||||
altitude_series.add(time(), altitude);
|
||||
}
|
||||
|
||||
private void compute_height() {
|
||||
if (height_series == null) {
|
||||
double ground_altitude = cal_data().ground_altitude;
|
||||
if (ground_altitude != AltosLib.MISSING && altitude_series != null) {
|
||||
height_series = add_series(height_name, AltosConvert.height);
|
||||
for (AltosTimeValue alt : altitude_series)
|
||||
height_series.add(alt.time, alt.value - ground_altitude);
|
||||
} else if (speed_series != null) {
|
||||
height_series = add_series(height_name, AltosConvert.height);
|
||||
speed_series.integrate(height_series);
|
||||
}
|
||||
}
|
||||
|
||||
if (gps_height == null && cal_data().gps_pad != null && cal_data().gps_pad.alt != AltosLib.MISSING && gps_altitude != null) {
|
||||
double gps_ground_altitude = cal_data().gps_pad.alt;
|
||||
gps_height = add_series(gps_height_name, AltosConvert.height);
|
||||
for (AltosTimeValue gps_alt : gps_altitude)
|
||||
gps_height.add(gps_alt.time, gps_alt.value - gps_ground_altitude);
|
||||
}
|
||||
}
|
||||
|
||||
public AltosTimeSeries speed_series;
|
||||
|
||||
public static final String speed_name = "Speed";
|
||||
|
||||
private AltosTimeSeries compute_speed() {
|
||||
AltosTimeSeries new_speed_series = null;
|
||||
AltosTimeSeries alt_speed_series = null;
|
||||
AltosTimeSeries accel_speed_series = null;
|
||||
|
||||
if (altitude_series != null) {
|
||||
AltosTimeSeries temp_series;
|
||||
|
||||
if (speed_filter_width > 0) {
|
||||
temp_series = make_series(speed_name, AltosConvert.height);
|
||||
altitude_series.filter(temp_series, speed_filter_width);
|
||||
} else
|
||||
temp_series = altitude_series;
|
||||
|
||||
alt_speed_series = make_series(speed_name, AltosConvert.speed);
|
||||
temp_series.differentiate(alt_speed_series);
|
||||
}
|
||||
if (accel_series != null && !accel_computed) {
|
||||
|
||||
if (orient_series != null) {
|
||||
vert_accel_series = add_series(vert_accel_name, AltosConvert.accel);
|
||||
|
||||
for (AltosTimeValue a : accel_series) {
|
||||
double orient = orient_series.value(a.time);
|
||||
double a_abs = a.value + AltosConvert.gravity;
|
||||
double v_a = a_abs * Math.cos(AltosConvert.degrees_to_radians(orient)) - AltosConvert.gravity;
|
||||
|
||||
vert_accel_series.add(a.time, v_a);
|
||||
}
|
||||
}
|
||||
|
||||
AltosTimeSeries temp_series = make_series(speed_name, AltosConvert.speed);
|
||||
|
||||
if (vert_accel_series != null)
|
||||
vert_accel_series.integrate(temp_series);
|
||||
else
|
||||
accel_series.integrate(temp_series);
|
||||
|
||||
AltosTimeSeries clip_series = make_series(speed_name, AltosConvert.speed);
|
||||
|
||||
temp_series.clip(clip_series, 0, Double.POSITIVE_INFINITY);
|
||||
|
||||
accel_speed_series = make_series(speed_name, AltosConvert.speed);
|
||||
clip_series.filter(accel_speed_series, 0.1);
|
||||
}
|
||||
|
||||
if (alt_speed_series != null && accel_speed_series != null) {
|
||||
double apogee_time = AltosLib.MISSING;
|
||||
if (state_series != null) {
|
||||
for (AltosTimeValue d : state_series) {
|
||||
if (d.value >= AltosLib.ao_flight_drogue){
|
||||
apogee_time = d.time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (apogee_time == AltosLib.MISSING) {
|
||||
new_speed_series = alt_speed_series;
|
||||
} else {
|
||||
new_speed_series = make_series(speed_name, AltosConvert.speed);
|
||||
for (AltosTimeValue d : accel_speed_series) {
|
||||
if (d.time <= apogee_time)
|
||||
new_speed_series.add(d);
|
||||
}
|
||||
for (AltosTimeValue d : alt_speed_series) {
|
||||
if (d.time > apogee_time)
|
||||
new_speed_series.add(d);
|
||||
}
|
||||
|
||||
}
|
||||
} else if (alt_speed_series != null) {
|
||||
new_speed_series = alt_speed_series;
|
||||
} else if (accel_speed_series != null) {
|
||||
new_speed_series = accel_speed_series;
|
||||
}
|
||||
return new_speed_series;
|
||||
}
|
||||
|
||||
public AltosTimeSeries orient_series;
|
||||
public AltosTimeSeries azimuth_series;
|
||||
|
||||
public static final String orient_name = "Tilt Angle";
|
||||
public static final String azimuth_name = "Azimuth Angle";
|
||||
|
||||
private void compute_orient() {
|
||||
|
||||
if (orient_series != null)
|
||||
return;
|
||||
|
||||
if (accel_ground_across == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
AltosCalData cal_data = cal_data();
|
||||
|
||||
if (cal_data.pad_orientation == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
if (cal_data.accel_zero_across == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
if (cal_data.gyro_zero_roll == AltosLib.MISSING)
|
||||
return;
|
||||
|
||||
AltosRotation rotation = new AltosRotation(accel_ground_across,
|
||||
accel_ground_through,
|
||||
accel_ground_along,
|
||||
cal_data.pad_orientation);
|
||||
double prev_time = ground_time;
|
||||
|
||||
orient_series = add_series(orient_name, AltosConvert.orient);
|
||||
orient_series.add(ground_time, rotation.tilt());
|
||||
|
||||
azimuth_series = add_series(azimuth_name, AltosConvert.orient);
|
||||
azimuth_series.add(ground_time, rotation.azimuth());
|
||||
|
||||
for (AltosTimeValue roll_v : gyro_roll) {
|
||||
double time = roll_v.time;
|
||||
double dt = time - prev_time;
|
||||
|
||||
if (dt > 0) {
|
||||
double roll = AltosConvert.degrees_to_radians(roll_v.value) * dt;
|
||||
double pitch = AltosConvert.degrees_to_radians(gyro_pitch.value(time)) * dt;
|
||||
double yaw = AltosConvert.degrees_to_radians(gyro_yaw.value(time)) * dt;
|
||||
|
||||
rotation.rotate(pitch, yaw, roll);
|
||||
orient_series.add(time, rotation.tilt());
|
||||
azimuth_series.add(time, rotation.azimuth());
|
||||
}
|
||||
prev_time = time;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosTimeSeries kalman_height_series, kalman_speed_series, kalman_accel_series;
|
||||
|
||||
public static final String kalman_height_name = "Kalman Height";
|
||||
public static final String kalman_speed_name = "Kalman Speed";
|
||||
public static final String kalman_accel_name = "Kalman Accel";
|
||||
|
||||
public void set_kalman(double height, double speed, double acceleration) {
|
||||
if (kalman_height_series == null) {
|
||||
kalman_height_series = add_series(kalman_height_name, AltosConvert.height);
|
||||
kalman_speed_series = add_series(kalman_speed_name, AltosConvert.speed);
|
||||
kalman_accel_series = add_series(kalman_accel_name, AltosConvert.accel);
|
||||
}
|
||||
kalman_height_series.add(time(), height);
|
||||
kalman_speed_series.add(time(), speed);
|
||||
kalman_accel_series.add(time(), acceleration);
|
||||
}
|
||||
|
||||
public AltosTimeSeries thrust_series;
|
||||
|
||||
public static final String thrust_name = "Thrust";
|
||||
|
||||
public void set_thrust(double N) {
|
||||
if (thrust_series == null)
|
||||
thrust_series = add_series(thrust_name, AltosConvert.force);
|
||||
thrust_series.add(time(), N);
|
||||
}
|
||||
|
||||
public AltosTimeSeries temperature_series;
|
||||
|
||||
public static final String temperature_name = "Temperature";
|
||||
|
||||
public void set_temperature(double deg_c) {
|
||||
if (temperature_series == null)
|
||||
temperature_series = add_series(temperature_name, AltosConvert.temperature);
|
||||
temperature_series.add(time(), deg_c);
|
||||
}
|
||||
|
||||
public AltosTimeSeries battery_voltage_series;
|
||||
|
||||
public static final String battery_voltage_name = "Battery Voltage";
|
||||
|
||||
public void set_battery_voltage(double volts) {
|
||||
if (volts == AltosLib.MISSING)
|
||||
return;
|
||||
if (battery_voltage_series == null)
|
||||
battery_voltage_series = add_series(battery_voltage_name, AltosConvert.voltage);
|
||||
battery_voltage_series.add(time(), volts);
|
||||
}
|
||||
|
||||
public AltosTimeSeries apogee_voltage_series;
|
||||
|
||||
public static final String apogee_voltage_name = "Apogee Voltage";
|
||||
|
||||
public void set_apogee_voltage(double volts) {
|
||||
if (volts == AltosLib.MISSING)
|
||||
return;
|
||||
if (apogee_voltage_series == null)
|
||||
apogee_voltage_series = add_series(apogee_voltage_name, AltosConvert.voltage);
|
||||
apogee_voltage_series.add(time(), volts);
|
||||
}
|
||||
|
||||
public AltosTimeSeries main_voltage_series;
|
||||
|
||||
public static final String main_voltage_name = "Main Voltage";
|
||||
|
||||
public void set_main_voltage(double volts) {
|
||||
if (volts == AltosLib.MISSING)
|
||||
return;
|
||||
if (main_voltage_series == null)
|
||||
main_voltage_series = add_series(main_voltage_name, AltosConvert.voltage);
|
||||
main_voltage_series.add(time(), volts);
|
||||
}
|
||||
|
||||
public ArrayList<AltosGPSTimeValue> gps_series;
|
||||
|
||||
public AltosGPS gps_before(double time) {
|
||||
AltosGPSTimeValue nearest = null;
|
||||
for (AltosGPSTimeValue gtv : gps_series) {
|
||||
if (nearest == null)
|
||||
nearest = gtv;
|
||||
else {
|
||||
if (gtv.time <= time) {
|
||||
if (nearest.time <= time && gtv.time > nearest.time)
|
||||
nearest = gtv;
|
||||
} else {
|
||||
if (nearest.time > time && gtv.time < nearest.time)
|
||||
nearest = gtv;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nearest != null)
|
||||
return nearest.gps;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public AltosTimeSeries sats_in_view;
|
||||
public AltosTimeSeries sats_in_soln;
|
||||
public AltosTimeSeries gps_altitude;
|
||||
public AltosTimeSeries gps_height;
|
||||
public AltosTimeSeries gps_ground_speed;
|
||||
public AltosTimeSeries gps_ascent_rate;
|
||||
public AltosTimeSeries gps_course;
|
||||
public AltosTimeSeries gps_speed;
|
||||
public AltosTimeSeries gps_pdop, gps_vdop, gps_hdop;
|
||||
|
||||
public static final String sats_in_view_name = "Satellites in view";
|
||||
public static final String sats_in_soln_name = "Satellites in solution";
|
||||
public static final String gps_altitude_name = "GPS Altitude";
|
||||
public static final String gps_height_name = "GPS Height";
|
||||
public static final String gps_ground_speed_name = "GPS Ground Speed";
|
||||
public static final String gps_ascent_rate_name = "GPS Ascent Rate";
|
||||
public static final String gps_course_name = "GPS Course";
|
||||
public static final String gps_speed_name = "GPS Speed";
|
||||
public static final String gps_pdop_name = "GPS Dilution of Precision";
|
||||
public static final String gps_vdop_name = "GPS Vertical Dilution of Precision";
|
||||
public static final String gps_hdop_name = "GPS Horizontal Dilution of Precision";
|
||||
|
||||
public void set_gps(AltosGPS gps, boolean new_location, boolean new_sats) {
|
||||
super.set_gps(gps, new_location, new_sats);
|
||||
AltosCalData cal_data = cal_data();
|
||||
if (gps_series == null)
|
||||
gps_series = new ArrayList<AltosGPSTimeValue>();
|
||||
gps_series.add(new AltosGPSTimeValue(time(), gps));
|
||||
|
||||
if (new_location) {
|
||||
if (sats_in_soln == null) {
|
||||
sats_in_soln = add_series(sats_in_soln_name, null);
|
||||
}
|
||||
sats_in_soln.add(time(), gps.nsat);
|
||||
if (gps.pdop != AltosLib.MISSING) {
|
||||
if (gps_pdop == null)
|
||||
gps_pdop = add_series(gps_pdop_name, null);
|
||||
gps_pdop.add(time(), gps.pdop);
|
||||
}
|
||||
if (gps.hdop != AltosLib.MISSING) {
|
||||
if (gps_hdop == null)
|
||||
gps_hdop = add_series(gps_hdop_name, null);
|
||||
gps_hdop.add(time(), gps.hdop);
|
||||
}
|
||||
if (gps.vdop != AltosLib.MISSING) {
|
||||
if (gps_vdop == null)
|
||||
gps_vdop = add_series(gps_vdop_name, null);
|
||||
gps_vdop.add(time(), gps.vdop);
|
||||
}
|
||||
if (gps.locked) {
|
||||
if (gps.alt != AltosLib.MISSING) {
|
||||
if (gps_altitude == null)
|
||||
gps_altitude = add_series(gps_altitude_name, AltosConvert.height);
|
||||
gps_altitude.add(time(), gps.alt);
|
||||
}
|
||||
if (gps.ground_speed != AltosLib.MISSING) {
|
||||
if (gps_ground_speed == null)
|
||||
gps_ground_speed = add_series(gps_ground_speed_name, AltosConvert.speed);
|
||||
gps_ground_speed.add(time(), gps.ground_speed);
|
||||
}
|
||||
if (gps.climb_rate != AltosLib.MISSING) {
|
||||
if (gps_ascent_rate == null)
|
||||
gps_ascent_rate = add_series(gps_ascent_rate_name, AltosConvert.speed);
|
||||
gps_ascent_rate.add(time(), gps.climb_rate);
|
||||
}
|
||||
if (gps.course != AltosLib.MISSING) {
|
||||
if (gps_course == null)
|
||||
gps_course = add_series(gps_course_name, null);
|
||||
gps_course.add(time(), gps.course);
|
||||
}
|
||||
if (gps.ground_speed != AltosLib.MISSING && gps.climb_rate != AltosLib.MISSING) {
|
||||
if (gps_speed == null)
|
||||
gps_speed = add_series(gps_speed_name, null);
|
||||
gps_speed.add(time(), Math.sqrt(gps.ground_speed * gps.ground_speed +
|
||||
gps.climb_rate * gps.climb_rate));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (new_sats) {
|
||||
if (gps.cc_gps_sat != null) {
|
||||
if (sats_in_view == null)
|
||||
sats_in_view = add_series(sats_in_view_name, null);
|
||||
sats_in_view.add(time(), gps.cc_gps_sat.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String accel_along_name = "Accel Along";
|
||||
public static final String accel_across_name = "Accel Across";
|
||||
public static final String accel_through_name = "Accel Through";
|
||||
|
||||
public AltosTimeSeries accel_along, accel_across, accel_through;
|
||||
|
||||
public static final String gyro_roll_name = "Roll Rate";
|
||||
public static final String gyro_pitch_name = "Pitch Rate";
|
||||
public static final String gyro_yaw_name = "Yaw Rate";
|
||||
|
||||
public AltosTimeSeries gyro_roll, gyro_pitch, gyro_yaw;
|
||||
|
||||
public static final String mag_along_name = "Magnetic Field Along";
|
||||
public static final String mag_across_name = "Magnetic Field Across";
|
||||
public static final String mag_through_name = "Magnetic Field Through";
|
||||
public static final String mag_total_name = "Magnetic Field Strength";
|
||||
public static final String compass_name = "Compass";
|
||||
|
||||
public AltosTimeSeries mag_along, mag_across, mag_through, mag_total, compass;
|
||||
|
||||
public void set_accel(double along, double across, double through) {
|
||||
if (accel_along == null) {
|
||||
accel_along = add_series(accel_along_name, AltosConvert.accel);
|
||||
accel_across = add_series(accel_across_name, AltosConvert.accel);
|
||||
accel_through = add_series(accel_through_name, AltosConvert.accel);
|
||||
}
|
||||
accel_along.add(time(), along);
|
||||
accel_across.add(time(), across);
|
||||
accel_through.add(time(), through);
|
||||
}
|
||||
|
||||
private double accel_ground_along = AltosLib.MISSING;
|
||||
private double accel_ground_across = AltosLib.MISSING;
|
||||
private double accel_ground_through = AltosLib.MISSING;
|
||||
|
||||
private double ground_time;
|
||||
|
||||
public void set_accel_ground(double along, double across, double through) {
|
||||
accel_ground_along = along;
|
||||
accel_ground_across = across;
|
||||
accel_ground_through = through;
|
||||
ground_time = time();
|
||||
}
|
||||
|
||||
public void set_gyro(double roll, double pitch, double yaw) {
|
||||
if (gyro_roll == null) {
|
||||
gyro_roll = add_series(gyro_roll_name, AltosConvert.rotation_rate);
|
||||
gyro_pitch = add_series(gyro_pitch_name, AltosConvert.rotation_rate);
|
||||
gyro_yaw = add_series(gyro_yaw_name, AltosConvert.rotation_rate);
|
||||
}
|
||||
gyro_roll.add(time(), roll);
|
||||
gyro_pitch.add(time(), pitch);
|
||||
gyro_yaw.add(time(), yaw);
|
||||
}
|
||||
|
||||
public void set_mag(double along, double across, double through) {
|
||||
if (mag_along == null) {
|
||||
mag_along = add_series(mag_along_name, AltosConvert.magnetic_field);
|
||||
mag_across = add_series(mag_across_name, AltosConvert.magnetic_field);
|
||||
mag_through = add_series(mag_through_name, AltosConvert.magnetic_field);
|
||||
mag_total = add_series(mag_total_name, AltosConvert.magnetic_field);
|
||||
compass = add_series(compass_name, AltosConvert.orient);
|
||||
}
|
||||
mag_along.add(time(), along);
|
||||
mag_across.add(time(), across);
|
||||
mag_through.add(time(), through);
|
||||
mag_total.add(time(), Math.sqrt(along * along + across * across + through *through));
|
||||
compass.add(time(), Math.atan2(across, through) * 180 / Math.PI);
|
||||
}
|
||||
|
||||
public void set_orient(double orient) {
|
||||
if (orient_series == null)
|
||||
orient_series = add_series(orient_name, AltosConvert.orient);
|
||||
orient_series.add(time(), orient);
|
||||
}
|
||||
|
||||
public static final String pyro_voltage_name = "Pyro Voltage";
|
||||
|
||||
public AltosTimeSeries pyro_voltage;
|
||||
|
||||
public void set_pyro_voltage(double volts) {
|
||||
if (pyro_voltage == null)
|
||||
pyro_voltage = add_series(pyro_voltage_name, AltosConvert.voltage);
|
||||
pyro_voltage.add(time(), volts);
|
||||
}
|
||||
|
||||
private static String[] igniter_voltage_names;
|
||||
|
||||
public String igniter_voltage_name(int channel) {
|
||||
if (igniter_voltage_names == null || igniter_voltage_names.length <= channel) {
|
||||
String[] new_igniter_voltage_names = new String[channel + 1];
|
||||
int i = 0;
|
||||
|
||||
if (igniter_voltage_names != null) {
|
||||
for (; i < igniter_voltage_names.length; i++)
|
||||
new_igniter_voltage_names[i] = igniter_voltage_names[i];
|
||||
}
|
||||
for (; i < channel+1; i++)
|
||||
new_igniter_voltage_names[i] = AltosLib.igniter_name(i);
|
||||
igniter_voltage_names = new_igniter_voltage_names;
|
||||
}
|
||||
return igniter_voltage_names[channel];
|
||||
}
|
||||
|
||||
public AltosTimeSeries[] igniter_voltage;
|
||||
|
||||
public void set_igniter_voltage(double[] voltage) {
|
||||
int channels = voltage.length;
|
||||
if (igniter_voltage == null || igniter_voltage.length <= channels) {
|
||||
AltosTimeSeries[] new_igniter_voltage = new AltosTimeSeries[channels];
|
||||
int i = 0;
|
||||
|
||||
if (igniter_voltage != null) {
|
||||
for (; i < igniter_voltage.length; i++)
|
||||
new_igniter_voltage[i] = igniter_voltage[i];
|
||||
}
|
||||
for (; i < channels; i++)
|
||||
new_igniter_voltage[i] = add_series(igniter_voltage_name(i), AltosConvert.voltage);
|
||||
igniter_voltage = new_igniter_voltage;
|
||||
}
|
||||
for (int channel = 0; channel < voltage.length; channel++)
|
||||
igniter_voltage[channel].add(time(), voltage[channel]);
|
||||
}
|
||||
|
||||
public static final String pyro_fired_name = "Pyro Channel State";
|
||||
|
||||
public AltosTimeSeries pyro_fired_series;
|
||||
|
||||
int last_pyro_mask;
|
||||
|
||||
public void set_pyro_fired(int pyro_mask) {
|
||||
if (pyro_fired_series == null)
|
||||
pyro_fired_series = add_series(pyro_fired_name, AltosConvert.pyro_name);
|
||||
for (int channel = 0; channel < 32; channel++) {
|
||||
if ((last_pyro_mask & (1 << channel)) == 0 &&
|
||||
(pyro_mask & (1 << channel)) != 0) {
|
||||
pyro_fired_series.add(time(), channel);
|
||||
}
|
||||
}
|
||||
last_pyro_mask = pyro_mask;
|
||||
}
|
||||
|
||||
public void set_companion(AltosCompanion companion) {
|
||||
}
|
||||
|
||||
public static final String motor_pressure_name = "Motor Pressure";
|
||||
|
||||
public AltosTimeSeries motor_pressure_series;
|
||||
|
||||
public void set_motor_pressure(double motor_pressure) {
|
||||
if (motor_pressure_series == null)
|
||||
motor_pressure_series = add_series(motor_pressure_name, AltosConvert.pressure);
|
||||
motor_pressure_series.add(time(), motor_pressure);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
compute_orient();
|
||||
if (speed_series == null) {
|
||||
speed_series = compute_speed();
|
||||
if (speed_series != null)
|
||||
add_series(speed_series);
|
||||
}
|
||||
if (accel_series == null) {
|
||||
accel_series = compute_accel();
|
||||
if (accel_series != null) {
|
||||
add_series(accel_series);
|
||||
accel_computed = true;
|
||||
}
|
||||
}
|
||||
compute_height();
|
||||
}
|
||||
|
||||
public AltosTimeSeries[] series() {
|
||||
finish();
|
||||
return series.toArray(new AltosTimeSeries[0]);
|
||||
}
|
||||
|
||||
public AltosFlightSeries(AltosCalData cal_data) {
|
||||
super(cal_data);
|
||||
}
|
||||
}
|
||||
279
altoslib/AltosFlightStats.java
Normal file
279
altoslib/AltosFlightStats.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosFlightStats {
|
||||
public double max_height;
|
||||
public double max_gps_height;
|
||||
public double max_speed;
|
||||
public double max_acceleration;
|
||||
public double[] state_speed = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public double[] state_enter_speed = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public double[] state_enter_height = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public double[] state_enter_gps_height = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public double[] state_accel = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public double[] state_time = new double[AltosLib.ao_flight_invalid + 1];
|
||||
public String product;
|
||||
public String firmware_version;
|
||||
public int serial;
|
||||
public int flight;
|
||||
public int year, month, day;
|
||||
public int hour, minute, second;
|
||||
public double boost_time;
|
||||
public double landed_time;
|
||||
public double lat, lon;
|
||||
public double pad_lat, pad_lon;
|
||||
public boolean has_flight_data;
|
||||
public boolean has_gps;
|
||||
public boolean has_gps_sats;
|
||||
public boolean has_gps_detail;
|
||||
public boolean has_flight_adc;
|
||||
public boolean has_battery;
|
||||
public boolean has_rssi;
|
||||
public boolean has_imu;
|
||||
public boolean has_mag;
|
||||
public boolean has_orient;
|
||||
public int num_igniter;
|
||||
|
||||
double landed_time(AltosFlightSeries series) {
|
||||
double landed_state_time = AltosLib.MISSING;
|
||||
|
||||
double prev_state_time = AltosLib.MISSING;
|
||||
if (series.state_series != null) {
|
||||
for (AltosTimeValue state : series.state_series) {
|
||||
if (state.value == AltosLib.ao_flight_landed) {
|
||||
landed_state_time = state.time;
|
||||
break;
|
||||
} else {
|
||||
prev_state_time = state.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (landed_state_time == AltosLib.MISSING && series.height_series != null)
|
||||
landed_state_time = series.height_series.get(series.height_series.size()-1).time;
|
||||
|
||||
double landed_height = AltosLib.MISSING;
|
||||
|
||||
if (series.height_series != null) {
|
||||
for (AltosTimeValue height : series.height_series) {
|
||||
landed_height = height.value;
|
||||
if (height.time >= landed_state_time)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (landed_height == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
boolean above = true;
|
||||
|
||||
double landed_time = AltosLib.MISSING;
|
||||
|
||||
if (series.height_series != null) {
|
||||
for (AltosTimeValue height : series.height_series) {
|
||||
if (height.value > landed_height + 10) {
|
||||
above = true;
|
||||
} else {
|
||||
if (above && Math.abs(height.value - landed_height) < 2) {
|
||||
above = false;
|
||||
landed_time = height.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (landed_time == AltosLib.MISSING || (prev_state_time != AltosLib.MISSING && landed_time < prev_state_time))
|
||||
landed_time = landed_state_time;
|
||||
return landed_time;
|
||||
}
|
||||
|
||||
double boost_time(AltosFlightSeries series) {
|
||||
double boost_time = AltosLib.MISSING;
|
||||
double boost_state_time = AltosLib.MISSING;
|
||||
|
||||
if (series.state_series != null) {
|
||||
for (AltosTimeValue state : series.state_series) {
|
||||
if (state.value >= AltosLib.ao_flight_boost && state.value <= AltosLib.ao_flight_landed) {
|
||||
boost_state_time = state.time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (series.accel_series != null) {
|
||||
for (AltosTimeValue accel : series.accel_series) {
|
||||
if (accel.value < 1)
|
||||
boost_time = accel.time;
|
||||
if (boost_state_time != AltosLib.MISSING && accel.time >= boost_state_time)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (boost_time == AltosLib.MISSING)
|
||||
boost_time = boost_state_time;
|
||||
return boost_time;
|
||||
}
|
||||
|
||||
private void add_times(AltosFlightSeries series, int state, double start_time, double end_time) {
|
||||
double delta_time = end_time - start_time;
|
||||
if (0 <= state && state <= AltosLib.ao_flight_invalid && delta_time > 0) {
|
||||
if (state_enter_speed[state] == AltosLib.MISSING)
|
||||
state_enter_speed[state] = series.speed_series.value(start_time);
|
||||
if (state_enter_height[state] == AltosLib.MISSING)
|
||||
state_enter_height[state] = series.height_series.value(start_time);
|
||||
if (state_enter_gps_height[state] == AltosLib.MISSING)
|
||||
if (series.gps_height != null)
|
||||
state_enter_gps_height[state] = series.gps_height.value(start_time);
|
||||
speeds[state].value += series.speed_series.average(start_time, end_time) * delta_time;
|
||||
speeds[state].time += delta_time;
|
||||
accels[state].value += series.accel_series.average(start_time, end_time) * delta_time;
|
||||
accels[state].time += delta_time;
|
||||
state_time[state] += delta_time;
|
||||
|
||||
if (state == AltosLib.ao_flight_boost) {
|
||||
AltosTimeValue tv_speed = series.speed_series.max(start_time, end_time);
|
||||
if (tv_speed != null && (max_speed == AltosLib.MISSING || tv_speed.value > max_speed))
|
||||
max_speed = tv_speed.value;
|
||||
AltosTimeValue tv_accel = series.accel_series.max(start_time, end_time);
|
||||
if (tv_accel != null && (max_acceleration == AltosLib.MISSING || tv_accel.value > max_acceleration))
|
||||
max_acceleration = tv_accel.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AltosTimeValue[] speeds = new AltosTimeValue[AltosLib.ao_flight_invalid + 1];
|
||||
AltosTimeValue[] accels = new AltosTimeValue[AltosLib.ao_flight_invalid + 1];
|
||||
|
||||
public AltosFlightStats(AltosFlightSeries series) {
|
||||
AltosCalData cal_data = series.cal_data();
|
||||
|
||||
series.finish();
|
||||
|
||||
boost_time = boost_time(series);
|
||||
landed_time = landed_time(series);
|
||||
|
||||
if (series.state_series != null){
|
||||
boolean fixed_boost = false;
|
||||
boolean fixed_landed = false;
|
||||
for (AltosTimeValue state : series.state_series) {
|
||||
if ((int) state.value == AltosLib.ao_flight_boost)
|
||||
if (boost_time != AltosLib.MISSING && !fixed_boost) {
|
||||
state.time = boost_time;
|
||||
fixed_boost = true;
|
||||
}
|
||||
if ((int) state.value == AltosLib.ao_flight_landed)
|
||||
if (landed_time != AltosLib.MISSING && !fixed_landed) {
|
||||
state.time = landed_time;
|
||||
fixed_landed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
year = month = day = AltosLib.MISSING;
|
||||
hour = minute = second = AltosLib.MISSING;
|
||||
serial = flight = AltosLib.MISSING;
|
||||
lat = lon = AltosLib.MISSING;
|
||||
has_flight_data = false;
|
||||
has_gps = false;
|
||||
has_gps_sats = false;
|
||||
has_flight_adc = false;
|
||||
has_battery = false;
|
||||
has_rssi = false;
|
||||
has_imu = false;
|
||||
has_mag = false;
|
||||
has_orient = false;
|
||||
|
||||
for (int s = 0; s < AltosLib.ao_flight_invalid + 1; s++) {
|
||||
state_speed[s] = AltosLib.MISSING;
|
||||
state_enter_speed[s] = AltosLib.MISSING;
|
||||
state_accel[s] = AltosLib.MISSING;
|
||||
state_time[s] = 0;
|
||||
speeds[s] = new AltosTimeValue(0, 0);
|
||||
accels[s] = new AltosTimeValue(0, 0);
|
||||
}
|
||||
|
||||
max_speed = AltosLib.MISSING;
|
||||
max_acceleration = AltosLib.MISSING;
|
||||
|
||||
if (series.state_series != null) {
|
||||
AltosTimeValue prev = null;
|
||||
for (AltosTimeValue state : series.state_series) {
|
||||
if (prev != null)
|
||||
add_times(series, (int) prev.value, prev.time, state.time);
|
||||
prev = state;
|
||||
}
|
||||
if (prev != null) {
|
||||
AltosTimeValue last_accel = series.accel_series.last();
|
||||
if (last_accel != null)
|
||||
add_times(series, (int) prev.value, prev.time, last_accel.time);
|
||||
}
|
||||
}
|
||||
|
||||
for (int s = 0; s <= AltosLib.ao_flight_invalid; s++) {
|
||||
if (speeds[s].time > 0)
|
||||
state_speed[s] = speeds[s].value / speeds[s].time;
|
||||
if (accels[s].time > 0)
|
||||
state_accel[s] = accels[s].value / accels[s].time;
|
||||
}
|
||||
|
||||
product = cal_data.product;
|
||||
firmware_version = cal_data.firmware_version;
|
||||
serial = cal_data.serial;
|
||||
flight = cal_data.flight;
|
||||
|
||||
has_battery = series.battery_voltage_series != null;
|
||||
has_flight_adc = series.main_voltage_series != null;
|
||||
has_rssi = series.rssi_series != null;
|
||||
has_flight_data = series.pressure_series != null;
|
||||
|
||||
AltosGPS gps = series.cal_data().gps_pad;
|
||||
|
||||
if (gps != null) {
|
||||
year = gps.year;
|
||||
month = gps.month;
|
||||
day = gps.day;
|
||||
hour = gps.hour;
|
||||
minute = gps.minute;
|
||||
second = gps.second;
|
||||
has_gps = true;
|
||||
lat = pad_lat = gps.lat;
|
||||
lon = pad_lon = gps.lon;
|
||||
if (series.gps_series != null) {
|
||||
for (AltosGPSTimeValue gtv : series.gps_series) {
|
||||
gps = gtv.gps;
|
||||
if (gps.locked && gps.nsat >= 4) {
|
||||
lat = gps.lat;
|
||||
lon = gps.lon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
max_height = series.max_height;
|
||||
if (max_height == AltosLib.MISSING && series.height_series != null)
|
||||
max_height = series.height_series.max().value;
|
||||
max_gps_height = AltosLib.MISSING;
|
||||
if (series.gps_height != null) {
|
||||
AltosTimeValue tv = series.gps_height.max();
|
||||
if (tv != null)
|
||||
max_gps_height = tv.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
altoslib/AltosFontListener.java
Normal file
23
altoslib/AltosFontListener.java
Normal 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.altoslib_14;
|
||||
|
||||
public interface AltosFontListener {
|
||||
void font_size_changed(int font_size);
|
||||
}
|
||||
49
altoslib/AltosForce.java
Normal file
49
altoslib/AltosForce.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altoslib_14;
|
||||
|
||||
public class AltosForce extends AltosUnits {
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.n_to_lb(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.lb_to_n(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "lbs";
|
||||
return "N";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "pounds";
|
||||
return "newtons";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
return width / 9;
|
||||
}
|
||||
}
|
||||
68
altoslib/AltosFrequency.java
Normal file
68
altoslib/AltosFrequency.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
|
||||
public class AltosFrequency {
|
||||
public double frequency;
|
||||
public String description;
|
||||
|
||||
public int hashCode() {
|
||||
return Double.valueOf(frequency).hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
if (!(o instanceof AltosFrequency))
|
||||
return false;
|
||||
AltosFrequency other = (AltosFrequency) o;
|
||||
return other.frequency == frequency;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%7.3f MHz %-20s",
|
||||
frequency, description);
|
||||
}
|
||||
|
||||
public String toShortString() {
|
||||
return String.format("%7.3f MHz %s",
|
||||
frequency, description);
|
||||
}
|
||||
|
||||
public String frequency_string() {
|
||||
return String.format("%7.3f", frequency);
|
||||
}
|
||||
|
||||
public boolean close(double f) {
|
||||
double diff = Math.abs(frequency - f);
|
||||
|
||||
return diff < 0.010;
|
||||
}
|
||||
public AltosFrequency(double f, String d) {
|
||||
frequency = f;
|
||||
description = d;
|
||||
}
|
||||
public AltosFrequency() {
|
||||
this(0, null);
|
||||
}
|
||||
}
|
||||
424
altoslib/AltosGPS.java
Normal file
424
altoslib/AltosGPS.java
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
import java.time.*;
|
||||
|
||||
public class AltosGPS implements Cloneable {
|
||||
|
||||
public final static int MISSING = AltosLib.MISSING;
|
||||
|
||||
public int nsat;
|
||||
public boolean locked;
|
||||
public boolean connected;
|
||||
public double lat; /* degrees (+N -S) */
|
||||
public double lon; /* degrees (+E -W) */
|
||||
public double alt; /* m */
|
||||
public int year;
|
||||
public int month;
|
||||
public int day;
|
||||
public int hour;
|
||||
public int minute;
|
||||
public int second;
|
||||
|
||||
public double ground_speed; /* m/s */
|
||||
public int course; /* degrees */
|
||||
public double climb_rate; /* m/s */
|
||||
public double pdop; /* unitless */
|
||||
public double hdop; /* unitless */
|
||||
public double vdop; /* unitless */
|
||||
public double h_error; /* m */
|
||||
public double v_error; /* m */
|
||||
|
||||
public AltosGPSSat[] cc_gps_sat; /* tracking data */
|
||||
|
||||
public 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]);
|
||||
}
|
||||
|
||||
public 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);
|
||||
hour = AltosParse.parse_int(hms[0]);
|
||||
minute = AltosParse.parse_int(hms[1]);
|
||||
second = AltosParse.parse_int(hms[2]);
|
||||
}
|
||||
|
||||
public void ClearGPSTime() {
|
||||
year = month = day = AltosLib.MISSING;
|
||||
hour = minute = second = AltosLib.MISSING;
|
||||
}
|
||||
|
||||
/* Return time since epoc in seconds */
|
||||
public long seconds() {
|
||||
if (year == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (month == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (day == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (hour == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (minute == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (second == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
OffsetDateTime odt = OffsetDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC);
|
||||
return odt.toEpochSecond();
|
||||
}
|
||||
|
||||
public AltosLatLon lat_lon() {
|
||||
return new AltosLatLon(lat, lon);
|
||||
}
|
||||
|
||||
public AltosGPS(AltosTelemetryMap map) throws ParseException {
|
||||
String state = map.get_string(AltosTelemetryLegacy.AO_TELEM_GPS_STATE,
|
||||
AltosTelemetryLegacy.AO_TELEM_GPS_STATE_ERROR);
|
||||
|
||||
nsat = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_NUM_SAT, 0);
|
||||
if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_LOCKED)) {
|
||||
connected = true;
|
||||
locked = true;
|
||||
lat = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
|
||||
lon = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
|
||||
alt = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_ALTITUDE, MISSING);
|
||||
year = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_YEAR, MISSING);
|
||||
if (year != MISSING)
|
||||
year += 2000;
|
||||
month = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MONTH, MISSING);
|
||||
day = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_DAY, MISSING);
|
||||
|
||||
hour = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HOUR, 0);
|
||||
minute = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MINUTE, 0);
|
||||
second = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_SECOND, 0);
|
||||
|
||||
ground_speed = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HORIZONTAL_SPEED,
|
||||
AltosLib.MISSING, 1/100.0);
|
||||
course = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_COURSE,
|
||||
AltosLib.MISSING);
|
||||
pdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_PDOP, MISSING, 1.0);
|
||||
hdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HDOP, MISSING, 1.0);
|
||||
vdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_VDOP, MISSING, 1.0);
|
||||
h_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HERROR, MISSING);
|
||||
v_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_VERROR, MISSING);
|
||||
} else if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_UNLOCKED)) {
|
||||
connected = true;
|
||||
locked = false;
|
||||
} else {
|
||||
connected = false;
|
||||
locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean parse_string (String line, boolean says_done) {
|
||||
String[] bits = line.split("\\s+");
|
||||
if (bits.length == 0)
|
||||
return false;
|
||||
if (line.startsWith("Date:")) {
|
||||
if (bits.length < 2)
|
||||
return false;
|
||||
String[] d = bits[1].split("/");
|
||||
if (d.length < 3)
|
||||
return false;
|
||||
year = Integer.parseInt(d[0]) + 2000;
|
||||
month = Integer.parseInt(d[1]);
|
||||
day = Integer.parseInt(d[2]);
|
||||
} else if (line.startsWith("Time:")) {
|
||||
if (bits.length < 2)
|
||||
return false;
|
||||
String[] d = bits[1].split(":");
|
||||
if (d.length < 3)
|
||||
return false;
|
||||
hour = Integer.parseInt(d[0]);
|
||||
minute = Integer.parseInt(d[1]);
|
||||
second = Integer.parseInt(d[2]);
|
||||
} else if (line.startsWith("Lat/Lon:")) {
|
||||
if (bits.length < 3)
|
||||
return false;
|
||||
lat = Integer.parseInt(bits[1]) * 1.0e-7;
|
||||
lon = Integer.parseInt(bits[2]) * 1.0e-7;
|
||||
} else if (line.startsWith("Alt:")) {
|
||||
if (bits.length < 2)
|
||||
return false;
|
||||
alt = Integer.parseInt(bits[1]);
|
||||
} else if (line.startsWith("Pdop/Hdop/Vdop:")) {
|
||||
if (bits.length < 4)
|
||||
return false;
|
||||
pdop = Integer.parseInt(bits[1]) / 10.0;
|
||||
hdop = Integer.parseInt(bits[2]) / 10.0;
|
||||
vdop = Integer.parseInt(bits[3]) / 10.0;
|
||||
} else if (line.startsWith("Ground Speed/Climb Rate/Course:")) {
|
||||
if (bits.length < 6)
|
||||
return false;
|
||||
ground_speed = Integer.parseInt(bits[3]) * 1.0e-2;
|
||||
climb_rate = Integer.parseInt(bits[4]) * 1.0e-2;
|
||||
course = Integer.parseInt(bits[5]) * 2;
|
||||
} else if (line.startsWith("Flags:")) {
|
||||
if (bits.length < 2)
|
||||
return false;
|
||||
int status = Integer.decode(bits[1]);
|
||||
connected = (status & AltosLib.AO_GPS_RUNNING) != 0;
|
||||
locked = (status & AltosLib.AO_GPS_VALID) != 0;
|
||||
nsat = (status >> AltosLib.AO_GPS_NUM_SAT_SHIFT) & AltosLib.AO_GPS_NUM_SAT_MASK;
|
||||
if (!says_done)
|
||||
return false;
|
||||
} else if (line.startsWith("Sats:")) {
|
||||
if (bits.length < 2)
|
||||
return false;
|
||||
int nsvs = Integer.parseInt(bits[1]);
|
||||
cc_gps_sat = new AltosGPSSat[nsvs];
|
||||
for (int i = 0; i < nsvs; i++) {
|
||||
int svid = Integer.parseInt(bits[2+i*2]);
|
||||
int cc_n0 = Integer.parseInt(bits[3+i*2]);
|
||||
cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
|
||||
}
|
||||
} else if (line.startsWith("done")) {
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
connected = false;
|
||||
locked = false;
|
||||
lat = lon = 0;
|
||||
alt = 0;
|
||||
ClearGPSTime();
|
||||
if ((words[i]).equals("unlocked")) {
|
||||
connected = true;
|
||||
i++;
|
||||
} else if ((words[i]).equals("not-connected")) {
|
||||
i++;
|
||||
} else if (words.length >= 40) {
|
||||
locked = true;
|
||||
connected = true;
|
||||
|
||||
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(words[i++]);
|
||||
if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
|
||||
ground_speed = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(H)"));
|
||||
course = AltosParse.parse_int(words[i++]);
|
||||
climb_rate = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(V)"));
|
||||
hdop = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "(hdop)"));
|
||||
h_error = AltosParse.parse_int(words[i++]);
|
||||
v_error = AltosParse.parse_int(words[i++]);
|
||||
}
|
||||
} else {
|
||||
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 AltosGPSSat[tracking_channels];
|
||||
for (int chan = 0; chan < tracking_channels; chan++) {
|
||||
cc_gps_sat[chan] = new 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 AltosGPSSat[0];
|
||||
}
|
||||
|
||||
public void set_latitude(int in_lat) {
|
||||
lat = in_lat / 10.0e7;
|
||||
}
|
||||
|
||||
public void set_longitude(int in_lon) {
|
||||
lon = in_lon / 10.0e7;
|
||||
}
|
||||
|
||||
public void set_time(int in_hour, int in_minute, int in_second) {
|
||||
hour = in_hour;
|
||||
minute = in_minute;
|
||||
second = in_second;
|
||||
}
|
||||
|
||||
public void set_date(int in_year, int in_month, int in_day) {
|
||||
year = in_year;
|
||||
month = in_month;
|
||||
day = in_day;
|
||||
}
|
||||
|
||||
/*
|
||||
public void set_flags(int in_flags) {
|
||||
flags = in_flags;
|
||||
}
|
||||
*/
|
||||
|
||||
public void set_altitude(int in_altitude) {
|
||||
alt = in_altitude;
|
||||
}
|
||||
|
||||
public void add_sat(int svid, int c_n0) {
|
||||
if (cc_gps_sat == null) {
|
||||
cc_gps_sat = new AltosGPSSat[1];
|
||||
} else {
|
||||
AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1];
|
||||
for (int i = 0; i < cc_gps_sat.length; i++)
|
||||
new_gps_sat[i] = cc_gps_sat[i];
|
||||
cc_gps_sat = new_gps_sat;
|
||||
}
|
||||
AltosGPSSat sat = new AltosGPSSat();
|
||||
sat.svid = svid;
|
||||
sat.c_n0 = c_n0;
|
||||
cc_gps_sat[cc_gps_sat.length - 1] = sat;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
lat = AltosLib.MISSING;
|
||||
lon = AltosLib.MISSING;
|
||||
alt = AltosLib.MISSING;
|
||||
ground_speed = AltosLib.MISSING;
|
||||
course = AltosLib.MISSING;
|
||||
climb_rate = AltosLib.MISSING;
|
||||
pdop = AltosLib.MISSING;
|
||||
hdop = AltosLib.MISSING;
|
||||
vdop = AltosLib.MISSING;
|
||||
h_error = AltosLib.MISSING;
|
||||
v_error = AltosLib.MISSING;
|
||||
ClearGPSTime();
|
||||
cc_gps_sat = null;
|
||||
}
|
||||
|
||||
public AltosGPS() {
|
||||
init();
|
||||
}
|
||||
|
||||
public AltosGPS clone() {
|
||||
AltosGPS g = new AltosGPS();
|
||||
|
||||
g.nsat = nsat;
|
||||
g.locked = locked;
|
||||
g.connected = connected;
|
||||
g.lat = lat; /* degrees (+N -S) */
|
||||
g.lon = lon; /* degrees (+E -W) */
|
||||
g.alt = alt; /* m */
|
||||
g.year = year;
|
||||
g.month = month;
|
||||
g.day = day;
|
||||
g.hour = hour;
|
||||
g.minute = minute;
|
||||
g.second = second;
|
||||
|
||||
g.ground_speed = ground_speed; /* m/s */
|
||||
g.course = course; /* degrees */
|
||||
g.climb_rate = climb_rate; /* m/s */
|
||||
g.pdop = pdop; /* unitless */
|
||||
g.hdop = hdop; /* unitless */
|
||||
g.vdop = vdop; /* unitless */
|
||||
g.h_error = h_error; /* m */
|
||||
g.v_error = v_error; /* m */
|
||||
|
||||
if (cc_gps_sat != null) {
|
||||
g.cc_gps_sat = new AltosGPSSat[cc_gps_sat.length];
|
||||
for (int i = 0; i < cc_gps_sat.length; i++) {
|
||||
g.cc_gps_sat[i] = new AltosGPSSat(cc_gps_sat[i].svid,
|
||||
cc_gps_sat[i].c_n0);
|
||||
}
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
public AltosGPS(AltosGPS old) {
|
||||
if (old != null) {
|
||||
nsat = old.nsat;
|
||||
locked = old.locked;
|
||||
connected = old.connected;
|
||||
lat = old.lat; /* degrees (+N -S) */
|
||||
lon = old.lon; /* degrees (+E -W) */
|
||||
alt = old.alt; /* m */
|
||||
year = old.year;
|
||||
month = old.month;
|
||||
day = old.day;
|
||||
hour = old.hour;
|
||||
minute = old.minute;
|
||||
second = old.second;
|
||||
|
||||
ground_speed = old.ground_speed; /* m/s */
|
||||
course = old.course; /* degrees */
|
||||
climb_rate = old.climb_rate; /* m/s */
|
||||
pdop = old.pdop; /* unitless? */
|
||||
hdop = old.hdop; /* unitless? */
|
||||
vdop = old.vdop; /* unitless? */
|
||||
h_error = old.h_error; /* m */
|
||||
v_error = old.v_error; /* m */
|
||||
|
||||
if (old.cc_gps_sat != null) {
|
||||
cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
|
||||
for (int i = 0; i < old.cc_gps_sat.length; i++) {
|
||||
cc_gps_sat[i] = new AltosGPSSat();
|
||||
cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
|
||||
cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
|
||||
try {
|
||||
AltosGPS gps = new AltosGPS(link, link.config_data());
|
||||
if (gps != null)
|
||||
listener.set_gps(gps, true, true);
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosGPS (AltosLink link, AltosConfigData config_data) throws TimeoutException, InterruptedException {
|
||||
boolean says_done = config_data.compare_version("1.0") >= 0;
|
||||
init();
|
||||
link.printf("g\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
if (!parse_string(line, says_done))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
altoslib/AltosGPSSat.java
Normal file
38
altoslib/AltosGPSSat.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosGPSSat {
|
||||
public int svid;
|
||||
public int c_n0;
|
||||
|
||||
public AltosGPSSat(int s, int c) {
|
||||
svid = s;
|
||||
c_n0= c;
|
||||
}
|
||||
|
||||
public AltosGPSSat() {
|
||||
}
|
||||
}
|
||||
|
||||
28
altoslib/AltosGPSTimeValue.java
Normal file
28
altoslib/AltosGPSTimeValue.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
package org.altusmetrum.altoslib_14;
|
||||
|
||||
public class AltosGPSTimeValue {
|
||||
public double time;
|
||||
public AltosGPS gps;
|
||||
|
||||
public AltosGPSTimeValue(double time, AltosGPS gps) {
|
||||
this.time = time;
|
||||
this.gps = gps;
|
||||
}
|
||||
}
|
||||
62
altoslib/AltosGauss.java
Normal file
62
altoslib/AltosGauss.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright © 2020 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 java.io.*;
|
||||
|
||||
public class AltosGauss extends AltosUnits {
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
return "G";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
return "gauss";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
return width - 1;
|
||||
}
|
||||
|
||||
public AltosGauss() {
|
||||
range_metric = new AltosUnitsRange[1];
|
||||
|
||||
range_metric[0] = new AltosUnitsRange(0, "µT", "microtesla") {
|
||||
double value(double v) {
|
||||
return v * 100;
|
||||
}
|
||||
int show_fraction(int width) {
|
||||
return width / 9;
|
||||
}
|
||||
int say_fraction() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
range_imperial = range_metric;
|
||||
}
|
||||
}
|
||||
110
altoslib/AltosGreatCircle.java
Normal file
110
altoslib/AltosGreatCircle.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.io.*;
|
||||
|
||||
public class AltosGreatCircle implements Cloneable {
|
||||
public double distance;
|
||||
public double bearing;
|
||||
public double range;
|
||||
public double elevation;
|
||||
|
||||
double sqr(double a) { return a * a; }
|
||||
|
||||
public static final double rad = Math.PI / 180;
|
||||
public static final double earth_radius = 6371.2 * 1000; /* in meters */
|
||||
|
||||
public static final int BEARING_LONG = AltosConvert.BEARING_LONG;
|
||||
public static final int BEARING_SHORT = AltosConvert.BEARING_SHORT;
|
||||
public static final int BEARING_VOICE = AltosConvert.BEARING_VOICE;
|
||||
|
||||
public String bearing_words(int length) {
|
||||
return AltosConvert.bearing_to_words(length, bearing);
|
||||
}
|
||||
|
||||
public AltosGreatCircle (double start_lat, double start_lon, double start_alt,
|
||||
double end_lat, double end_lon, double end_alt) {
|
||||
double lat1 = rad * start_lat;
|
||||
double lon1 = rad * -start_lon;
|
||||
double lat2 = rad * end_lat;
|
||||
double lon2 = rad * -end_lon;
|
||||
|
||||
double d_lon = lon2 - lon1;
|
||||
|
||||
/* From http://en.wikipedia.org/wiki/Great-circle_distance */
|
||||
double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) +
|
||||
sqr(Math.cos(lat1) * Math.sin(lat2) -
|
||||
Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon)));
|
||||
double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon);
|
||||
double d = Math.atan2(vdn,vdd);
|
||||
double course;
|
||||
|
||||
if (Math.cos(lat1) < 1e-20) {
|
||||
if (lat1 > 0)
|
||||
course = Math.PI;
|
||||
else
|
||||
course = -Math.PI;
|
||||
} else {
|
||||
if (d < 1e-10)
|
||||
course = 0;
|
||||
else
|
||||
course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) /
|
||||
(Math.sin(d)*Math.cos(lat1)));
|
||||
if (Math.sin(lon2-lon1) > 0)
|
||||
course = 2 * Math.PI-course;
|
||||
}
|
||||
distance = d * earth_radius;
|
||||
if (Double.isNaN(course) || Double.isInfinite(course))
|
||||
bearing = 0;
|
||||
else
|
||||
bearing = course * 180/Math.PI;
|
||||
|
||||
double height_diff = end_alt - start_alt;
|
||||
range = Math.sqrt(distance * distance + height_diff * height_diff);
|
||||
elevation = Math.atan2(height_diff, distance) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
public AltosGreatCircle clone() {
|
||||
AltosGreatCircle n = new AltosGreatCircle();
|
||||
|
||||
n.distance = distance;
|
||||
n.bearing = bearing;
|
||||
n.range = range;
|
||||
n.elevation = elevation;
|
||||
return n;
|
||||
}
|
||||
|
||||
public AltosGreatCircle (double start_lat, double start_lon,
|
||||
double end_lat, double end_lon) {
|
||||
this(start_lat, start_lon, 0, end_lat, end_lon, 0);
|
||||
}
|
||||
|
||||
public AltosGreatCircle(AltosGPS start, AltosGPS end) {
|
||||
this(start.lat, start.lon, start.alt, end.lat, end.lon, end.alt);
|
||||
}
|
||||
|
||||
public AltosGreatCircle() {
|
||||
distance = 0;
|
||||
bearing = 0;
|
||||
range = 0;
|
||||
elevation = 0;
|
||||
}
|
||||
}
|
||||
50
altoslib/AltosHeight.java
Normal file
50
altoslib/AltosHeight.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosHeight extends AltosUnits {
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.meters_to_feet(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return AltosConvert.feet_to_meters(v);
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "ft";
|
||||
return "m";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
if (imperial_units)
|
||||
return "feet";
|
||||
return "meters";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
return width / 9;
|
||||
}
|
||||
}
|
||||
485
altoslib/AltosHexfile.java
Normal file
485
altoslib/AltosHexfile.java
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Arrays;
|
||||
|
||||
class HexFileInputStream extends PushbackInputStream {
|
||||
public int line;
|
||||
|
||||
public HexFileInputStream(FileInputStream o) {
|
||||
super(new BufferedInputStream(o));
|
||||
line = 1;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int c = super.read();
|
||||
if (c == '\n')
|
||||
line++;
|
||||
return c;
|
||||
}
|
||||
|
||||
public void unread(int c) throws IOException {
|
||||
if (c == '\n')
|
||||
line--;
|
||||
if (c != -1)
|
||||
super.unread(c);
|
||||
}
|
||||
}
|
||||
|
||||
class HexRecord implements Comparable<Object> {
|
||||
public long address;
|
||||
public int type;
|
||||
public byte checksum;
|
||||
public byte[] data;
|
||||
|
||||
static final int NORMAL = 0;
|
||||
static final int EOF = 1;
|
||||
static final int EXTENDED_ADDRESS = 2;
|
||||
|
||||
enum read_state {
|
||||
marker,
|
||||
length,
|
||||
address,
|
||||
type,
|
||||
data,
|
||||
checksum,
|
||||
newline,
|
||||
white,
|
||||
done,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
boolean isspace(int c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public byte checksum() {
|
||||
byte got = 0;
|
||||
|
||||
got += data.length;
|
||||
got += (address >> 8) & 0xff;
|
||||
got += (address ) & 0xff;
|
||||
got += type;
|
||||
for (int i = 0; i < data.length; i++)
|
||||
got += data[i];
|
||||
return (byte) (-got);
|
||||
}
|
||||
|
||||
public int compareTo(Object other) {
|
||||
HexRecord o = (HexRecord) other;
|
||||
|
||||
long diff = address - o.address;
|
||||
|
||||
if (diff > 0)
|
||||
return 1;
|
||||
if (diff < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%04x: %02x (%d)", address, type, data.length);
|
||||
}
|
||||
|
||||
public HexRecord(HexFileInputStream input) throws IOException, EOFException {
|
||||
read_state state = read_state.marker;
|
||||
long nhexbytes = 0;
|
||||
long hex = 0;
|
||||
int ndata = 0;
|
||||
byte got_checksum;
|
||||
|
||||
while (state != read_state.done) {
|
||||
int c = input.read();
|
||||
if (c < 0 && state != read_state.white && state != read_state.marker)
|
||||
throw new IOException(String.format("%d: Unexpected EOF", input.line));
|
||||
if (c == ' ')
|
||||
continue;
|
||||
switch (state) {
|
||||
case marker:
|
||||
if (c == EOF || c == -1)
|
||||
throw new EOFException();
|
||||
if (c != ':')
|
||||
throw new IOException(String.format ("Missing ':' (got %x)", c));
|
||||
state = read_state.length;
|
||||
nhexbytes = 2;
|
||||
hex = 0;
|
||||
break;
|
||||
case length:
|
||||
case address:
|
||||
case type:
|
||||
case data:
|
||||
case checksum:
|
||||
if(!ishex(c))
|
||||
throw new IOException(String.format("Non-hex char '%c'", c));
|
||||
hex = hex << 4 | fromhex(c);
|
||||
--nhexbytes;
|
||||
if (nhexbytes != 0)
|
||||
break;
|
||||
|
||||
switch (state) {
|
||||
case length:
|
||||
data = new byte[(int) hex];
|
||||
state = read_state.address;
|
||||
nhexbytes = 4;
|
||||
break;
|
||||
case address:
|
||||
address = hex;
|
||||
state = read_state.type;
|
||||
nhexbytes = 2;
|
||||
break;
|
||||
case type:
|
||||
type = (int) hex;
|
||||
if (data.length > 0)
|
||||
state = read_state.data;
|
||||
else
|
||||
state = read_state.checksum;
|
||||
nhexbytes = 2;
|
||||
ndata = 0;
|
||||
break;
|
||||
case data:
|
||||
data[ndata] = (byte) hex;
|
||||
ndata++;
|
||||
nhexbytes = 2;
|
||||
if (ndata == data.length)
|
||||
state = read_state.checksum;
|
||||
break;
|
||||
case checksum:
|
||||
checksum = (byte) hex;
|
||||
state = read_state.newline;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
hex = 0;
|
||||
break;
|
||||
case newline:
|
||||
if (c != '\n' && c != '\r')
|
||||
throw new IOException("Missing newline");
|
||||
state = read_state.white;
|
||||
break;
|
||||
case white:
|
||||
if (!isspace(c)) {
|
||||
input.unread(c);
|
||||
state = read_state.done;
|
||||
}
|
||||
break;
|
||||
case done:
|
||||
break;
|
||||
}
|
||||
}
|
||||
got_checksum = checksum();
|
||||
if (got_checksum != checksum)
|
||||
throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
|
||||
checksum, got_checksum));
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosHexfile {
|
||||
public long address;
|
||||
public long max_address;
|
||||
public byte[] data;
|
||||
LinkedList<AltosHexsym> symlist = new LinkedList<AltosHexsym>();
|
||||
|
||||
public byte get_byte(long a) {
|
||||
return data[(int) (a - address)];
|
||||
}
|
||||
|
||||
public int get_u8(long a) {
|
||||
return ((int) get_byte(a)) & 0xff;
|
||||
}
|
||||
|
||||
public int get_u16(long a) {
|
||||
return get_u8(a) | (get_u8(a+1) << 8);
|
||||
}
|
||||
|
||||
/* CC1111-based products have the romconfig stuff located
|
||||
* at a fixed address; when the file we load has no symbols,
|
||||
* assume it is one of those and set the symbols appropriately
|
||||
*/
|
||||
final static int ao_romconfig_version_addr = 0xa0;
|
||||
final static int ao_romconfig_check_addr = 0xa2;
|
||||
final static int ao_serial_number_addr = 0xa4;
|
||||
final static int ao_radio_cal_addr = 0xa6;
|
||||
final static int ao_usb_descriptors_addr = 0xaa;
|
||||
|
||||
static AltosHexsym[] cc_symbols = {
|
||||
new AltosHexsym("ao_romconfig_version", ao_romconfig_version_addr),
|
||||
new AltosHexsym("ao_romconfig_check", ao_romconfig_check_addr),
|
||||
new AltosHexsym("ao_serial_number", ao_serial_number_addr),
|
||||
new AltosHexsym("ao_radio_cal", ao_radio_cal_addr),
|
||||
new AltosHexsym("ao_usb_descriptors", ao_usb_descriptors_addr)
|
||||
};
|
||||
|
||||
static final int AO_USB_DESC_DEVICE = 1;
|
||||
static final int AO_USB_DESC_STRING = 3;
|
||||
|
||||
static final int AO_ROMCONFIG_VERSION_INDEX = 0;
|
||||
static final int AO_ROMCONFIG_CHECK_INDEX = 1;
|
||||
static final int AO_SERIAL_NUMBER_INDEX = 2;
|
||||
static final int AO_RADIO_CAL_INDEX = 3;
|
||||
static final int AO_USB_DESCRIPTORS_INDEX = 4;
|
||||
|
||||
private void add_cc_symbols() {
|
||||
for (int i = 0; i < cc_symbols.length; i++)
|
||||
symlist.add(cc_symbols[i]);
|
||||
}
|
||||
|
||||
public void add_symbol(AltosHexsym symbol) {
|
||||
symlist.add(symbol);
|
||||
}
|
||||
|
||||
/* Take symbols from another hexfile and duplicate them here */
|
||||
public void add_symbols(AltosHexfile other) {
|
||||
for (AltosHexsym symbol : other.symlist)
|
||||
symlist.add(symbol);
|
||||
}
|
||||
|
||||
public AltosHexsym lookup_symbol(String name) {
|
||||
if (symlist.isEmpty())
|
||||
add_cc_symbols();
|
||||
|
||||
for (AltosHexsym symbol : symlist)
|
||||
if (name.equals(symbol.name))
|
||||
return symbol;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final int look_around[] = { 0, -2, 2, -4, 4 };
|
||||
|
||||
private long find_usb_descriptors() {
|
||||
AltosHexsym usb_descriptors = lookup_symbol("ao_usb_descriptors");
|
||||
long a;
|
||||
|
||||
if (usb_descriptors == null)
|
||||
return -1;
|
||||
|
||||
/* The address of this has moved depending on padding
|
||||
* in the linker script and romconfig symbols. Look
|
||||
* forward and backwards two and four bytes to see if
|
||||
* we can find it
|
||||
*/
|
||||
a = usb_descriptors.address;
|
||||
|
||||
for (int look : look_around) {
|
||||
try {
|
||||
if (get_u8(a + look) == 0x12 && get_u8(a + look + 1) == AO_USB_DESC_DEVICE)
|
||||
return a;
|
||||
} catch (ArrayIndexOutOfBoundsException ae) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public AltosUsbId find_usb_id() {
|
||||
long a = find_usb_descriptors();
|
||||
|
||||
if (a == -1)
|
||||
return null;
|
||||
|
||||
/* Walk the descriptors looking for the device */
|
||||
while (get_u8(a+1) != AO_USB_DESC_DEVICE) {
|
||||
int delta = get_u8(a);
|
||||
a += delta;
|
||||
if (delta == 0 || a >= max_address)
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AltosUsbId(get_u16(a + 8),
|
||||
get_u16(a + 10));
|
||||
}
|
||||
|
||||
public String find_usb_product() {
|
||||
long a = find_usb_descriptors();
|
||||
int num_strings;
|
||||
int product_string;
|
||||
|
||||
if (a == -1)
|
||||
return null;
|
||||
|
||||
product_string = get_u8(a+15);
|
||||
|
||||
/* Walk the descriptors looking for the device */
|
||||
num_strings = 0;
|
||||
for (;;) {
|
||||
if (get_u8(a+1) == AO_USB_DESC_STRING) {
|
||||
++num_strings;
|
||||
if (num_strings == product_string + 1)
|
||||
break;
|
||||
}
|
||||
|
||||
int delta = get_u8(a);
|
||||
a += delta;
|
||||
if (delta == 0 || a >= max_address)
|
||||
return null;
|
||||
}
|
||||
|
||||
int product_len = get_u8(a);
|
||||
|
||||
if (product_len <= 0)
|
||||
return null;
|
||||
|
||||
String product = "";
|
||||
|
||||
for (int i = 0; i < product_len - 2; i += 2) {
|
||||
int c = get_u16(a + 2 + i);
|
||||
|
||||
product += Character.toString((char) c);
|
||||
}
|
||||
|
||||
if (AltosLink.debug)
|
||||
System.out.printf("product %s\n", product);
|
||||
|
||||
return product;
|
||||
}
|
||||
|
||||
private String make_string(byte[] data, int start, int length) {
|
||||
String s = "";
|
||||
for (int i = 0; i < length; i++)
|
||||
s += (char) data[start + i];
|
||||
return s;
|
||||
}
|
||||
|
||||
public AltosHexfile(byte[] bytes, long offset) {
|
||||
data = bytes;
|
||||
address = offset;
|
||||
max_address = address + bytes.length;
|
||||
}
|
||||
|
||||
public AltosHexfile(FileInputStream file) throws IOException {
|
||||
HexFileInputStream input = new HexFileInputStream(file);
|
||||
LinkedList<HexRecord> record_list = new LinkedList<HexRecord>();
|
||||
boolean done = false;
|
||||
|
||||
while (!done) {
|
||||
try {
|
||||
HexRecord record = new HexRecord(input);
|
||||
|
||||
record_list.add(record);
|
||||
} catch (EOFException eof) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
long extended_addr = 0;
|
||||
long base = 0;
|
||||
long bound = 0;
|
||||
boolean set = false;
|
||||
for (HexRecord record : record_list) {
|
||||
long addr;
|
||||
switch (record.type) {
|
||||
case 0:
|
||||
addr = extended_addr + record.address;
|
||||
long r_bound = addr + record.data.length;
|
||||
if (!set || addr < base)
|
||||
base = addr;
|
||||
if (!set || r_bound > bound)
|
||||
bound = r_bound;
|
||||
set = true;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (record.data.length != 2)
|
||||
throw new IOException("invalid extended segment address record");
|
||||
extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
|
||||
break;
|
||||
case 4:
|
||||
if (record.data.length != 2)
|
||||
throw new IOException("invalid extended segment address record");
|
||||
extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
|
||||
break;
|
||||
case 0xfe:
|
||||
String name = make_string(record.data, 0, record.data.length);
|
||||
addr = extended_addr + record.address;
|
||||
AltosHexsym s = new AltosHexsym(name, addr);
|
||||
symlist.add(s);
|
||||
break;
|
||||
default:
|
||||
throw new IOException ("invalid hex record type");
|
||||
}
|
||||
}
|
||||
|
||||
if (!set || base >= bound)
|
||||
throw new IOException("invalid hex file");
|
||||
|
||||
if (bound - base > 4 * 1024 * 1024)
|
||||
throw new IOException("hex file too large");
|
||||
|
||||
data = new byte[(int) (bound - base)];
|
||||
address = base;
|
||||
max_address = bound;
|
||||
Arrays.fill(data, (byte) 0xff);
|
||||
|
||||
/* Paint the records into the new array */
|
||||
for (HexRecord record : record_list) {
|
||||
switch (record.type) {
|
||||
case 0:
|
||||
long addr = extended_addr + record.address;
|
||||
long r_bound = addr + record.data.length;
|
||||
for (int j = 0; j < record.data.length; j++)
|
||||
data[(int) (addr - base) + j] = record.data[j];
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (record.data.length != 2)
|
||||
throw new IOException("invalid extended segment address record");
|
||||
extended_addr = ((record.data[0] << 8) + (record.data[1])) << 4;
|
||||
break;
|
||||
case 4:
|
||||
if (record.data.length != 2)
|
||||
throw new IOException("invalid extended segment address record");
|
||||
extended_addr = ((record.data[0] << 8) + (record.data[1])) << 16;
|
||||
break;
|
||||
case 0xfe:
|
||||
break;
|
||||
default:
|
||||
throw new IOException ("invalid hex record type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
altoslib/AltosHexsym.java
Normal file
35
altoslib/AltosHexsym.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosHexsym {
|
||||
String name;
|
||||
long address;
|
||||
|
||||
final static long invalid_addr = 0xffffffff;
|
||||
|
||||
public String toString() {
|
||||
return String.format("%s:0x%x", name, address);
|
||||
}
|
||||
|
||||
public AltosHexsym(String name, long address) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
466
altoslib/AltosIMU.java
Normal file
466
altoslib/AltosIMU.java
Normal file
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
|
||||
public class AltosIMU implements Cloneable {
|
||||
private int accel_x = AltosLib.MISSING;
|
||||
private int accel_y = AltosLib.MISSING;
|
||||
private int accel_z = AltosLib.MISSING;
|
||||
|
||||
private int accel_along = AltosLib.MISSING;
|
||||
private int accel_across = AltosLib.MISSING;
|
||||
private int accel_through = AltosLib.MISSING;
|
||||
|
||||
private int gyro_x = AltosLib.MISSING;
|
||||
private int gyro_y = AltosLib.MISSING;
|
||||
private int gyro_z = AltosLib.MISSING;
|
||||
|
||||
private int gyro_roll = AltosLib.MISSING;
|
||||
private int gyro_pitch = AltosLib.MISSING;
|
||||
private int gyro_yaw = AltosLib.MISSING;
|
||||
|
||||
private int mag_x = AltosLib.MISSING;
|
||||
private int mag_y = AltosLib.MISSING;
|
||||
private int mag_z = AltosLib.MISSING;
|
||||
|
||||
private int mag_along = AltosLib.MISSING;
|
||||
private int mag_across = AltosLib.MISSING;
|
||||
private int mag_through = AltosLib.MISSING;
|
||||
|
||||
private int imu_model = AltosLib.MISSING;
|
||||
private int mag_model = AltosLib.MISSING;
|
||||
|
||||
private static final double counts_per_g_mpu = 2048.0;
|
||||
private static final double counts_per_g_bmx = 2048.0;
|
||||
private static final double counts_per_g_adxl = 20.5;
|
||||
private static final double counts_per_g_bmi088 = 1365.0;
|
||||
|
||||
private static double counts_per_g(int imu_type, int imu_model) {
|
||||
switch (imu_model) {
|
||||
case AltosLib.model_mpu6000:
|
||||
case AltosLib.model_mpu9250:
|
||||
return counts_per_g_mpu;
|
||||
case AltosLib.model_adxl375:
|
||||
return counts_per_g_adxl;
|
||||
case AltosLib.model_bmx160:
|
||||
return counts_per_g_bmx;
|
||||
case AltosLib.model_bmi088:
|
||||
return counts_per_g_bmi088;
|
||||
}
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
case imu_type_easymega_v2:
|
||||
return counts_per_g_mpu;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return counts_per_g_bmx;
|
||||
case imu_type_easymotor_v2:
|
||||
return counts_per_g_adxl;
|
||||
case imu_type_easytimer_v2:
|
||||
return counts_per_g_bmi088;
|
||||
}
|
||||
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public static double convert_accel(double counts, int imu_type, int imu_model) {
|
||||
double cpg = counts_per_g(imu_type, imu_model);
|
||||
if (cpg == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return counts / cpg * AltosConvert.gravity;
|
||||
}
|
||||
|
||||
private static final double GYRO_FULLSCALE_DEGREES_MPU = 2000.0;
|
||||
private static final double GYRO_COUNTS_MPU = 32767.0;
|
||||
private static final double counts_per_degree_mpu = GYRO_COUNTS_MPU / GYRO_FULLSCALE_DEGREES_MPU;
|
||||
private static final double GYRO_FULLSCALE_DEGREES_BMX = 2000.0;
|
||||
private static final double GYRO_COUNTS_BMX = 32767.0;
|
||||
private static final double counts_per_degree_bmx = GYRO_COUNTS_BMX / GYRO_FULLSCALE_DEGREES_BMX;
|
||||
private static final double counts_per_degree_bmi088 = 16.384;
|
||||
|
||||
private static double counts_per_degree(int imu_type, int imu_model) {
|
||||
switch (imu_model) {
|
||||
case AltosLib.model_mpu6000:
|
||||
case AltosLib.model_mpu9250:
|
||||
return counts_per_degree_mpu;
|
||||
case AltosLib.model_bmx160:
|
||||
return counts_per_degree_bmx;
|
||||
case AltosLib.model_bmi088:
|
||||
return counts_per_degree_bmi088;
|
||||
}
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
case imu_type_easymega_v2:
|
||||
return counts_per_degree_mpu;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return counts_per_degree_bmx;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
public static double gyro_degrees_per_second(double counts, int imu_type, int imu_model) {
|
||||
double cpd = counts_per_degree(imu_type, imu_model);
|
||||
|
||||
if (cpd == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
return counts / cpd;
|
||||
}
|
||||
|
||||
private static final double MAG_FULLSCALE_GAUSS_MPU = 48.00; /* 4800µT */
|
||||
private static final double MAG_COUNTS_MPU = 32767.0;
|
||||
private static final double counts_per_gauss_mpu = MAG_COUNTS_MPU / MAG_FULLSCALE_GAUSS_MPU;
|
||||
|
||||
private static final double counts_per_gauss_bmx = 100.0; /* BMX driver converts to µT */
|
||||
|
||||
public static double counts_per_gauss(int imu_type, int imu_model) {
|
||||
switch (imu_model) {
|
||||
case AltosLib.model_mpu9250:
|
||||
return counts_per_gauss_mpu;
|
||||
case AltosLib.model_bmx160:
|
||||
return counts_per_gauss_bmx;
|
||||
}
|
||||
|
||||
switch(imu_type) {
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v2:
|
||||
return counts_per_gauss_mpu;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return counts_per_gauss_bmx;
|
||||
}
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
|
||||
private boolean parse_string(String line) {
|
||||
if (line.startsWith("Accel:")) {
|
||||
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
if (items.length >= 8) {
|
||||
accel_x = Integer.parseInt(items[1]);
|
||||
accel_y = Integer.parseInt(items[2]);
|
||||
accel_z = Integer.parseInt(items[3]);
|
||||
gyro_x = Integer.parseInt(items[5]);
|
||||
gyro_y = Integer.parseInt(items[6]);
|
||||
gyro_z = Integer.parseInt(items[7]);
|
||||
}
|
||||
if (items.length >= 12) {
|
||||
mag_x = Integer.parseInt(items[9]);
|
||||
mag_y = Integer.parseInt(items[10]);
|
||||
mag_z = Integer.parseInt(items[11]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (line.startsWith("MPU6000:")) {
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
imu_model = AltosLib.model_mpu6000;
|
||||
|
||||
if (items.length >= 7) {
|
||||
accel_along = Integer.parseInt(items[1]);
|
||||
accel_across = Integer.parseInt(items[2]);
|
||||
accel_through = Integer.parseInt(items[3]);
|
||||
gyro_roll = Integer.parseInt(items[4]);
|
||||
gyro_pitch = Integer.parseInt(items[5]);
|
||||
gyro_yaw = Integer.parseInt(items[6]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (line.startsWith("BMI088:")) {
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
imu_model = AltosLib.model_bmi088;
|
||||
|
||||
if (items.length >= 7) {
|
||||
accel_along = Integer.parseInt(items[1]);
|
||||
accel_across = Integer.parseInt(items[2]);
|
||||
accel_through = Integer.parseInt(items[3]);
|
||||
gyro_roll = Integer.parseInt(items[4]);
|
||||
gyro_pitch = Integer.parseInt(items[5]);
|
||||
gyro_yaw = Integer.parseInt(items[6]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public AltosIMU clone() {
|
||||
AltosIMU n = new AltosIMU();
|
||||
|
||||
n.accel_x = accel_x;
|
||||
n.accel_y = accel_y;
|
||||
n.accel_z = accel_z;
|
||||
|
||||
n.accel_along = accel_along;
|
||||
n.accel_across = accel_across;
|
||||
n.accel_through = accel_through;
|
||||
|
||||
n.gyro_x = gyro_x;
|
||||
n.gyro_y = gyro_y;
|
||||
n.gyro_z = gyro_z;
|
||||
|
||||
n.gyro_roll = gyro_roll;
|
||||
n.gyro_pitch = gyro_pitch;
|
||||
n.gyro_yaw = gyro_yaw;
|
||||
|
||||
n.mag_x = mag_x;
|
||||
n.mag_y = mag_y;
|
||||
n.mag_z = mag_z;
|
||||
|
||||
n.mag_along = mag_along;
|
||||
n.mag_across = mag_across;
|
||||
n.mag_through = mag_through;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public static final int imu_type_telemega_v1_v2 = 0; /* MPU6000 */
|
||||
public static final int imu_type_telemega_v3 = 1; /* MPU9250 */
|
||||
public static final int imu_type_telemega_v4 = 2; /* BMX160 */
|
||||
|
||||
public static final int imu_type_easymega_v1 = 3; /* MPU6000 */
|
||||
public static final int imu_type_easymega_v2 = 4; /* MPU9250 */
|
||||
|
||||
public static final int imu_type_easytimer_v1 = 5; /* BMX160 */
|
||||
|
||||
public static final int imu_type_easymotor_v2 = 6; /* ADXL375 (accel only) */
|
||||
|
||||
public static final int imu_type_easytimer_v2 = 7; /* BMI088 */
|
||||
|
||||
private int accel_across(int imu_type) {
|
||||
|
||||
if (accel_across != AltosLib.MISSING)
|
||||
return accel_across;
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return accel_x;
|
||||
case imu_type_easymega_v2:
|
||||
return -accel_y;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return -accel_y;
|
||||
case imu_type_easymotor_v2:
|
||||
return accel_y;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int accel_along(int imu_type) {
|
||||
if (accel_along != AltosLib.MISSING) {
|
||||
System.out.printf("accel along %d\n", accel_along);
|
||||
return accel_along;
|
||||
}
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return accel_y;
|
||||
case imu_type_easymega_v2:
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return accel_x;
|
||||
case imu_type_easymotor_v2:
|
||||
return -accel_x;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int accel_through(int imu_type) {
|
||||
if (accel_through != AltosLib.MISSING)
|
||||
return accel_through;
|
||||
|
||||
return accel_z;
|
||||
}
|
||||
|
||||
private int gyro_roll(int imu_type) {
|
||||
if (gyro_roll != AltosLib.MISSING)
|
||||
return gyro_roll;
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return gyro_y;
|
||||
case imu_type_easymega_v2:
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return gyro_x;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int gyro_pitch(int imu_type) {
|
||||
if (gyro_pitch != AltosLib.MISSING)
|
||||
return gyro_pitch;
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return gyro_x;
|
||||
case imu_type_easymega_v2:
|
||||
return -gyro_y;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return -gyro_y;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int gyro_yaw(int imu_type) {
|
||||
if (gyro_yaw != AltosLib.MISSING)
|
||||
return gyro_yaw;
|
||||
|
||||
return gyro_z;
|
||||
}
|
||||
|
||||
private int mag_across(int imu_type) {
|
||||
if (mag_across != AltosLib.MISSING)
|
||||
return mag_across;
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return mag_x;
|
||||
case imu_type_easymega_v2:
|
||||
return -mag_y;
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return mag_y;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int mag_along(int imu_type) {
|
||||
if (mag_along != AltosLib.MISSING)
|
||||
return mag_along;
|
||||
|
||||
switch (imu_type) {
|
||||
case imu_type_telemega_v1_v2:
|
||||
case imu_type_telemega_v3:
|
||||
case imu_type_easymega_v1:
|
||||
return mag_y;
|
||||
case imu_type_easymega_v2:
|
||||
case imu_type_telemega_v4:
|
||||
case imu_type_easytimer_v1:
|
||||
return mag_x;
|
||||
default:
|
||||
return AltosLib.MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
private int mag_through(int imu_type) {
|
||||
if (mag_through != AltosLib.MISSING)
|
||||
return mag_through;
|
||||
|
||||
return mag_z;
|
||||
}
|
||||
|
||||
private static boolean is_primary_accel(int imu_type) {
|
||||
switch (imu_type) {
|
||||
case imu_type_easytimer_v1:
|
||||
case imu_type_easytimer_v2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link, int imu_type) throws InterruptedException {
|
||||
try {
|
||||
AltosIMU imu = new AltosIMU(link);
|
||||
AltosCalData cal_data = listener.cal_data();
|
||||
|
||||
System.out.printf("imu_model %d mag_model %d\n", imu.imu_model, imu.mag_model);
|
||||
if (imu_type != AltosLib.MISSING)
|
||||
cal_data.set_imu_type(imu_type);
|
||||
if (imu != null) {
|
||||
if (imu.imu_model != AltosLib.MISSING)
|
||||
cal_data.set_imu_model(imu.imu_model);
|
||||
if (imu.mag_model != AltosLib.MISSING)
|
||||
cal_data.set_mag_model(imu.mag_model);
|
||||
|
||||
if (imu.gyro_roll(imu_type) != AltosLib.MISSING) {
|
||||
cal_data.set_gyro_zero(0, 0, 0);
|
||||
listener.set_gyro(cal_data.gyro_roll(imu.gyro_roll(imu_type)),
|
||||
cal_data.gyro_pitch(imu.gyro_pitch(imu_type)),
|
||||
cal_data.gyro_yaw(imu.gyro_yaw(imu_type)));
|
||||
}
|
||||
listener.set_accel_ground(cal_data.accel_along(imu.accel_along(imu_type)),
|
||||
cal_data.accel_across(imu.accel_across(imu_type)),
|
||||
cal_data.accel_through(imu.accel_through(imu_type)));
|
||||
listener.set_accel(cal_data.accel_along(imu.accel_along(imu_type)),
|
||||
cal_data.accel_across(imu.accel_across(imu_type)),
|
||||
cal_data.accel_through(imu.accel_through(imu_type)));
|
||||
if (is_primary_accel(imu_type)) {
|
||||
int accel = imu.accel_along(imu_type);
|
||||
if (!cal_data.adxl375_inverted)
|
||||
accel = -accel;
|
||||
if (cal_data.pad_orientation == 1)
|
||||
accel = -accel;
|
||||
listener.set_acceleration(cal_data.acceleration(accel));
|
||||
}
|
||||
if (imu.mag_along(imu_type) != AltosLib.MISSING) {
|
||||
listener.set_mag(cal_data.mag_along(imu.mag_along(imu_type)),
|
||||
cal_data.mag_across(imu.mag_across(imu_type)),
|
||||
cal_data.mag_through(imu.mag_through(imu_type)));
|
||||
}
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosIMU() {
|
||||
}
|
||||
|
||||
public AltosIMU(AltosLink link) throws InterruptedException, TimeoutException {
|
||||
this();
|
||||
link.printf("I\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (parse_string(line))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
40
altoslib/AltosIdle.java
Normal file
40
altoslib/AltosIdle.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AltosIdle {
|
||||
AltosLink link;
|
||||
AltosConfigData config_data;
|
||||
|
||||
public void printf(String format, Object ... arguments) {
|
||||
link.printf(format, arguments);
|
||||
}
|
||||
|
||||
public abstract void update_state(AltosState state) throws InterruptedException, TimeoutException;
|
||||
|
||||
public AltosIdle(AltosLink link, AltosConfigData config_data) {
|
||||
this.link = link;
|
||||
this.config_data = config_data;
|
||||
}
|
||||
}
|
||||
314
altoslib/AltosIdleFetch.java
Normal file
314
altoslib/AltosIdleFetch.java
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
class AltosIdler {
|
||||
String prefix;
|
||||
int[] idlers;
|
||||
|
||||
static final int idle_gps = 0;
|
||||
static final int idle_imu_tm_v1_v2 = 1;
|
||||
static final int idle_imu_tm_v3 = 2;
|
||||
static final int idle_imu_tm_v4 = 3;
|
||||
static final int idle_imu_em_v1 = 4;
|
||||
static final int idle_imu_em_v2 = 5;
|
||||
static final int idle_imu_et_v1 = 6;
|
||||
static final int idle_mag = 7;
|
||||
static final int idle_mma655x = 8;
|
||||
static final int idle_ms5607 = 9;
|
||||
static final int idle_adxl375 = 10;
|
||||
static final int idle_adxl375_easymotor_v2 = 11;
|
||||
static final int idle_imu = 12;
|
||||
static final int idle_imu_et_v2 = 13;
|
||||
static final int idle_imu_em_v3 = 14;
|
||||
|
||||
static final int idle_sensor_tm = 100;
|
||||
static final int idle_sensor_metrum = 101;
|
||||
static final int idle_sensor_mega = 102;
|
||||
static final int idle_sensor_emini1 = 103;
|
||||
static final int idle_sensor_emini2 = 104;
|
||||
static final int idle_sensor_tmini2 = 105;
|
||||
static final int idle_sensor_tgps1 = 106;
|
||||
static final int idle_sensor_tgps2 = 107;
|
||||
static final int idle_sensor_tgps3 = 108;
|
||||
static final int idle_sensor_tmini3 = 109;
|
||||
static final int idle_sensor_easytimer1 = 110;
|
||||
static final int idle_sensor_easymotor2 = 111;
|
||||
static final int idle_sensor_emini3 = 112;
|
||||
static final int idle_sensor_etimer2 = 113;
|
||||
static final int idle_sensor_emega3 = 114;
|
||||
|
||||
public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, TimeoutException, AltosUnknownProduct {
|
||||
for (int idler : idlers) {
|
||||
switch (idler) {
|
||||
case idle_gps:
|
||||
AltosGPS.provide_data(listener, link);
|
||||
break;
|
||||
case idle_imu_tm_v1_v2:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_telemega_v1_v2);
|
||||
break;
|
||||
case idle_imu_tm_v3:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_telemega_v3);
|
||||
break;
|
||||
case idle_imu_tm_v4:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_telemega_v4);
|
||||
break;
|
||||
case idle_imu_em_v1:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easymega_v1);
|
||||
break;
|
||||
case idle_imu_em_v2:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easymega_v2);
|
||||
break;
|
||||
case idle_imu_et_v1:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v1);
|
||||
break;
|
||||
case idle_imu_et_v2:
|
||||
AltosIMU.provide_data(listener, link, AltosIMU.imu_type_easytimer_v2);
|
||||
break;
|
||||
case idle_imu:
|
||||
AltosIMU.provide_data(listener, link, AltosLib.MISSING);
|
||||
break;
|
||||
case idle_mag:
|
||||
AltosMag.provide_data(listener, link);
|
||||
break;
|
||||
case idle_mma655x:
|
||||
AltosMma655x.provide_data(listener, link);
|
||||
break;
|
||||
case idle_adxl375:
|
||||
AltosAdxl375.provide_data(listener, link, false, AltosLib.MISSING);
|
||||
break;
|
||||
case idle_adxl375_easymotor_v2:
|
||||
AltosAdxl375.provide_data(listener, link, true, AltosIMU.imu_type_easymotor_v2);
|
||||
break;
|
||||
case idle_ms5607:
|
||||
AltosMs5607.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_tm:
|
||||
AltosSensorTM.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_metrum:
|
||||
AltosSensorMetrum.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_mega:
|
||||
AltosSensorMega.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_emini1:
|
||||
AltosSensorEMini.provide_data(listener, link, 1);
|
||||
break;
|
||||
case idle_sensor_emini2:
|
||||
AltosSensorEMini.provide_data(listener, link, 2);
|
||||
break;
|
||||
case idle_sensor_emini3:
|
||||
AltosSensorEMini.provide_data(listener, link, 3);
|
||||
break;
|
||||
case idle_sensor_tmini2:
|
||||
AltosSensorTMini2.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_tgps1:
|
||||
AltosSensorTGPS1.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_tgps2:
|
||||
AltosSensorTGPS2.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_tgps3:
|
||||
AltosSensorTGPS3.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_tmini3:
|
||||
AltosSensorTMini3.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_easytimer1:
|
||||
AltosSensorEasyTimer1.provide_data(listener, link);
|
||||
break;
|
||||
case idle_sensor_easymotor2:
|
||||
AltosSensorEasyMotor2.provide_data(listener, link);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches(AltosConfigData config_data) {
|
||||
return config_data.product.startsWith(prefix);
|
||||
}
|
||||
|
||||
public AltosIdler(String prefix, int ... idlers) {
|
||||
this.prefix = prefix;
|
||||
this.idlers = idlers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class AltosIdleFetch implements AltosDataProvider {
|
||||
|
||||
static final AltosIdler[] idlers = {
|
||||
|
||||
new AltosIdler("EasyMini-v1",
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_emini1),
|
||||
|
||||
new AltosIdler("EasyMini-v2",
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_emini2),
|
||||
|
||||
new AltosIdler("EasyMini-v3",
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_emini3),
|
||||
|
||||
new AltosIdler("TeleMini-v1",
|
||||
AltosIdler.idle_sensor_tm),
|
||||
|
||||
new AltosIdler("TeleMini-v2",
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_tmini2),
|
||||
|
||||
new AltosIdler("TeleMini-v3",
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_tmini3),
|
||||
|
||||
new AltosIdler("TeleMetrum-v1",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_sensor_tm),
|
||||
|
||||
new AltosIdler("TeleMetrum-v2",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_metrum),
|
||||
|
||||
new AltosIdler("TeleMetrum-v3",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_metrum),
|
||||
|
||||
new AltosIdler("TeleMetrum-v4",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_sensor_metrum),
|
||||
|
||||
new AltosIdler("TeleMega-v0",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_tm_v1_v2, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v1",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_tm_v1_v2, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v2",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_tm_v1_v2, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v3",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_tm_v3,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v4",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_tm_v4,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v5",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleMega-v6",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("EasyMega-v1",
|
||||
AltosIdler.idle_mma655x,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_em_v1, AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("EasyMega-v2",
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu_em_v2,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("EasyMega-v3",
|
||||
AltosIdler.idle_adxl375,
|
||||
AltosIdler.idle_ms5607,
|
||||
AltosIdler.idle_imu,
|
||||
AltosIdler.idle_mag,
|
||||
AltosIdler.idle_sensor_mega),
|
||||
new AltosIdler("TeleGPS-v1",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_sensor_tgps1),
|
||||
new AltosIdler("TeleGPS-v2",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_sensor_tgps2),
|
||||
new AltosIdler("TeleGPS-v3",
|
||||
AltosIdler.idle_gps,
|
||||
AltosIdler.idle_sensor_tgps3),
|
||||
new AltosIdler("EasyTimer-v1",
|
||||
AltosIdler.idle_imu_et_v1,
|
||||
AltosIdler.idle_sensor_easytimer1),
|
||||
new AltosIdler("EasyMotor-v2",
|
||||
AltosIdler.idle_adxl375_easymotor_v2,
|
||||
AltosIdler.idle_sensor_easymotor2),
|
||||
new AltosIdler("EasyTimer-v2",
|
||||
AltosIdler.idle_imu_et_v2,
|
||||
AltosIdler.idle_sensor_easymotor2),
|
||||
};
|
||||
|
||||
AltosLink link;
|
||||
|
||||
public void provide_data(AltosDataListener listener) throws InterruptedException, AltosUnknownProduct {
|
||||
try {
|
||||
boolean matched = false;
|
||||
/* Fetch config data from remote */
|
||||
AltosConfigData config_data = link.config_data();
|
||||
listener.set_state(AltosLib.ao_flight_stateless);
|
||||
for (AltosIdler idler : idlers) {
|
||||
if (idler.matches(config_data)) {
|
||||
idler.provide_data(listener, link);
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
throw new AltosUnknownProduct(config_data.product);
|
||||
listener.set_received_time(System.currentTimeMillis());
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AltosIdleFetch(AltosLink link) {
|
||||
this.link = link;
|
||||
}
|
||||
}
|
||||
140
altoslib/AltosIdleMonitor.java
Normal file
140
altoslib/AltosIdleMonitor.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
|
||||
public class AltosIdleMonitor extends Thread {
|
||||
AltosLink link;
|
||||
AltosIdleMonitorListener listener;
|
||||
|
||||
AltosIdleFetch fetch;
|
||||
|
||||
boolean remote;
|
||||
boolean close_on_exit;
|
||||
double frequency = AltosLib.MISSING;
|
||||
String callsign;
|
||||
|
||||
AltosState state;
|
||||
AltosListenerState listener_state;
|
||||
AltosConfigData config_data;
|
||||
AltosGPS gps;
|
||||
|
||||
void start_link() throws InterruptedException, TimeoutException {
|
||||
if (remote) {
|
||||
link.set_radio_frequency(frequency);
|
||||
link.set_callsign(callsign);
|
||||
link.start_remote();
|
||||
} else
|
||||
link.flush_input();
|
||||
}
|
||||
|
||||
boolean stop_link() throws InterruptedException, TimeoutException {
|
||||
if (remote)
|
||||
link.stop_remote();
|
||||
return link.reply_abort;
|
||||
}
|
||||
|
||||
boolean provide_data() throws InterruptedException, TimeoutException, AltosUnknownProduct {
|
||||
boolean worked = false;
|
||||
boolean aborted = false;
|
||||
|
||||
try {
|
||||
start_link();
|
||||
link.config_data();
|
||||
if (state == null)
|
||||
state = new AltosState(new AltosCalData(link.config_data()));
|
||||
fetch.provide_data(state);
|
||||
if (frequency != AltosLib.MISSING)
|
||||
state.set_frequency(frequency);
|
||||
if (!link.has_error && !link.reply_abort)
|
||||
worked = true;
|
||||
} finally {
|
||||
aborted = stop_link();
|
||||
if (worked) {
|
||||
if (remote)
|
||||
state.set_rssi(link.rssi(), 0);
|
||||
listener_state.battery = link.monitor_battery();
|
||||
}
|
||||
}
|
||||
return aborted;
|
||||
}
|
||||
|
||||
public void set_frequency(double in_frequency) {
|
||||
frequency = in_frequency;
|
||||
link.abort_reply();
|
||||
}
|
||||
|
||||
public void set_callsign(String in_callsign) {
|
||||
callsign = in_callsign;
|
||||
link.abort_reply();
|
||||
}
|
||||
|
||||
public void abort() throws InterruptedException {
|
||||
while (isAlive()) {
|
||||
interrupt();
|
||||
link.abort_reply();
|
||||
Thread.sleep(100);
|
||||
}
|
||||
join();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
state = null;
|
||||
try {
|
||||
for (;;) {
|
||||
try {
|
||||
provide_data();
|
||||
listener.update(state, listener_state);
|
||||
} catch (TimeoutException te) {
|
||||
} catch (AltosUnknownProduct ae) {
|
||||
listener.error(String.format("Unknown product \"%s\"", ae.product));
|
||||
}
|
||||
if (link.has_error || link.reply_abort) {
|
||||
listener.failed();
|
||||
break;
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
if (close_on_exit) {
|
||||
try {
|
||||
link.close();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote, boolean in_close_on_exit) {
|
||||
listener = in_listener;
|
||||
link = in_link;
|
||||
remote = in_remote;
|
||||
close_on_exit = in_close_on_exit;
|
||||
listener_state = new AltosListenerState();
|
||||
fetch = new AltosIdleFetch(link);
|
||||
}
|
||||
|
||||
public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote) {
|
||||
this(in_listener, in_link, in_remote, true);
|
||||
}
|
||||
}
|
||||
|
||||
25
altoslib/AltosIdleMonitorListener.java
Normal file
25
altoslib/AltosIdleMonitorListener.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosIdleMonitorListener {
|
||||
public void update(AltosState state, AltosListenerState listener_state);
|
||||
public void error(String reason);
|
||||
public void failed();
|
||||
}
|
||||
133
altoslib/AltosIdleReader.java
Normal file
133
altoslib/AltosIdleReader.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright © 2016 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 java.text.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosIdleReader extends AltosFlightReader {
|
||||
AltosLink link;
|
||||
boolean remote;
|
||||
AltosCalData cal_data = null;
|
||||
AltosState state = null;
|
||||
AltosIdleFetch fetch;
|
||||
long next_millis;
|
||||
static final long report_interval = 5 * 1000;
|
||||
static final long minimum_delay = 1 * 1000;
|
||||
|
||||
private void start_link() throws InterruptedException, TimeoutException {
|
||||
if (remote) {
|
||||
link.start_remote();
|
||||
} else
|
||||
link.flush_input();
|
||||
}
|
||||
|
||||
private boolean stop_link() throws InterruptedException, TimeoutException {
|
||||
if (remote)
|
||||
link.stop_remote();
|
||||
return link.reply_abort;
|
||||
}
|
||||
|
||||
public AltosCalData cal_data() {
|
||||
if (cal_data == null) {
|
||||
try {
|
||||
cal_data = new AltosCalData(link.config_data());
|
||||
} catch (InterruptedException ie) {
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
if (cal_data == null)
|
||||
cal_data = new AltosCalData();
|
||||
}
|
||||
return cal_data;
|
||||
}
|
||||
|
||||
public AltosState read() throws InterruptedException, ParseException, AltosCRCException, IOException {
|
||||
boolean worked = false;
|
||||
boolean aborted = false;
|
||||
|
||||
long delay = next_millis - System.currentTimeMillis();
|
||||
|
||||
if (delay > 0)
|
||||
Thread.sleep(delay);
|
||||
next_millis = System.currentTimeMillis() + report_interval;
|
||||
try {
|
||||
try {
|
||||
start_link();
|
||||
if (state == null)
|
||||
state = new AltosState(cal_data());
|
||||
fetch.provide_data(state);
|
||||
if (!link.has_error && !link.reply_abort)
|
||||
worked = true;
|
||||
} catch (TimeoutException te) {
|
||||
} catch (AltosUnknownProduct ue) {
|
||||
worked = true;
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
aborted = stop_link();
|
||||
} catch (TimeoutException te) {
|
||||
aborted = true;
|
||||
}
|
||||
if (worked) {
|
||||
if (remote) {
|
||||
try {
|
||||
state.set_rssi(link.rssi(), 0);
|
||||
} catch (TimeoutException te) {
|
||||
state.set_rssi(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long finish = System.currentTimeMillis();
|
||||
|
||||
if (next_millis - finish < minimum_delay)
|
||||
next_millis = finish + minimum_delay;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public void close(boolean interrupted) {
|
||||
try {
|
||||
link.close();
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public void set_frequency(double frequency) throws InterruptedException, TimeoutException {
|
||||
link.set_radio_frequency(frequency);
|
||||
}
|
||||
|
||||
public void save_frequency() {
|
||||
AltosPreferences.set_frequency(link.serial, link.frequency);
|
||||
}
|
||||
|
||||
public void set_callsign(String callsign) throws InterruptedException, TimeoutException {
|
||||
link.set_callsign(callsign);
|
||||
}
|
||||
|
||||
public AltosIdleReader (AltosLink link, boolean remote)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
this.link = link;
|
||||
this.remote = remote;
|
||||
this.next_millis = System.currentTimeMillis();
|
||||
fetch = new AltosIdleFetch(link);
|
||||
}
|
||||
}
|
||||
213
altoslib/AltosIgnite.java
Normal file
213
altoslib/AltosIgnite.java
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosIgnite {
|
||||
AltosLink link;
|
||||
boolean remote;
|
||||
boolean close_on_exit;
|
||||
boolean link_started;
|
||||
boolean has_pyro_info = false;
|
||||
boolean has_standard = false;
|
||||
int npyro;
|
||||
AltosConfigData config_data;
|
||||
|
||||
public final static String None = null;
|
||||
public final static String Apogee = "drogue";
|
||||
public final static String Main = "main";
|
||||
|
||||
public final static int Unknown = 0;
|
||||
public final static int Ready = 1;
|
||||
public final static int Active = 2;
|
||||
public final static int Open = 3;
|
||||
|
||||
private void start_link() throws InterruptedException, TimeoutException {
|
||||
link_started = true;
|
||||
if (remote)
|
||||
link.start_remote();
|
||||
}
|
||||
|
||||
private void stop_link() throws InterruptedException {
|
||||
if (!link_started)
|
||||
return;
|
||||
link_started = false;
|
||||
if (link == null)
|
||||
return;
|
||||
if (remote)
|
||||
link.stop_remote();
|
||||
}
|
||||
|
||||
class string_ref {
|
||||
String value;
|
||||
|
||||
public String get() {
|
||||
return value;
|
||||
}
|
||||
public void set(String i) {
|
||||
value = i;
|
||||
}
|
||||
public string_ref() {
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private boolean get_string(String line, String label, string_ref s) {
|
||||
if (line.startsWith(label)) {
|
||||
String quoted = line.substring(label.length()).trim();
|
||||
|
||||
if (quoted.startsWith("\""))
|
||||
quoted = quoted.substring(1);
|
||||
if (quoted.endsWith("\""))
|
||||
quoted = quoted.substring(0,quoted.length()-1);
|
||||
s.set(quoted);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private int map_status(String status_name) {
|
||||
if (status_name.equals("unknown"))
|
||||
return Unknown;
|
||||
if (status_name.equals("ready"))
|
||||
return Ready;
|
||||
if (status_name.equals("active"))
|
||||
return Active;
|
||||
if (status_name.equals("open"))
|
||||
return Open;
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
private void get_npyro() throws InterruptedException, TimeoutException {
|
||||
if (config_data == null)
|
||||
config_data = new AltosConfigData(link);
|
||||
if (config_data != null && config_data.npyro != AltosLib.MISSING)
|
||||
npyro = config_data.npyro;
|
||||
else
|
||||
npyro = 0;
|
||||
if (config_data != null)
|
||||
has_standard = config_data.ignite_mode != AltosLib.MISSING;
|
||||
|
||||
has_pyro_info = true;
|
||||
}
|
||||
|
||||
public int npyro() throws InterruptedException, TimeoutException {
|
||||
if (!has_pyro_info) {
|
||||
start_link();
|
||||
get_npyro();
|
||||
stop_link();
|
||||
}
|
||||
return npyro;
|
||||
}
|
||||
|
||||
public boolean has_standard() throws InterruptedException, TimeoutException {
|
||||
if (!has_pyro_info) {
|
||||
start_link();
|
||||
get_npyro();
|
||||
stop_link();
|
||||
}
|
||||
return has_standard;
|
||||
}
|
||||
|
||||
public HashMap<String,Integer> status() throws InterruptedException, TimeoutException {
|
||||
HashMap<String,Integer> status = new HashMap<String,Integer>();
|
||||
|
||||
if (link == null)
|
||||
return status;
|
||||
try {
|
||||
start_link();
|
||||
get_npyro();
|
||||
|
||||
String last_igniter = Main;
|
||||
if (npyro > 0)
|
||||
last_igniter = String.format("%d", npyro - 1);
|
||||
|
||||
link.printf("t\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
if (items.length < 4)
|
||||
continue;
|
||||
|
||||
if (!items[0].equals("Igniter:"))
|
||||
continue;
|
||||
|
||||
if (!items[2].equals("Status:"))
|
||||
continue;
|
||||
|
||||
status.put(items[1], map_status(items[3]));
|
||||
|
||||
if (items[1].equals(last_igniter))
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
stop_link();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public static String status_string(int status) {
|
||||
switch (status) {
|
||||
case Unknown: return "Unknown";
|
||||
case Ready: return "Ready";
|
||||
case Active: return "Active";
|
||||
case Open: return "Open";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public void fire(String igniter) throws InterruptedException {
|
||||
if (link == null)
|
||||
return;
|
||||
try {
|
||||
start_link();
|
||||
link.printf("i DoIt %s\n", igniter);
|
||||
link.flush_output();
|
||||
} catch (TimeoutException te) {
|
||||
} finally {
|
||||
stop_link();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
stop_link();
|
||||
if (close_on_exit)
|
||||
link.close();
|
||||
link = null;
|
||||
}
|
||||
|
||||
public AltosIgnite(AltosLink in_link, boolean in_remote, boolean in_close_on_exit) {
|
||||
link = in_link;
|
||||
remote = in_remote;
|
||||
close_on_exit = in_close_on_exit;
|
||||
}
|
||||
|
||||
public AltosIgnite(AltosLink in_link, boolean in_remote) {
|
||||
this(in_link, in_remote, true);
|
||||
}
|
||||
}
|
||||
26
altoslib/AltosImage.java
Normal file
26
altoslib/AltosImage.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
|
||||
public interface AltosImage {
|
||||
/* Discard storage for image */
|
||||
public abstract void flush();
|
||||
}
|
||||
1367
altoslib/AltosJson.java
Normal file
1367
altoslib/AltosJson.java
Normal file
File diff suppressed because it is too large
Load Diff
354
altoslib/AltosKML.java
Normal file
354
altoslib/AltosKML.java
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
class KMLWriter extends PrintWriter {
|
||||
public PrintWriter printf(String format, Object ... arguments) {
|
||||
return printf(Locale.ROOT, format, arguments);
|
||||
}
|
||||
|
||||
public KMLWriter(File name) throws FileNotFoundException {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
public class AltosKML implements AltosWriter {
|
||||
|
||||
File name;
|
||||
PrintWriter out;
|
||||
int flight_state = -1;
|
||||
AltosGPS prev = null;
|
||||
double gps_start_altitude = AltosLib.MISSING;
|
||||
AltosFlightSeries series;
|
||||
AltosFlightStats stats;
|
||||
AltosCalData cal_data;
|
||||
|
||||
static final String[] kml_state_colors = {
|
||||
"FF000000", // startup
|
||||
"FF000000", // idle
|
||||
"FF000000", // pad
|
||||
"FF0000FF", // boost
|
||||
"FF8040FF", // coast
|
||||
"FF4080FF", // fast
|
||||
"FF00FFFF", // drogue
|
||||
"FF00FF00", // main
|
||||
"FF000000", // landed
|
||||
"FFFFFFFF", // invalid
|
||||
"FFFF0000", // stateless
|
||||
};
|
||||
|
||||
static String state_color(int state) {
|
||||
if (state < 0 || kml_state_colors.length <= state)
|
||||
return kml_state_colors[AltosLib.ao_flight_invalid];
|
||||
return kml_state_colors[state];
|
||||
}
|
||||
|
||||
static final String[] kml_style_colors = {
|
||||
"FF0000FF", // baro
|
||||
"FFFF0000", // gps
|
||||
};
|
||||
|
||||
static String style_color(int style) {
|
||||
if (style < 0 || kml_style_colors.length <= style)
|
||||
return kml_style_colors[0];
|
||||
return kml_style_colors[style];
|
||||
}
|
||||
|
||||
static final String kml_header_start =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
|
||||
"<Document>\n" +
|
||||
" <name>AO Flight#%d S/N: %03d</name>\n" +
|
||||
" <Snippet maxLines=\"8\">\n";
|
||||
|
||||
static final String kml_header_end =
|
||||
" </Snippet>\n" +
|
||||
" <open>1</open>\n";
|
||||
|
||||
static final String kml_folder_start =
|
||||
" <Folder>\n" +
|
||||
" <name>%s</name>\n";
|
||||
|
||||
static final String kml_path_style_start =
|
||||
" <Style id=\"ao-style-%s\">\n" +
|
||||
" <LineStyle><color>%s</color><width>8</width></LineStyle>\n" +
|
||||
" <BalloonStyle>\n" +
|
||||
" <text>\n";
|
||||
|
||||
static final String kml_path_style_end =
|
||||
" </text>\n" +
|
||||
" </BalloonStyle>\n" +
|
||||
" </Style>\n";
|
||||
|
||||
static final String kml_point_style_start =
|
||||
" <Style id=\"ao-style-%s\">\n" +
|
||||
" <LabelStyle><color>%s</color></LabelStyle>\n" +
|
||||
" <IconStyle><color>%s</color></IconStyle>\n" +
|
||||
" <BalloonStyle>\n" +
|
||||
" <text>\n";
|
||||
|
||||
static final String kml_point_style_end =
|
||||
" </text>\n" +
|
||||
" </BalloonStyle>\n" +
|
||||
" </Style>\n";
|
||||
|
||||
static final String kml_path_start =
|
||||
" <Placemark>\n" +
|
||||
" <name>%s</name>\n" +
|
||||
" <styleUrl>#ao-style-%s</styleUrl>\n" +
|
||||
" <LineString>\n" +
|
||||
" <tessellate>1</tessellate>\n" +
|
||||
" <altitudeMode>absolute</altitudeMode>\n" +
|
||||
" <coordinates>\n";
|
||||
|
||||
static final String kml_coord_fmt =
|
||||
" %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
|
||||
|
||||
static final String kml_path_end =
|
||||
" </coordinates>\n" +
|
||||
" </LineString>\n" +
|
||||
" </Placemark>\n";
|
||||
|
||||
static final String kml_point_start =
|
||||
" <Placemark>\n" +
|
||||
" <name>%s</name>\n" +
|
||||
" <styleUrl>#ao-style-%s</styleUrl>\n" +
|
||||
" <Point>\n" +
|
||||
" <tessellate>1</tessellate>\n" +
|
||||
" <altitudeMode>absolute</altitudeMode>\n" +
|
||||
" <coordinates>\n";
|
||||
|
||||
static final String kml_point_end =
|
||||
" </coordinates>\n" +
|
||||
" </Point>\n" +
|
||||
" </Placemark>\n";
|
||||
|
||||
static final String kml_folder_end =
|
||||
" </Folder>\n";
|
||||
|
||||
static final String kml_footer =
|
||||
"</Document>\n" +
|
||||
"</kml>\n";
|
||||
|
||||
void start () {
|
||||
AltosGPS gps = cal_data.gps_pad;
|
||||
|
||||
gps_start_altitude = cal_data.gps_pad_altitude;
|
||||
out.printf(kml_header_start, cal_data.flight, cal_data.serial);
|
||||
out.printf("Product: %s\n", stats.product);
|
||||
out.printf("Firmware: %s\n", stats.firmware_version);
|
||||
out.printf("Date: %04d-%02d-%02d\n",
|
||||
gps.year, gps.month, gps.day);
|
||||
out.printf("Time: %2d:%02d:%02d\n",
|
||||
gps.hour, gps.minute, gps.second);
|
||||
if (stats.max_height != AltosLib.MISSING)
|
||||
out.printf("Max baro height: %s\n", AltosConvert.height.show(6, stats.max_height));
|
||||
if (stats.max_gps_height != AltosLib.MISSING)
|
||||
out.printf("Max GPS Height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
|
||||
if (stats.max_speed != AltosLib.MISSING)
|
||||
out.printf("Max speed: %s\n", AltosConvert.speed.show(6, stats.max_speed));
|
||||
if (stats.max_acceleration != AltosLib.MISSING)
|
||||
out.printf("Max accel: %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
|
||||
out.printf("%s", kml_header_end);
|
||||
}
|
||||
|
||||
void folder_start(String folder_name) {
|
||||
out.printf(kml_folder_start, folder_name);
|
||||
}
|
||||
|
||||
void folder_end() {
|
||||
out.printf(kml_folder_end);
|
||||
}
|
||||
|
||||
void path_style_start(String style, String color) {
|
||||
out.printf(kml_path_style_start, style, color);
|
||||
}
|
||||
|
||||
void path_style_end() {
|
||||
out.printf(kml_path_style_end);
|
||||
}
|
||||
|
||||
void point_style_start(String style, String color) {
|
||||
out.printf(kml_point_style_start, style, color, color);
|
||||
}
|
||||
|
||||
void point_style_end() {
|
||||
out.printf(kml_point_style_end);
|
||||
}
|
||||
|
||||
void path_start(String name, String style) {
|
||||
out.printf(kml_path_start, name, style);
|
||||
}
|
||||
|
||||
void path_end() {
|
||||
out.printf(kml_path_end);
|
||||
}
|
||||
|
||||
void point_start(String name, String style) {
|
||||
out.printf(kml_point_start, name, style);
|
||||
}
|
||||
|
||||
void point_end() {
|
||||
out.printf(kml_point_end);
|
||||
}
|
||||
|
||||
boolean started = false;
|
||||
|
||||
private double baro_altitude(AltosFlightSeries series, double time) {
|
||||
double height = series.value(AltosFlightSeries.height_name, time);
|
||||
|
||||
if (height == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
if (cal_data.gps_pad_altitude == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
return height + cal_data.gps_pad_altitude;
|
||||
}
|
||||
|
||||
void coord(double time, AltosGPS gps, double altitude) {
|
||||
out.printf(kml_coord_fmt,
|
||||
gps.lon, gps.lat,
|
||||
altitude, (double) gps.alt,
|
||||
time, gps.nsat);
|
||||
}
|
||||
|
||||
void end() {
|
||||
out.printf("%s", kml_footer);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
out = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(AltosGPS gps, double alt)
|
||||
{
|
||||
if (gps == null)
|
||||
return;
|
||||
if (gps.lat == AltosLib.MISSING)
|
||||
return;
|
||||
if (gps.lon == AltosLib.MISSING)
|
||||
return;
|
||||
if (alt == AltosLib.MISSING) {
|
||||
alt = cal_data.gps_pad_altitude;
|
||||
if (alt == AltosLib.MISSING)
|
||||
return;
|
||||
}
|
||||
coord(0, gps, alt);
|
||||
prev = gps;
|
||||
}
|
||||
|
||||
public void write_point(AltosTimeValue tv, boolean is_gps) {
|
||||
int state = (int) tv.value;
|
||||
String style_prefix = is_gps ? "gps-" : "baro-";
|
||||
String state_name = AltosLib.state_name(state);
|
||||
String state_label = AltosLib.state_name_capital(state);
|
||||
String style_name = style_prefix + state_name;
|
||||
String folder_name = is_gps ? "GPS" : "Baro";
|
||||
String full_name = state_label + " (" + folder_name + ")";
|
||||
AltosGPS gps = series.gps_before(tv.time);
|
||||
double altitude = is_gps ? gps.alt : baro_altitude(series, tv.time);
|
||||
|
||||
point_style_start(style_name, state_color(state));
|
||||
out.printf("%s\n", full_name);
|
||||
switch (state) {
|
||||
case AltosLib.ao_flight_boost:
|
||||
out.printf("Max accel %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
|
||||
out.printf("Max speed %s\n", AltosConvert.speed.show(6, stats.max_speed));
|
||||
break;
|
||||
case AltosLib.ao_flight_coast:
|
||||
case AltosLib.ao_flight_fast:
|
||||
out.printf("Entry speed %s\n", AltosConvert.speed.show(6, stats.state_enter_speed[state]));
|
||||
out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
|
||||
break;
|
||||
case AltosLib.ao_flight_drogue:
|
||||
out.printf("Max height %s\n", AltosConvert.height.show(6, is_gps ? stats.max_gps_height : stats.max_height));
|
||||
out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
|
||||
break;
|
||||
case AltosLib.ao_flight_main:
|
||||
out.printf("Entry speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
|
||||
out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
|
||||
out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
|
||||
break;
|
||||
case AltosLib.ao_flight_landed:
|
||||
out.printf("Landing speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
|
||||
break;
|
||||
}
|
||||
point_style_end();
|
||||
point_start(full_name, style_name);
|
||||
gps = series.gps_before(tv.time);
|
||||
write(gps, altitude);
|
||||
point_end();
|
||||
}
|
||||
|
||||
public void write(AltosFlightSeries series) {
|
||||
this.series = series;
|
||||
series.finish();
|
||||
stats = new AltosFlightStats(series);
|
||||
cal_data = series.cal_data();
|
||||
start();
|
||||
if (series.height_series != null) {
|
||||
folder_start("Barometric Altitude");
|
||||
path_style_start("baro", style_color(0));
|
||||
out.printf("Barometric Altitude\n");
|
||||
out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_height));
|
||||
path_style_end();
|
||||
path_start("Barometric Altitude", "baro");
|
||||
for (AltosGPSTimeValue gtv : series.gps_series)
|
||||
write(gtv.gps, baro_altitude(series, gtv.time));
|
||||
path_end();
|
||||
if (series.state_series != null) {
|
||||
for (AltosTimeValue tv : series.state_series) {
|
||||
write_point(tv, false);
|
||||
}
|
||||
}
|
||||
folder_end();
|
||||
}
|
||||
folder_start("GPS Altitude");
|
||||
path_style_start("gps", style_color(1));
|
||||
out.printf("GPS Altitude");
|
||||
out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
|
||||
path_style_end();
|
||||
path_start("GPS Altitude", "gps");
|
||||
for (AltosGPSTimeValue gtv : series.gps_series)
|
||||
write(gtv.gps, gtv.gps.alt);
|
||||
path_end();
|
||||
if (series.state_series != null) {
|
||||
for (AltosTimeValue tv : series.state_series) {
|
||||
write_point(tv, true);
|
||||
}
|
||||
}
|
||||
folder_end();
|
||||
end();
|
||||
}
|
||||
|
||||
public AltosKML(File in_name) throws FileNotFoundException {
|
||||
name = in_name;
|
||||
out = new KMLWriter(name);
|
||||
}
|
||||
|
||||
public AltosKML(String in_string) throws FileNotFoundException {
|
||||
this(new File(in_string));
|
||||
}
|
||||
}
|
||||
47
altoslib/AltosLatLon.java
Normal file
47
altoslib/AltosLatLon.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosLatLon {
|
||||
public double lat;
|
||||
public double lon;
|
||||
|
||||
public int hashCode() {
|
||||
return Double.valueOf(lat).hashCode() ^ Double.valueOf(lon).hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
if (!(o instanceof AltosLatLon))
|
||||
return false;
|
||||
|
||||
AltosLatLon other = (AltosLatLon) o;
|
||||
return lat == other.lat && lon == other.lon;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("%f/%f", lat, lon);
|
||||
}
|
||||
|
||||
public AltosLatLon(double lat, double lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
}
|
||||
}
|
||||
24
altoslib/AltosLatitude.java
Normal file
24
altoslib/AltosLatitude.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosLatitude extends AltosLocation {
|
||||
public String pos() { return "N"; }
|
||||
public String neg() { return "S"; }
|
||||
}
|
||||
58
altoslib/AltosLaunchSite.java
Normal file
58
altoslib/AltosLaunchSite.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosLaunchSite {
|
||||
public String name;
|
||||
public double latitude;
|
||||
public double longitude;
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public AltosLaunchSite(String in_name, double in_latitude, double in_longitude) {
|
||||
name = in_name;
|
||||
latitude = in_latitude;
|
||||
longitude = in_longitude;
|
||||
}
|
||||
|
||||
public AltosLaunchSite(String line) throws ParseException {
|
||||
String[] elements = line.split(":");
|
||||
|
||||
if (elements.length < 3)
|
||||
throw new ParseException(String.format("Invalid site line %s", line), 0);
|
||||
|
||||
name = elements[0];
|
||||
|
||||
try {
|
||||
latitude = AltosParse.parse_double_net(elements[1]);
|
||||
longitude = AltosParse.parse_double_net(elements[2]);
|
||||
} catch (ParseException pe) {
|
||||
throw new ParseException(String.format("Invalid site line %s", line), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
altoslib/AltosLaunchSiteListener.java
Normal file
27
altoslib/AltosLaunchSiteListener.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public interface AltosLaunchSiteListener {
|
||||
public abstract void notify_launch_sites(List<AltosLaunchSite> sites);
|
||||
}
|
||||
86
altoslib/AltosLaunchSites.java
Normal file
86
altoslib/AltosLaunchSites.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.net.*;
|
||||
import java.text.*;
|
||||
|
||||
public class AltosLaunchSites extends Thread {
|
||||
URL url;
|
||||
LinkedList<AltosLaunchSite> sites;
|
||||
AltosLaunchSiteListener listener;
|
||||
|
||||
public static String launch_sites_url;
|
||||
|
||||
void notify_complete() {
|
||||
listener.notify_launch_sites(sites);
|
||||
}
|
||||
|
||||
void add(AltosLaunchSite site) {
|
||||
sites.add(site);
|
||||
}
|
||||
|
||||
void add(String line) {
|
||||
try {
|
||||
add(new AltosLaunchSite(line));
|
||||
} catch (ParseException pe) {
|
||||
System.out.printf("parse exception %s\n", pe.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
String path;
|
||||
|
||||
if (launch_sites_url != null)
|
||||
path = launch_sites_url;
|
||||
else {
|
||||
path = System.getenv(AltosLib.launch_sites_env);
|
||||
if (path == null)
|
||||
path = AltosLib.launch_sites_url;
|
||||
}
|
||||
url = new URL(path);
|
||||
URLConnection uc = url.openConnection();
|
||||
|
||||
InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
|
||||
BufferedReader in = new BufferedReader(in_stream);
|
||||
|
||||
for (;;) {
|
||||
String line = in.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
add(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.printf("file exception %s\n", e.toString());
|
||||
} finally {
|
||||
notify_complete();
|
||||
}
|
||||
}
|
||||
|
||||
public AltosLaunchSites(AltosLaunchSiteListener listener) {
|
||||
sites = new LinkedList<AltosLaunchSite>();
|
||||
this.listener = listener;
|
||||
start();
|
||||
}
|
||||
}
|
||||
719
altoslib/AltosLib.java
Normal file
719
altoslib/AltosLib.java
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class AltosLib {
|
||||
/* EEProm command letters */
|
||||
public static final int AO_LOG_FLIGHT = 'F';
|
||||
public static final int AO_LOG_SENSOR = 'A';
|
||||
public static final int AO_LOG_TEMP_VOLT = 'T';
|
||||
public static final int AO_LOG_DEPLOY = 'D';
|
||||
public static final int AO_LOG_STATE = 'S';
|
||||
public static final int AO_LOG_GPS_POS = 'P';
|
||||
public static final int AO_LOG_GPS_TIME = 'G';
|
||||
public static final int AO_LOG_GPS_LAT = 'N';
|
||||
public static final int AO_LOG_GPS_LON = 'W';
|
||||
public static final int AO_LOG_GPS_ALT = 'H';
|
||||
public static final int AO_LOG_GPS_SAT = 'V';
|
||||
public static final int AO_LOG_GPS_DATE = 'Y';
|
||||
public static final int AO_LOG_PRESSURE = 'P';
|
||||
|
||||
public static boolean is_gps_cmd(int cmd) {
|
||||
switch (cmd) {
|
||||
case AltosLib.AO_LOG_GPS_POS:
|
||||
case AltosLib.AO_LOG_GPS_TIME:
|
||||
case AltosLib.AO_LOG_GPS_LAT:
|
||||
case AltosLib.AO_LOG_GPS_LON:
|
||||
case AltosLib.AO_LOG_GPS_ALT:
|
||||
case AltosLib.AO_LOG_GPS_SAT:
|
||||
case AltosLib.AO_LOG_GPS_DATE:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Added for header fields in eeprom files */
|
||||
public static final int AO_LOG_CONFIG_VERSION = 1000;
|
||||
public static final int AO_LOG_MAIN_DEPLOY = 1001;
|
||||
public static final int AO_LOG_APOGEE_DELAY = 1002;
|
||||
public static final int AO_LOG_RADIO_CHANNEL = 1003;
|
||||
public static final int AO_LOG_CALLSIGN = 1004;
|
||||
public static final int AO_LOG_ACCEL_CAL = 1005;
|
||||
public static final int AO_LOG_RADIO_CAL = 1006;
|
||||
public static final int AO_LOG_MAX_FLIGHT_LOG = 1007;
|
||||
public static final int AO_LOG_MANUFACTURER = 2000;
|
||||
public static final int AO_LOG_PRODUCT = 2001;
|
||||
public static final int AO_LOG_SERIAL_NUMBER = 2002;
|
||||
public static final int AO_LOG_LOG_FORMAT = 2003;
|
||||
|
||||
public static final int AO_LOG_FREQUENCY = 2004;
|
||||
public static final int AO_LOG_APOGEE_LOCKOUT = 2005;
|
||||
public static final int AO_LOG_RADIO_RATE = 2006;
|
||||
public static final int AO_LOG_IGNITE_MODE = 2007;
|
||||
public static final int AO_LOG_PAD_ORIENTATION = 2008;
|
||||
public static final int AO_LOG_RADIO_ENABLE = 2009;
|
||||
public static final int AO_LOG_AES_KEY = 2010;
|
||||
public static final int AO_LOG_APRS = 2011;
|
||||
public static final int AO_LOG_BEEP_SETTING = 2012;
|
||||
public static final int AO_LOG_TRACKER_SETTING = 2013;
|
||||
public static final int AO_LOG_PYRO_TIME = 2014;
|
||||
public static final int AO_LOG_APRS_ID = 2015;
|
||||
public static final int AO_LOG_ALTITUDE_32 = 2016;
|
||||
|
||||
/* Added for header fields in telemega files */
|
||||
public static final int AO_LOG_BARO_RESERVED = 3000;
|
||||
public static final int AO_LOG_BARO_SENS = 3001;
|
||||
public static final int AO_LOG_BARO_OFF = 3002;
|
||||
public static final int AO_LOG_BARO_TCS = 3004;
|
||||
public static final int AO_LOG_BARO_TCO = 3005;
|
||||
public static final int AO_LOG_BARO_TREF = 3006;
|
||||
public static final int AO_LOG_BARO_TEMPSENS = 3007;
|
||||
public static final int AO_LOG_BARO_CRC = 3008;
|
||||
public static final int AO_LOG_IMU_CAL = 3009;
|
||||
|
||||
public static final int AO_LOG_SOFTWARE_VERSION = 9999;
|
||||
|
||||
public final static int MISSING = 0x7fffffff;
|
||||
|
||||
/* Added to flag invalid records */
|
||||
public static final int AO_LOG_INVALID = -1;
|
||||
|
||||
/* Flight state numbers and names */
|
||||
public static final int ao_flight_startup = 0;
|
||||
public static final int ao_flight_idle = 1;
|
||||
public static final int ao_flight_pad = 2;
|
||||
public static final int ao_flight_boost = 3;
|
||||
public static final int ao_flight_fast = 4;
|
||||
public static final int ao_flight_coast = 5;
|
||||
public static final int ao_flight_drogue = 6;
|
||||
public static final int ao_flight_main = 7;
|
||||
public static final int ao_flight_landed = 8;
|
||||
public static final int ao_flight_invalid = 9;
|
||||
public static final int ao_flight_stateless = 10;
|
||||
|
||||
/* USB product IDs */
|
||||
public final static int vendor_altusmetrum = 0xfffe;
|
||||
|
||||
public final static int product_altusmetrum = 0x000a;
|
||||
public final static int product_telemetrum = 0x000b;
|
||||
public final static int product_teledongle = 0x000c;
|
||||
public final static int product_easytimer = 0x000d;
|
||||
public final static int product_telebt = 0x000e;
|
||||
public final static int product_telelaunch = 0x000f;
|
||||
public final static int product_telelco = 0x0010;
|
||||
public final static int product_telescience = 0x0011;
|
||||
public final static int product_telepyro =0x0012;
|
||||
public final static int product_telemega = 0x0023;
|
||||
public final static int product_megadongle = 0x0024;
|
||||
public final static int product_telegps = 0x0025;
|
||||
public final static int product_easymini = 0x0026;
|
||||
public final static int product_telemini = 0x0027;
|
||||
public final static int product_easymega = 0x0028;
|
||||
public final static int product_usbtrng = 0x0029;
|
||||
public final static int product_usbrelay = 0x002a;
|
||||
public final static int product_mpusb = 0x002b;
|
||||
public final static int product_easymotor = 0x002c;
|
||||
public final static int product_altusmetrum_min = 0x000a;
|
||||
public final static int product_altusmetrum_max = 0x002c;
|
||||
|
||||
public final static int product_any = 0x10000;
|
||||
public final static int product_basestation = 0x10000 + 1;
|
||||
public final static int product_altimeter = 0x10000 + 2;
|
||||
|
||||
public final static int gps_builtin = 0;
|
||||
public final static int gps_mosaic = 1;
|
||||
|
||||
public final static String[] gps_receiver_names = {
|
||||
"Builtin", "Mosaic-X5"
|
||||
};
|
||||
|
||||
private static class Product {
|
||||
final String name;
|
||||
final int product;
|
||||
|
||||
Product (String name, int product) {
|
||||
this.name = name;
|
||||
this.product = product;
|
||||
}
|
||||
}
|
||||
|
||||
private static Product[] products = {
|
||||
new Product("telemetrum", product_telemetrum),
|
||||
new Product("teleballoon", product_telemetrum),
|
||||
new Product("teledongle", product_teledongle),
|
||||
new Product("easytimer", product_easytimer),
|
||||
new Product("telebt", product_telebt),
|
||||
new Product("telelaunch", product_telelaunch),
|
||||
new Product("telelco", product_telelco),
|
||||
new Product("telescience", product_telescience),
|
||||
new Product("telepyro", product_telepyro),
|
||||
new Product("telemega", product_telemega),
|
||||
new Product("megadongle", product_megadongle),
|
||||
new Product("telegps", product_telegps),
|
||||
new Product("easymini", product_easymini),
|
||||
new Product("telemini", product_telemini),
|
||||
new Product("easymega", product_easymega),
|
||||
new Product("easymotor", product_easymotor)
|
||||
};
|
||||
|
||||
public static int name_to_product(String name) {
|
||||
String low = name.toLowerCase();
|
||||
|
||||
for (int i = 0; i < products.length; i++)
|
||||
if (low.startsWith(products[i].name))
|
||||
return products[i].product;
|
||||
return product_any;
|
||||
}
|
||||
|
||||
public static boolean has_9dof(int device_type) {
|
||||
return device_type == product_telemega || device_type == product_easymega;
|
||||
}
|
||||
|
||||
public static boolean has_radio(int device_type) {
|
||||
return device_type != product_easymini && device_type != product_easymega;
|
||||
}
|
||||
|
||||
public static boolean has_gps(int device_type) {
|
||||
return device_type == product_telemetrum ||
|
||||
device_type == product_telemega ||
|
||||
device_type == product_telegps;
|
||||
}
|
||||
|
||||
/* Bluetooth "identifier" (bluetooth sucks) */
|
||||
public final static String bt_product_telebt = "TeleBT";
|
||||
|
||||
/* "good" voltages */
|
||||
|
||||
public final static double ao_battery_good = 3.8;
|
||||
public final static double ao_igniter_good = 3.5;
|
||||
|
||||
/* Telemetry modes */
|
||||
public static final int ao_telemetry_off = 0;
|
||||
public static final int ao_telemetry_min = 1;
|
||||
public static final int ao_telemetry_standard = 1;
|
||||
public static final int ao_telemetry_0_9 = 2;
|
||||
public static final int ao_telemetry_0_8 = 3;
|
||||
public static final int ao_telemetry_max = 3;
|
||||
|
||||
private static final String[] ao_telemetry_name = {
|
||||
"Off", "Standard Telemetry", "TeleMetrum v0.9", "TeleMetrum v0.8"
|
||||
};
|
||||
|
||||
public static final int ao_telemetry_rate_38400 = 0;
|
||||
public static final int ao_telemetry_rate_9600 = 1;
|
||||
public static final int ao_telemetry_rate_2400 = 2;
|
||||
public static final int ao_telemetry_rate_max = 2;
|
||||
|
||||
public static final Integer[] ao_telemetry_rate_values = {
|
||||
38400, 9600, 2400
|
||||
};
|
||||
|
||||
public static final int ao_aprs_format_compressed = 0;
|
||||
public static final int ao_aprs_format_uncompressed = 1;
|
||||
|
||||
public static final String[] ao_aprs_format_name = {
|
||||
"Compressed", "Uncompressed"
|
||||
};
|
||||
|
||||
public static final String launch_sites_url = "https://maps.altusmetrum.org/launch-sites.txt";
|
||||
public static final String launch_sites_env = "LAUNCH_SITES";
|
||||
// public static final String launch_sites_url = "file:///home/keithp/misc/text/altusmetrum/AltOS/launch-sites.txt";
|
||||
|
||||
public static final String unit_info_url = "https://altusmetrum.org/cgi-bin/unitinfo.cgi?sn=%d";
|
||||
public static final String unit_info_env = "UNIT_INFO";
|
||||
|
||||
public static final int ao_telemetry_standard_len = 32;
|
||||
public static final int ao_telemetry_0_9_len = 95;
|
||||
public static final int ao_telemetry_0_8_len = 94;
|
||||
|
||||
private static final int[] ao_telemetry_len = {
|
||||
0, 32, 95, 94
|
||||
};
|
||||
|
||||
private static HashMap<String,Integer> string_to_state = new HashMap<String,Integer>();
|
||||
|
||||
private static boolean map_initialized = false;
|
||||
|
||||
public static void initialize_map()
|
||||
{
|
||||
string_to_state.put("startup", ao_flight_startup);
|
||||
string_to_state.put("idle", ao_flight_idle);
|
||||
string_to_state.put("pad", ao_flight_pad);
|
||||
string_to_state.put("boost", ao_flight_boost);
|
||||
string_to_state.put("fast", ao_flight_fast);
|
||||
string_to_state.put("coast", ao_flight_coast);
|
||||
string_to_state.put("drogue", ao_flight_drogue);
|
||||
string_to_state.put("apogee", ao_flight_coast);
|
||||
string_to_state.put("main", ao_flight_main);
|
||||
string_to_state.put("landed", ao_flight_landed);
|
||||
string_to_state.put("invalid", ao_flight_invalid);
|
||||
string_to_state.put("stateless", ao_flight_stateless);
|
||||
map_initialized = true;
|
||||
}
|
||||
|
||||
public static int telemetry_len(int telemetry) {
|
||||
if (telemetry <= ao_telemetry_max)
|
||||
return ao_telemetry_len[telemetry];
|
||||
throw new IllegalArgumentException(String.format("Invalid telemetry %d",
|
||||
telemetry));
|
||||
}
|
||||
|
||||
public static String telemetry_name(int telemetry) {
|
||||
if (telemetry <= ao_telemetry_max)
|
||||
return ao_telemetry_name[telemetry];
|
||||
throw new IllegalArgumentException(String.format("Invalid telemetry %d",
|
||||
telemetry));
|
||||
}
|
||||
|
||||
private static int[] split_version(String version) {
|
||||
String[] tokens = version.split("\\.");
|
||||
int[] ret = new int[tokens.length];
|
||||
for (int i = 0; i < tokens.length; i++)
|
||||
ret[i] = Integer.parseInt(tokens[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int compare_version(String version_a, String version_b) {
|
||||
int[] a = split_version(version_a);
|
||||
int[] b = split_version(version_b);
|
||||
|
||||
for (int i = 0; i < Math.min(a.length, b.length); i++) {
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
if (a.length < b.length)
|
||||
return -1;
|
||||
if (a.length > b.length)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static String[] state_to_string = {
|
||||
"startup",
|
||||
"idle",
|
||||
"pad",
|
||||
"boost",
|
||||
"fast",
|
||||
"coast",
|
||||
"drogue",
|
||||
"main",
|
||||
"landed",
|
||||
"invalid",
|
||||
"stateless",
|
||||
};
|
||||
|
||||
private static String[] state_to_string_capital = {
|
||||
"Startup",
|
||||
"Idle",
|
||||
"Pad",
|
||||
"Boost",
|
||||
"Fast",
|
||||
"Coast",
|
||||
"Drogue",
|
||||
"Main",
|
||||
"Landed",
|
||||
"Invalid",
|
||||
"Stateless",
|
||||
};
|
||||
|
||||
public static int state(String state) {
|
||||
if (!map_initialized)
|
||||
initialize_map();
|
||||
if (string_to_state.containsKey(state))
|
||||
return string_to_state.get(state);
|
||||
return ao_flight_invalid;
|
||||
}
|
||||
|
||||
public static String state_name(int state) {
|
||||
if (state < 0 || state_to_string.length <= state)
|
||||
return "invalid";
|
||||
return state_to_string[state];
|
||||
}
|
||||
|
||||
public static String state_name_capital(int state) {
|
||||
if (state < 0 || state_to_string.length <= state)
|
||||
return "Invalid";
|
||||
return state_to_string_capital[state];
|
||||
}
|
||||
|
||||
public static final int AO_GPS_VALID = (1 << 4);
|
||||
public static final int AO_GPS_RUNNING = (1 << 5);
|
||||
public static final int AO_GPS_DATE_VALID = (1 << 6);
|
||||
public static final int AO_GPS_NUM_SAT_SHIFT = 0;
|
||||
public static final int AO_GPS_NUM_SAT_MASK = 0xf;
|
||||
|
||||
public static final int AO_PAD_ORIENTATION_ANTENNA_UP = 0;
|
||||
public static final int AO_PAD_ORIENTATION_ANTENNA_DOWN = 1;
|
||||
public static final int AO_PAD_ORIENTATION_WORDS_UPRIGHT = 2;
|
||||
public static final int AO_PAD_ORIENTATION_WORDS_UPSIDEDOWN = 3;
|
||||
public static final int AO_PAD_ORIENTATION_BIG_PARTS_UP = 4;
|
||||
public static final int AO_PAD_ORIENTATION_BIG_PARTS_DOWN = 5;
|
||||
|
||||
public static final int AO_LOG_FORMAT_UNKNOWN = 0;
|
||||
public static final int AO_LOG_FORMAT_FULL = 1;
|
||||
public static final int AO_LOG_FORMAT_TINY = 2;
|
||||
public static final int AO_LOG_FORMAT_TELEMETRY = 3;
|
||||
public static final int AO_LOG_FORMAT_TELESCIENCE = 4;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA_OLD = 5;
|
||||
public static final int AO_LOG_FORMAT_EASYMINI1 = 6;
|
||||
public static final int AO_LOG_FORMAT_TELEMETRUM = 7;
|
||||
public static final int AO_LOG_FORMAT_TELEMINI2 = 8;
|
||||
public static final int AO_LOG_FORMAT_TELEGPS = 9;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA = 10;
|
||||
public static final int AO_LOG_FORMAT_DETHERM = 11;
|
||||
public static final int AO_LOG_FORMAT_TELEMINI3 = 12;
|
||||
public static final int AO_LOG_FORMAT_TELEFIRETWO = 13;
|
||||
public static final int AO_LOG_FORMAT_EASYMINI2 = 14;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA_3 = 15;
|
||||
public static final int AO_LOG_FORMAT_EASYMEGA_2 = 16;
|
||||
public static final int AO_LOG_FORMAT_TELESTATIC = 17;
|
||||
public static final int AO_LOG_FORMAT_MICROPEAK2 = 18;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA_4 = 19;
|
||||
public static final int AO_LOG_FORMAT_EASYMOTOR = 20;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA_5 = 21;
|
||||
public static final int AO_LOG_FORMAT_TELEMEGA_6 = 22;
|
||||
public static final int AO_LOG_FORMAT_EASYTIMER_2 = 23;
|
||||
public static final int AO_LOG_FORMAT_EASYMEGA_3 = 24;
|
||||
public static final int AO_LOG_FORMAT_NONE = 127;
|
||||
|
||||
public static final int model_mpu6000 = 0;
|
||||
public static final int model_mpu9250 = 1;
|
||||
public static final int model_adxl375 = 2;
|
||||
public static final int model_bmx160 = 3;
|
||||
public static final int model_hmc5883 = 4;
|
||||
public static final int model_mmc5983 = 5;
|
||||
public static final int model_bmi088 = 6;
|
||||
|
||||
public static boolean isspace(int c) {
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final 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;
|
||||
}
|
||||
|
||||
public static final boolean ishex(String s) {
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
if (!ishex(s.charAt(i)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public static boolean isdec(int c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isdec(String s) {
|
||||
for (int i = 0; i < s.length(); i++)
|
||||
if (!isdec(s.charAt(i)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int fromdec(int c) {
|
||||
if ('0' <= c && c <= '9')
|
||||
return c - '0';
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int int8(int[] bytes, int i) {
|
||||
return (int) (byte) bytes[i];
|
||||
}
|
||||
|
||||
public static int uint8(int[] bytes, int i) {
|
||||
return bytes[i];
|
||||
}
|
||||
|
||||
public static int int16(int[] bytes, int i) {
|
||||
return (int) (short) (bytes[i] + (bytes[i+1] << 8));
|
||||
}
|
||||
|
||||
public static int uint16(int[] bytes, int i) {
|
||||
return bytes[i] + (bytes[i+1] << 8);
|
||||
}
|
||||
|
||||
public static int uint32(int[] bytes, int i) {
|
||||
return bytes[i] +
|
||||
(bytes[i+1] << 8) +
|
||||
(bytes[i+2] << 16) +
|
||||
(bytes[i+3] << 24);
|
||||
}
|
||||
|
||||
public static int int32(int[] bytes, int i) {
|
||||
return (int) uint32(bytes, i);
|
||||
}
|
||||
|
||||
public static final Charset unicode_set = Charset.forName("UTF-8");
|
||||
|
||||
public static String string(int[] bytes, int s, int l) {
|
||||
if (s + l > bytes.length) {
|
||||
if (s > bytes.length) {
|
||||
s = bytes.length;
|
||||
l = 0;
|
||||
} else {
|
||||
l = bytes.length - s;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = l - 1; i >= 0; i--)
|
||||
if (bytes[s+i] != 0)
|
||||
break;
|
||||
|
||||
l = i + 1;
|
||||
byte[] b = new byte[l];
|
||||
|
||||
for (i = 0; i < l; i++)
|
||||
b[i] = (byte) bytes[s+i];
|
||||
String n = new String(b, unicode_set);
|
||||
return n;
|
||||
}
|
||||
|
||||
public static int hexbyte(String s, int i) {
|
||||
int c0, c1;
|
||||
|
||||
if (s.length() < i + 2)
|
||||
throw new NumberFormatException(String.format("invalid hex \"%s\"", s));
|
||||
c0 = s.charAt(i);
|
||||
if (!ishex(c0))
|
||||
throw new NumberFormatException(String.format("invalid hex \"%c\"", c0));
|
||||
c1 = s.charAt(i+1);
|
||||
if (!ishex(c1))
|
||||
throw new NumberFormatException(String.format("invalid hex \"%c\"", c1));
|
||||
return fromhex(c0) * 16 + fromhex(c1);
|
||||
}
|
||||
|
||||
public static int[] hexbytes(String s) {
|
||||
int n;
|
||||
int[] r;
|
||||
int i;
|
||||
|
||||
if ((s.length() & 1) != 0)
|
||||
throw new NumberFormatException(String.format("invalid line \"%s\"", s));
|
||||
byte[] bytes = s.getBytes(unicode_set);
|
||||
n = bytes.length / 2;
|
||||
r = new int[n];
|
||||
for (i = 0; i < n; i++) {
|
||||
int h = fromhex(bytes[(i << 1)]);
|
||||
int l = fromhex(bytes[(i << 1) + 1]);
|
||||
if (h < 0 || l < 0)
|
||||
throw new NumberFormatException(String.format("invalid hex \"%c%c\"",
|
||||
bytes[(i<<1)], bytes[(i<<1) + 1]));
|
||||
r[i] = (h << 4) + l;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static long fromdec(String s) throws NumberFormatException {
|
||||
int c;
|
||||
long v = 0;
|
||||
long 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;
|
||||
}
|
||||
|
||||
public static String gets(FileInputStream s) throws IOException {
|
||||
int c;
|
||||
String line = "";
|
||||
|
||||
while ((c = s.read()) != -1) {
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (c == '\n') {
|
||||
return line;
|
||||
}
|
||||
line = line + (char) c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String replace_extension(String input, String extension) {
|
||||
int dot = input.lastIndexOf(".");
|
||||
if (dot > 0)
|
||||
input = input.substring(0,dot);
|
||||
return input.concat(extension);
|
||||
}
|
||||
|
||||
public static File replace_extension(File input, String extension) {
|
||||
return new File(replace_extension(input.getPath(), extension));
|
||||
}
|
||||
|
||||
public static String product_name(int product_id) {
|
||||
switch (product_id) {
|
||||
case product_altusmetrum: return "AltusMetrum";
|
||||
case product_telemetrum: return "TeleMetrum";
|
||||
case product_teledongle: return "TeleDongle";
|
||||
case product_easytimer: return "EasyTimer";
|
||||
case product_telebt: return "TeleBT";
|
||||
case product_telelaunch: return "TeleLaunch";
|
||||
case product_telelco: return "TeleLco";
|
||||
case product_telescience: return "Telescience";
|
||||
case product_telepyro: return "TelePyro";
|
||||
case product_telemega: return "TeleMega";
|
||||
case product_megadongle: return "MegaDongle";
|
||||
case product_telegps: return "TeleGPS";
|
||||
case product_easymini: return "EasyMini";
|
||||
case product_telemini: return "TeleMini";
|
||||
case product_easymega: return "EasyMega";
|
||||
case product_easymotor: return "EasyMotor";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static int product_id_from_log_format(int log_format) {
|
||||
switch (log_format){
|
||||
case AO_LOG_FORMAT_UNKNOWN:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_FULL:
|
||||
return product_telemetrum;
|
||||
case AO_LOG_FORMAT_TINY:
|
||||
return product_telemini;
|
||||
case AO_LOG_FORMAT_TELEMETRY:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_TELESCIENCE:
|
||||
return product_telescience;
|
||||
case AO_LOG_FORMAT_TELEMEGA_OLD:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_EASYMINI1:
|
||||
return product_easymini;
|
||||
case AO_LOG_FORMAT_TELEMETRUM:
|
||||
return product_telemetrum;
|
||||
case AO_LOG_FORMAT_TELEMINI2:
|
||||
return product_telemini;
|
||||
case AO_LOG_FORMAT_TELEGPS:
|
||||
return product_telegps;
|
||||
case AO_LOG_FORMAT_TELEMEGA:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_DETHERM:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_TELEMINI3:
|
||||
return product_telemini;
|
||||
case AO_LOG_FORMAT_TELEFIRETWO:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_EASYMINI2:
|
||||
return product_easymini;
|
||||
case AO_LOG_FORMAT_TELEMEGA_3:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_EASYMEGA_2:
|
||||
case AO_LOG_FORMAT_EASYMEGA_3:
|
||||
return product_easymega;
|
||||
case AO_LOG_FORMAT_TELESTATIC:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_MICROPEAK2:
|
||||
return product_altusmetrum;
|
||||
case AO_LOG_FORMAT_TELEMEGA_4:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_EASYMOTOR:
|
||||
return product_easymotor;
|
||||
case AO_LOG_FORMAT_TELEMEGA_5:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_TELEMEGA_6:
|
||||
return product_telemega;
|
||||
case AO_LOG_FORMAT_NONE:
|
||||
return product_altusmetrum;
|
||||
default:
|
||||
return product_altusmetrum;
|
||||
}
|
||||
}
|
||||
|
||||
public static String igniter_name(int i) {
|
||||
return String.format("Igniter %c", 'A' + i);
|
||||
}
|
||||
|
||||
public static String igniter_short_name(int i) {
|
||||
return String.format("igniter_%c", 'a' + i);
|
||||
}
|
||||
|
||||
public static AltosRecordSet record_set(File file) throws FileNotFoundException, IOException {
|
||||
FileInputStream in;
|
||||
in = new FileInputStream(file);
|
||||
if (file.getName().endsWith("telem")) {
|
||||
return new AltosTelemetryFile(in);
|
||||
} else if (file.getName().endsWith("eeprom")) {
|
||||
return new AltosEepromFile(in);
|
||||
} else {
|
||||
String name = file.getName();
|
||||
int dot = name.lastIndexOf('.');
|
||||
String extension;
|
||||
|
||||
if (dot == -1)
|
||||
throw new IOException(String.format("%s (Missing extension)", file.toString()));
|
||||
else {
|
||||
extension = name.substring(dot);
|
||||
throw new IOException(String.format("%s (Invalid extension '%s')",
|
||||
file.toString(),
|
||||
extension));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
31
altoslib/AltosLine.java
Normal file
31
altoslib/AltosLine.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosLine {
|
||||
public String line;
|
||||
|
||||
public AltosLine() {
|
||||
line = null;
|
||||
}
|
||||
|
||||
public AltosLine(String s) {
|
||||
line = s;
|
||||
}
|
||||
}
|
||||
640
altoslib/AltosLink.java
Normal file
640
altoslib/AltosLink.java
Normal file
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AltosLink implements Runnable {
|
||||
|
||||
public final static int ERROR = -1;
|
||||
public final static int TIMEOUT = -2;
|
||||
|
||||
public abstract int getchar() throws InterruptedException;
|
||||
public abstract void print(String data) throws InterruptedException;
|
||||
public abstract void putchar(byte c);
|
||||
public abstract void close() throws InterruptedException;
|
||||
|
||||
public static boolean debug = false;
|
||||
public static void set_debug(boolean in_debug) { debug = in_debug; }
|
||||
|
||||
public boolean has_error;
|
||||
|
||||
LinkedList<String> pending_output = new LinkedList<String>();
|
||||
|
||||
public LinkedList<LinkedBlockingQueue<AltosLine>> monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();;
|
||||
public LinkedBlockingQueue<AltosLine> reply_queue = new LinkedBlockingQueue<AltosLine>();
|
||||
public LinkedBlockingQueue<byte[]> binary_queue = new LinkedBlockingQueue<byte[]>();
|
||||
|
||||
private String match_string = null;
|
||||
|
||||
public synchronized void add_monitor(LinkedBlockingQueue<AltosLine> q) {
|
||||
set_monitor(true);
|
||||
monitors.add(q);
|
||||
}
|
||||
|
||||
public synchronized void remove_monitor(LinkedBlockingQueue<AltosLine> q) {
|
||||
monitors.remove(q);
|
||||
if (monitors.isEmpty())
|
||||
set_monitor(false);
|
||||
}
|
||||
|
||||
public void printf(String format, Object ... arguments) {
|
||||
String line = String.format(format, arguments);
|
||||
if (debug) {
|
||||
synchronized (pending_output) {
|
||||
pending_output.add(line);
|
||||
}
|
||||
}
|
||||
try {
|
||||
print(line);
|
||||
} catch (InterruptedException ie) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException {
|
||||
flush_output();
|
||||
AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
if (line != null)
|
||||
return line.line;
|
||||
return null;
|
||||
}
|
||||
|
||||
public String get_reply() throws InterruptedException {
|
||||
return get_reply(5000);
|
||||
}
|
||||
|
||||
|
||||
public abstract boolean can_cancel_reply();
|
||||
public abstract boolean show_reply_timeout();
|
||||
public abstract void hide_reply_timeout();
|
||||
|
||||
public boolean reply_abort;
|
||||
public int in_reply;
|
||||
boolean cancel_enable = true;
|
||||
|
||||
public void set_cancel_enable(boolean e) {
|
||||
cancel_enable = e;
|
||||
}
|
||||
|
||||
boolean reply_timeout_shown = false;
|
||||
|
||||
private boolean check_reply_timeout() {
|
||||
if (!cancel_enable)
|
||||
return false;
|
||||
if (!reply_timeout_shown)
|
||||
reply_timeout_shown = show_reply_timeout();
|
||||
return reply_abort;
|
||||
}
|
||||
|
||||
private void cleanup_reply_timeout() {
|
||||
if (reply_timeout_shown) {
|
||||
reply_timeout_shown = false;
|
||||
hide_reply_timeout();
|
||||
}
|
||||
}
|
||||
|
||||
private int len_read = 0;
|
||||
|
||||
private boolean match_bytes(byte[] bytes, int byte_count, String match) {
|
||||
if (byte_count < match.length())
|
||||
return false;
|
||||
String line = new String(bytes, 0, byte_count, AltosLib.unicode_set);
|
||||
if (line == null)
|
||||
return false;
|
||||
return line.indexOf(match) >= 0;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
int c;
|
||||
byte[] line_bytes = null;
|
||||
int line_count = 0;
|
||||
|
||||
try {
|
||||
for (;;) {
|
||||
c = getchar();
|
||||
if (Thread.interrupted()) {
|
||||
break;
|
||||
}
|
||||
if (c == ERROR) {
|
||||
if (debug)
|
||||
System.out.printf("ERROR\n");
|
||||
has_error = true;
|
||||
add_telem (new AltosLine());
|
||||
add_reply (new AltosLine());
|
||||
break;
|
||||
}
|
||||
if (c == TIMEOUT) {
|
||||
if (debug)
|
||||
System.out.printf("TIMEOUT\n");
|
||||
continue;
|
||||
}
|
||||
if (c == '\r' && len_read == 0)
|
||||
continue;
|
||||
synchronized(this) {
|
||||
if (c == '\n' && len_read == 0) {
|
||||
if (line_count != 0) {
|
||||
add_bytes(line_bytes, line_count);
|
||||
line_count = 0;
|
||||
}
|
||||
} else {
|
||||
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++;
|
||||
if (len_read !=0 && line_count == len_read) {
|
||||
add_binary(line_bytes, line_count);
|
||||
line_count = 0;
|
||||
len_read = 0;
|
||||
}
|
||||
if (match_string != null && match_bytes(line_bytes, line_count, match_string)) {
|
||||
match_string = null;
|
||||
add_bytes(line_bytes, line_count);
|
||||
line_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void set_match(String match) {
|
||||
match_string = match;
|
||||
}
|
||||
|
||||
public String get_reply(int timeout) throws InterruptedException {
|
||||
boolean can_cancel = can_cancel_reply();
|
||||
String reply = null;
|
||||
|
||||
// if (!can_cancel && remote)
|
||||
// System.out.printf("Uh-oh, reading remote serial device from swing thread\n");
|
||||
|
||||
if (remote && can_cancel) {
|
||||
timeout = 500;
|
||||
switch (telemetry_rate) {
|
||||
case AltosLib.ao_telemetry_rate_38400:
|
||||
default:
|
||||
timeout = 500;
|
||||
break;
|
||||
case AltosLib.ao_telemetry_rate_9600:
|
||||
timeout = 2000;
|
||||
break;
|
||||
case AltosLib.ao_telemetry_rate_2400:
|
||||
timeout = 8000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
++in_reply;
|
||||
|
||||
flush_output();
|
||||
|
||||
reply_abort = false;
|
||||
reply_timeout_shown = false;
|
||||
for (;;) {
|
||||
AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
if (line != null) {
|
||||
cleanup_reply_timeout();
|
||||
reply = line.line;
|
||||
break;
|
||||
}
|
||||
if (!remote || !can_cancel || check_reply_timeout()) {
|
||||
reply = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
--in_reply;
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
public byte[] get_binary_reply(int timeout, int len) throws InterruptedException {
|
||||
boolean can_cancel = can_cancel_reply();
|
||||
byte[] bytes = null;
|
||||
|
||||
synchronized(this) {
|
||||
len_read = len;
|
||||
}
|
||||
try {
|
||||
++in_reply;
|
||||
|
||||
flush_output();
|
||||
|
||||
reply_abort = false;
|
||||
reply_timeout_shown = false;
|
||||
for (;;) {
|
||||
bytes = binary_queue.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
if (bytes != null) {
|
||||
cleanup_reply_timeout();
|
||||
break;
|
||||
}
|
||||
if (!remote || !can_cancel || check_reply_timeout()) {
|
||||
bytes = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
--in_reply;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void add_telem(AltosLine line) throws InterruptedException {
|
||||
for (int e = 0; e < monitors.size(); e++) {
|
||||
LinkedBlockingQueue<AltosLine> q = monitors.get(e);
|
||||
q.put(line);
|
||||
}
|
||||
}
|
||||
|
||||
public void add_reply(AltosLine line) throws InterruptedException {
|
||||
reply_queue.put (line);
|
||||
}
|
||||
|
||||
public void abort_reply() {
|
||||
try {
|
||||
add_telem (new AltosLine());
|
||||
add_reply (new AltosLine());
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
|
||||
public void add_string(String line) throws InterruptedException {
|
||||
if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
|
||||
add_telem(new AltosLine(line));
|
||||
} else {
|
||||
add_reply(new AltosLine(line));
|
||||
}
|
||||
}
|
||||
|
||||
public void add_bytes(byte[] bytes, int len) throws InterruptedException {
|
||||
String line;
|
||||
line = new String(bytes, 0, len, AltosLib.unicode_set);
|
||||
if (debug)
|
||||
System.out.printf("\t\t\t\t\t%s\n", line);
|
||||
add_string(line);
|
||||
}
|
||||
|
||||
public void add_binary(byte[] bytes, int len) throws InterruptedException {
|
||||
byte[] dup = new byte[len];
|
||||
|
||||
if (debug)
|
||||
System.out.printf ("\t\t\t\t\t%d:", len);
|
||||
for(int i = 0; i < len; i++) {
|
||||
dup[i] = bytes[i];
|
||||
if (debug)
|
||||
System.out.printf(" %02x", dup[i]);
|
||||
}
|
||||
if (debug)
|
||||
System.out.printf("\n");
|
||||
|
||||
binary_queue.put(dup);
|
||||
}
|
||||
|
||||
public synchronized void flush_output() {
|
||||
if (pending_output == null)
|
||||
return;
|
||||
synchronized (pending_output) {
|
||||
for (String s : pending_output)
|
||||
System.out.print(s);
|
||||
pending_output.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void flush_input(int timeout) throws InterruptedException {
|
||||
flush_output();
|
||||
boolean got_some;
|
||||
|
||||
do {
|
||||
Thread.sleep(timeout);
|
||||
got_some = !reply_queue.isEmpty();
|
||||
reply_queue.clear();
|
||||
} while (got_some);
|
||||
}
|
||||
|
||||
|
||||
public void flush_input() throws InterruptedException {
|
||||
if (remote) {
|
||||
int timeout = 500;
|
||||
switch (telemetry_rate) {
|
||||
case AltosLib.ao_telemetry_rate_38400:
|
||||
default:
|
||||
timeout = 500;
|
||||
break;
|
||||
case AltosLib.ao_telemetry_rate_9600:
|
||||
timeout = 1000;
|
||||
break;
|
||||
case AltosLib.ao_telemetry_rate_2400:
|
||||
timeout = 2000;
|
||||
break;
|
||||
}
|
||||
flush_input(timeout);
|
||||
} else
|
||||
flush_input(100);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Various command-level operations on
|
||||
* the link
|
||||
*/
|
||||
public boolean monitor_mode = false;
|
||||
public int telemetry = AltosLib.ao_telemetry_standard;
|
||||
public int telemetry_rate = -1;
|
||||
public double frequency;
|
||||
public String callsign;
|
||||
private AltosConfigData config_data_local;
|
||||
private AltosConfigData config_data_remote;
|
||||
|
||||
private Object config_data_lock = new Object();
|
||||
|
||||
private int telemetry_len() {
|
||||
return AltosLib.telemetry_len(telemetry);
|
||||
}
|
||||
|
||||
private void set_radio_freq(int frequency) {
|
||||
if (monitor_mode)
|
||||
printf("m 0\nc F %d\nm %x\n",
|
||||
frequency, telemetry_len());
|
||||
else
|
||||
printf("c F %d\n", frequency);
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public void set_radio_frequency(double frequency,
|
||||
boolean has_frequency,
|
||||
boolean has_setting,
|
||||
int cal) {
|
||||
if (debug)
|
||||
System.out.printf("set_radio_frequency %7.3f (freq %b) (set %b) %d\n", frequency, has_frequency, has_setting, cal);
|
||||
if (frequency == 0 || frequency == AltosLib.MISSING)
|
||||
return;
|
||||
if (has_frequency)
|
||||
set_radio_freq((int) Math.floor (frequency * 1000 + 0.5));
|
||||
else if (has_setting)
|
||||
set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
|
||||
else
|
||||
set_channel(AltosConvert.radio_frequency_to_channel(frequency));
|
||||
}
|
||||
|
||||
public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException {
|
||||
frequency = in_frequency;
|
||||
AltosConfigData config_data = config_data();
|
||||
set_radio_frequency(frequency,
|
||||
config_data.radio_frequency > 0,
|
||||
config_data.radio_setting > 0,
|
||||
config_data.radio_calibration);
|
||||
}
|
||||
|
||||
public void set_telemetry(int in_telemetry) {
|
||||
telemetry = in_telemetry;
|
||||
if (monitor_mode)
|
||||
printf("m 0\nm %x\n", telemetry_len());
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public void set_telemetry_rate(int in_telemetry_rate) {
|
||||
telemetry_rate = in_telemetry_rate;
|
||||
if (monitor_mode)
|
||||
printf("m 0\nc T %d\nm %x\n", telemetry_rate, telemetry_len());
|
||||
else
|
||||
printf("c T %d\n", telemetry_rate);
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public synchronized void set_monitor(boolean monitor) {
|
||||
monitor_mode = monitor;
|
||||
if (monitor)
|
||||
printf("m %x\n", telemetry_len());
|
||||
else
|
||||
printf("m 0\n");
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public synchronized boolean get_monitor() {
|
||||
return monitor_mode;
|
||||
}
|
||||
|
||||
private void set_channel(int channel) {
|
||||
if (monitor_mode)
|
||||
printf("m 0\nc r %d\nm %x\n",
|
||||
channel, telemetry_len());
|
||||
else
|
||||
printf("c r %d\n", channel);
|
||||
flush_output();
|
||||
}
|
||||
|
||||
private void set_radio_setting(int setting) {
|
||||
if (monitor_mode)
|
||||
printf("m 0\nc R %d\nm %x\n",
|
||||
setting, telemetry_len());
|
||||
else
|
||||
printf("c R %d\n", setting);
|
||||
flush_output();
|
||||
}
|
||||
|
||||
public AltosConfigData config_data() throws InterruptedException, TimeoutException {
|
||||
synchronized(config_data_lock) {
|
||||
AltosConfigData config_data;
|
||||
|
||||
if (remote) {
|
||||
if (config_data_remote == null) {
|
||||
printf("m 0\n");
|
||||
config_data_remote = new AltosConfigData(this);
|
||||
if (monitor_mode)
|
||||
set_monitor(true);
|
||||
}
|
||||
config_data = config_data_remote;
|
||||
} else {
|
||||
if (config_data_local == null) {
|
||||
printf("m 0\n");
|
||||
config_data_local = new AltosConfigData(this);
|
||||
if (monitor_mode)
|
||||
set_monitor(true);
|
||||
}
|
||||
config_data = config_data_local;
|
||||
}
|
||||
return config_data;
|
||||
}
|
||||
}
|
||||
|
||||
public void set_callsign(String callsign) {
|
||||
this.callsign = callsign;
|
||||
if (callsign != null) {
|
||||
printf ("c c %s\n", callsign);
|
||||
flush_output();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean is_loader() throws InterruptedException {
|
||||
boolean ret = false;
|
||||
printf("v\n");
|
||||
for (;;) {
|
||||
String line = get_reply();
|
||||
|
||||
if (line == null)
|
||||
return false;
|
||||
if (line.startsWith("software-version"))
|
||||
break;
|
||||
if (line.startsWith("altos-loader"))
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void synchronize(int timeout) throws InterruptedException {
|
||||
printf("v\n");
|
||||
for (;;) {
|
||||
String line = get_reply(timeout);
|
||||
|
||||
if (line == null)
|
||||
break;
|
||||
if (line.startsWith("software-version"))
|
||||
break;
|
||||
if (line.startsWith("altos-loader"))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void to_loader() throws InterruptedException {
|
||||
printf("X\n");
|
||||
flush_output();
|
||||
close();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
|
||||
public boolean remote;
|
||||
public int serial;
|
||||
public String name;
|
||||
|
||||
public void start_remote() throws TimeoutException, InterruptedException {
|
||||
if (frequency == 0.0 || frequency == AltosLib.MISSING)
|
||||
frequency = AltosPreferences.frequency(serial);
|
||||
if (debug)
|
||||
System.out.printf("start remote %7.3f\n", frequency);
|
||||
set_radio_frequency(frequency);
|
||||
if (telemetry_rate < 0)
|
||||
telemetry_rate = AltosPreferences.telemetry_rate(serial);
|
||||
set_telemetry_rate(telemetry_rate);
|
||||
if (callsign == null || callsign.equals(""))
|
||||
callsign = AltosPreferences.callsign();
|
||||
set_callsign(callsign);
|
||||
printf("p\nE 0\n");
|
||||
flush_input();
|
||||
remote = true;
|
||||
}
|
||||
|
||||
public void stop_remote() throws InterruptedException {
|
||||
if (debug)
|
||||
System.out.printf("stop remote\n");
|
||||
try {
|
||||
flush_output();
|
||||
flush_input();
|
||||
} finally {
|
||||
printf ("~\n");
|
||||
flush_output();
|
||||
}
|
||||
remote = false;
|
||||
}
|
||||
|
||||
public int rssi() throws TimeoutException, InterruptedException {
|
||||
if (remote)
|
||||
return 0;
|
||||
printf("s\n");
|
||||
String line = get_reply_no_dialog(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
String[] items = line.split("\\s+");
|
||||
if (items.length < 2)
|
||||
return 0;
|
||||
if (!items[0].equals("RSSI:"))
|
||||
return 0;
|
||||
int rssi = Integer.parseInt(items[1]);
|
||||
return rssi;
|
||||
}
|
||||
|
||||
public String[] adc() throws TimeoutException, InterruptedException {
|
||||
printf("a\n");
|
||||
for (;;) {
|
||||
String line = get_reply_no_dialog(5000);
|
||||
if (line == null) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (!line.startsWith("tick:"))
|
||||
continue;
|
||||
String[] items = line.split("\\s+");
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has_monitor_battery() {
|
||||
try {
|
||||
return config_data().has_monitor_battery();
|
||||
} catch (InterruptedException ie) {
|
||||
return false;
|
||||
} catch (TimeoutException te) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public double monitor_battery() throws InterruptedException {
|
||||
double volts = AltosLib.MISSING;
|
||||
|
||||
try {
|
||||
AltosConfigData config_data = config_data();
|
||||
int monitor_batt = AltosLib.MISSING;
|
||||
|
||||
if (config_data.has_monitor_battery()) {
|
||||
String[] items = adc();
|
||||
for (int i = 0; i < items.length;) {
|
||||
if (items[i].equals("batt")) {
|
||||
monitor_batt = Integer.parseInt(items[i+1]);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (monitor_batt != AltosLib.MISSING) {
|
||||
if (config_data.product.startsWith("TeleBT-v3") || config_data.product.startsWith("TeleBT-v4")) {
|
||||
volts = AltosConvert.tele_bt_3_battery(monitor_batt);
|
||||
} else {
|
||||
volts = AltosConvert.cc_battery_to_voltage(monitor_batt);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
return volts;
|
||||
}
|
||||
|
||||
public AltosLink() {
|
||||
callsign = "";
|
||||
has_error = false;
|
||||
}
|
||||
}
|
||||
33
altoslib/AltosListenerState.java
Normal file
33
altoslib/AltosListenerState.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class AltosListenerState {
|
||||
public int crc_errors;
|
||||
public double battery;
|
||||
public boolean running;
|
||||
|
||||
public AltosListenerState() {
|
||||
crc_errors = 0;
|
||||
battery = AltosLib.MISSING;
|
||||
running = true;
|
||||
}
|
||||
}
|
||||
67
altoslib/AltosLocation.java
Normal file
67
altoslib/AltosLocation.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public abstract class AltosLocation extends AltosUnits {
|
||||
|
||||
public abstract String pos();
|
||||
public abstract String neg();
|
||||
|
||||
public double value(double v, boolean imperial_units) {
|
||||
return v;
|
||||
}
|
||||
|
||||
public double inverse(double v, boolean imperial_units) {
|
||||
return v;
|
||||
}
|
||||
|
||||
public String show_units(boolean imperial_units) {
|
||||
return "°";
|
||||
}
|
||||
|
||||
public String say_units(boolean imperial_units) {
|
||||
return "degrees";
|
||||
}
|
||||
|
||||
public int show_fraction(int width, boolean imperial_units) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public String show(int width, double v, boolean imperial_units) {
|
||||
String h = pos();
|
||||
if (v < 0) {
|
||||
h = neg();
|
||||
v = -v;
|
||||
}
|
||||
int deg = (int) Math.floor(v);
|
||||
double min = (v - Math.floor(v)) * 60.0;
|
||||
return String.format("%s %4d° %9.6f", h, deg, min);
|
||||
}
|
||||
|
||||
public String say(double v, boolean imperial_units) {
|
||||
String h = pos();
|
||||
if (v < 0) {
|
||||
h = neg();
|
||||
v = -v;
|
||||
}
|
||||
int deg = (int) Math.floor(v);
|
||||
double min = (v - Math.floor(v)) * 60.0;
|
||||
return String.format("%s %d degrees %d", h, deg, (int) Math.floor(min + 0.5));
|
||||
}
|
||||
}
|
||||
168
altoslib/AltosLog.java
Normal file
168
altoslib/AltosLog.java
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/*
|
||||
* This creates a thread to capture telemetry data and write it to
|
||||
* a log file
|
||||
*/
|
||||
public class AltosLog implements Runnable {
|
||||
|
||||
LinkedBlockingQueue<AltosLine> input_queue;
|
||||
LinkedBlockingQueue<String> pending_queue;
|
||||
int serial;
|
||||
int flight;
|
||||
int receiver_serial;
|
||||
FileWriter log_file;
|
||||
Thread log_thread;
|
||||
AltosFile file;
|
||||
AltosLink link;
|
||||
AltosLogTrace trace;
|
||||
|
||||
private void trace(String format, Object ... arguments) {
|
||||
if (trace != null)
|
||||
trace.trace(format, arguments);
|
||||
}
|
||||
|
||||
private void close_log_file() {
|
||||
if (log_file != null) {
|
||||
try {
|
||||
log_file.close();
|
||||
} catch (IOException io) {
|
||||
}
|
||||
log_file = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
link.remove_monitor(input_queue);
|
||||
close_log_file();
|
||||
if (log_thread != null) {
|
||||
log_thread.interrupt();
|
||||
log_thread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public File file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
boolean open (AltosCalData cal_data) throws IOException, InterruptedException {
|
||||
trace("open serial %d flight %d receiver_serial %d",
|
||||
cal_data.serial,
|
||||
cal_data.flight,
|
||||
cal_data.receiver_serial);
|
||||
|
||||
AltosFile a = new AltosFile(cal_data);
|
||||
|
||||
trace("open file %s\n", a.getPath());
|
||||
|
||||
try {
|
||||
log_file = new FileWriter(a, true);
|
||||
} catch (IOException ie) {
|
||||
log_file = null;
|
||||
trace("open file failed\n");
|
||||
if (trace != null)
|
||||
trace.open_failed(a);
|
||||
}
|
||||
if (log_file != null) {
|
||||
trace("open file success\n");
|
||||
while (!pending_queue.isEmpty()) {
|
||||
String s = pending_queue.take();
|
||||
log_file.write(s);
|
||||
log_file.write('\n');
|
||||
}
|
||||
log_file.flush();
|
||||
file = a;
|
||||
AltosPreferences.set_logfile(link.serial, file);
|
||||
}
|
||||
return log_file != null;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
trace("log run start\n");
|
||||
try {
|
||||
AltosConfigData receiver_config = link.config_data();
|
||||
AltosCalData cal_data = new AltosCalData();
|
||||
AltosState state = null;
|
||||
cal_data.set_receiver_serial(receiver_config.serial);
|
||||
for (;;) {
|
||||
AltosLine line = input_queue.take();
|
||||
if (line.line == null)
|
||||
continue;
|
||||
try {
|
||||
AltosTelemetry telem = AltosTelemetry.parse(line.line);
|
||||
if (state == null)
|
||||
state = new AltosState(cal_data);
|
||||
telem.provide_data(state);
|
||||
|
||||
if (cal_data.serial != serial ||
|
||||
cal_data.flight != flight ||
|
||||
log_file == null)
|
||||
{
|
||||
close_log_file();
|
||||
serial = cal_data.serial;
|
||||
flight = cal_data.flight;
|
||||
state = null;
|
||||
if (cal_data.serial != AltosLib.MISSING &&
|
||||
cal_data.flight != AltosLib.MISSING)
|
||||
open(cal_data);
|
||||
}
|
||||
} catch (ParseException pe) {
|
||||
} catch (AltosCRCException ce) {
|
||||
}
|
||||
if (log_file != null) {
|
||||
log_file.write(line.line);
|
||||
log_file.write('\n');
|
||||
log_file.flush();
|
||||
} else
|
||||
pending_queue.put(line.line);
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
trace("interrupted exception\n");
|
||||
} catch (TimeoutException te) {
|
||||
trace("timeout exception\n");
|
||||
} catch (IOException ie) {
|
||||
trace("io exception %s message %s\n", ie.toString(), ie.getMessage());
|
||||
}
|
||||
trace("log run done\n");
|
||||
close();
|
||||
}
|
||||
|
||||
public AltosLog (AltosLink link, AltosLogTrace trace) {
|
||||
pending_queue = new LinkedBlockingQueue<String> ();
|
||||
input_queue = new LinkedBlockingQueue<AltosLine> ();
|
||||
link.add_monitor(input_queue);
|
||||
serial = -1;
|
||||
flight = -1;
|
||||
this.trace = trace;
|
||||
this.link = link;
|
||||
log_file = null;
|
||||
log_thread = new Thread(this);
|
||||
log_thread.start();
|
||||
}
|
||||
|
||||
public AltosLog (AltosLink link) {
|
||||
this(link, null);
|
||||
}
|
||||
}
|
||||
28
altoslib/AltosLogTrace.java
Normal file
28
altoslib/AltosLogTrace.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2021 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 java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
public interface AltosLogTrace {
|
||||
public abstract void trace(String format, Object ... arguments);
|
||||
|
||||
public abstract void open_failed(File path);
|
||||
}
|
||||
24
altoslib/AltosLongitude.java
Normal file
24
altoslib/AltosLongitude.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosLongitude extends AltosLocation {
|
||||
public String pos() { return "E"; }
|
||||
public String neg() { return "W"; }
|
||||
}
|
||||
135
altoslib/AltosMag.java
Normal file
135
altoslib/AltosMag.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
|
||||
public class AltosMag implements Cloneable {
|
||||
public int along = AltosLib.MISSING;
|
||||
public int across = AltosLib.MISSING;
|
||||
public int through = AltosLib.MISSING;
|
||||
|
||||
public static final int model_hmc5883 = 0;
|
||||
public static final int model_mmc5983 = 1;
|
||||
|
||||
public int mag_model = AltosLib.MISSING;
|
||||
|
||||
public static final double counts_per_gauss_hmc5883 = 1090;
|
||||
public static final double counts_per_gauss_mmc5983 = 4096;
|
||||
|
||||
public static double counts_per_gauss(int imu_type, int mag_model) {
|
||||
switch(mag_model) {
|
||||
case AltosLib.model_hmc5883:
|
||||
return counts_per_gauss_hmc5883;
|
||||
case AltosLib.model_mmc5983:
|
||||
return counts_per_gauss_mmc5983;
|
||||
}
|
||||
switch (imu_type) {
|
||||
case AltosIMU.imu_type_telemega_v1_v2:
|
||||
case AltosIMU.imu_type_easymega_v1:
|
||||
return counts_per_gauss_hmc5883;
|
||||
case AltosIMU.imu_type_easytimer_v2:
|
||||
return counts_per_gauss_mmc5983;
|
||||
}
|
||||
|
||||
return AltosIMU.counts_per_gauss(imu_type, mag_model);
|
||||
}
|
||||
|
||||
public static double convert_gauss(double counts, int imu_type, int mag_model) {
|
||||
double cpg = counts_per_gauss(imu_type, mag_model);
|
||||
|
||||
if (cpg == AltosLib.MISSING)
|
||||
return AltosLib.MISSING;
|
||||
|
||||
return counts / cpg;
|
||||
}
|
||||
|
||||
public boolean parse_string(String line) {
|
||||
|
||||
if (line.startsWith("X:")) {
|
||||
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
mag_model = model_hmc5883;
|
||||
|
||||
if (items.length >= 6) {
|
||||
across = Integer.parseInt(items[1]);
|
||||
through = Integer.parseInt(items[3]);
|
||||
along = Integer.parseInt(items[5]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (line.startsWith("MMC5983:")) {
|
||||
String[] items = line.split("\\s+");
|
||||
|
||||
mag_model = model_mmc5983;
|
||||
|
||||
if (items.length >= 4) {
|
||||
along = Integer.parseInt(items[1]);
|
||||
across = Integer.parseInt(items[2]);
|
||||
through = Integer.parseInt(items[3]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public AltosMag clone() {
|
||||
AltosMag n = new AltosMag();
|
||||
|
||||
n.along = along;
|
||||
n.across = across;
|
||||
n.through = through;
|
||||
return n;
|
||||
}
|
||||
|
||||
public AltosMag() {
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
|
||||
try {
|
||||
AltosMag mag = new AltosMag(link);
|
||||
AltosCalData cal_data = listener.cal_data();
|
||||
|
||||
if (mag != null) {
|
||||
if (mag.mag_model != AltosLib.MISSING)
|
||||
cal_data.set_mag_model(mag.mag_model);
|
||||
listener.set_mag(cal_data.mag_along(mag.along),
|
||||
cal_data.mag_across(mag.across),
|
||||
cal_data.mag_through(mag.through));
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMag(AltosLink link) throws InterruptedException, TimeoutException {
|
||||
this();
|
||||
link.printf("M\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
if (parse_string(line))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
524
altoslib/AltosMap.java
Normal file
524
altoslib/AltosMap.java
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
|
||||
|
||||
public static final int px_size = 512;
|
||||
|
||||
public static final int maptype_hybrid = 0;
|
||||
public static final int maptype_roadmap = 1;
|
||||
public static final int maptype_satellite = 2;
|
||||
public static final int maptype_terrain = 3;
|
||||
public static final int maptype_default = maptype_hybrid;
|
||||
|
||||
public static final int default_zoom = 15;
|
||||
public static final int min_zoom = 3;
|
||||
public static final int max_zoom = 21;
|
||||
|
||||
public static final String[] maptype_names = {
|
||||
"hybrid",
|
||||
"roadmap",
|
||||
"satellite",
|
||||
"terrain"
|
||||
};
|
||||
|
||||
public static final String[] maptype_labels = {
|
||||
"Hybrid",
|
||||
"Roadmap",
|
||||
"Satellite",
|
||||
"Terrain"
|
||||
};
|
||||
|
||||
AltosMapInterface map_interface;
|
||||
int scale;
|
||||
|
||||
AltosMapCache cache;
|
||||
|
||||
public AltosMapCache cache() { return cache; }
|
||||
|
||||
LinkedList<AltosMapMark> marks = new LinkedList<AltosMapMark>();
|
||||
|
||||
AltosMapPath path;
|
||||
AltosMapLine line;
|
||||
public AltosLatLon last_position;
|
||||
|
||||
boolean have_boost = false;
|
||||
boolean have_landed = false;
|
||||
|
||||
ConcurrentHashMap<AltosPointInt,AltosMapTile> tiles = new ConcurrentHashMap<AltosPointInt,AltosMapTile>();
|
||||
int load_radius;
|
||||
AltosLatLon load_centre = null;
|
||||
AltosMapTileListener load_listener;
|
||||
|
||||
int zoom = AltosMap.default_zoom;
|
||||
int maptype = AltosMap.maptype_default;
|
||||
|
||||
long user_input_time;
|
||||
|
||||
/* Milliseconds to wait after user action before auto-scrolling
|
||||
*/
|
||||
static final long auto_scroll_delay = 20 * 1000;
|
||||
|
||||
public AltosMapTransform transform;
|
||||
public AltosLatLon centre;
|
||||
|
||||
public void reset() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/* MapInterface wrapping functions */
|
||||
|
||||
public void repaint(int x, int y, int w, int h) {
|
||||
map_interface.repaint(new AltosRectangle(x, y, w, h));
|
||||
}
|
||||
|
||||
public void repaint(AltosMapRectangle damage, int pad) {
|
||||
AltosRectangle r = transform.screen(damage);
|
||||
repaint(r.x - pad, r.y - pad, r.width + pad * 2, r.height + pad * 2);
|
||||
}
|
||||
|
||||
public void repaint() {
|
||||
map_interface.repaint();
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return map_interface.width();
|
||||
}
|
||||
|
||||
public int height() {
|
||||
return map_interface.height();
|
||||
}
|
||||
|
||||
public void debug(String format, Object ... arguments) {
|
||||
map_interface.debug(format, arguments);
|
||||
}
|
||||
|
||||
static public AltosPointInt floor(AltosPointDouble point) {
|
||||
return new AltosPointInt ((int) Math.floor(point.x / AltosMap.px_size) * AltosMap.px_size,
|
||||
(int) Math.floor(point.y / AltosMap.px_size) * AltosMap.px_size);
|
||||
}
|
||||
|
||||
static public AltosPointInt ceil(AltosPointDouble point) {
|
||||
return new AltosPointInt ((int) Math.ceil(point.x / AltosMap.px_size) * AltosMap.px_size,
|
||||
(int) Math.ceil(point.y / AltosMap.px_size) * AltosMap.px_size);
|
||||
}
|
||||
|
||||
public void notice_user_input() {
|
||||
user_input_time = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean recent_user_input() {
|
||||
return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
|
||||
}
|
||||
|
||||
public boolean has_centre() {
|
||||
return centre != null;
|
||||
}
|
||||
|
||||
public boolean far_from_centre(AltosLatLon lat_lon) {
|
||||
|
||||
if (centre == null || transform == null)
|
||||
return true;
|
||||
|
||||
AltosPointDouble screen = transform.screen(lat_lon);
|
||||
|
||||
int width = width();
|
||||
int dx = Math.abs ((int) (double) screen.x - width/2);
|
||||
|
||||
if (dx > width / 4)
|
||||
return true;
|
||||
|
||||
int height = height();
|
||||
int dy = Math.abs ((int) (double) screen.y - height/2);
|
||||
|
||||
if (dy > height / 4)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void set_transform() {
|
||||
if (centre != null) {
|
||||
transform = new AltosMapTransform(width(), height(), zoom, centre);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void set_zoom_label() {
|
||||
map_interface.set_zoom_label(String.format("Zoom %d", get_zoom() - default_zoom));
|
||||
}
|
||||
|
||||
|
||||
public boolean set_zoom(int zoom) {
|
||||
notice_user_input();
|
||||
if (AltosMap.min_zoom <= zoom && zoom <= AltosMap.max_zoom && zoom != this.zoom) {
|
||||
this.zoom = zoom;
|
||||
tiles.clear();
|
||||
set_transform();
|
||||
set_zoom_label();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean set_zoom_centre(int zoom, AltosPointInt centre) {
|
||||
AltosLatLon mouse_lat_lon = null;
|
||||
boolean ret;
|
||||
|
||||
if (transform != null)
|
||||
mouse_lat_lon = transform.screen_lat_lon(centre);
|
||||
|
||||
ret = set_zoom(zoom);
|
||||
|
||||
if (ret && mouse_lat_lon != null) {
|
||||
AltosPointDouble new_mouse = transform.screen(mouse_lat_lon);
|
||||
|
||||
double dx = width()/2.0 - centre.x;
|
||||
double dy = height()/2.0 - centre.y;
|
||||
|
||||
AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointDouble(new_mouse.x + dx, new_mouse.y + dy));
|
||||
|
||||
centre(new_centre);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int get_zoom() {
|
||||
return zoom;
|
||||
}
|
||||
|
||||
public boolean set_maptype(int maptype) {
|
||||
/*
|
||||
if (maptype != this.maptype) {
|
||||
this.maptype = maptype;
|
||||
tiles.clear();
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
public void show(AltosGPS gps, double time, int state, double gps_height) {
|
||||
/*
|
||||
* If insufficient gps data, nothing to update
|
||||
*/
|
||||
|
||||
if (gps == null)
|
||||
return;
|
||||
|
||||
if (!gps.locked && gps.nsat < 4)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
case AltosLib.ao_flight_boost:
|
||||
if (!have_boost) {
|
||||
add_mark(gps.lat, gps.lon, state);
|
||||
have_boost = true;
|
||||
}
|
||||
break;
|
||||
case AltosLib.ao_flight_landed:
|
||||
if (!have_landed) {
|
||||
add_mark(gps.lat, gps.lon, state);
|
||||
have_landed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
AltosMapRectangle damage = path.add(gps, time, state, gps_height);
|
||||
|
||||
if (damage != null)
|
||||
repaint(damage, AltosMapPath.stroke_width);
|
||||
}
|
||||
|
||||
last_position = new AltosLatLon(gps.lat, gps.lon);
|
||||
|
||||
maybe_centre(gps.lat, gps.lon);
|
||||
}
|
||||
|
||||
public void show(AltosState state, AltosListenerState listener_state) {
|
||||
show(state.gps, state.time, state.state(), state.gps_height());
|
||||
}
|
||||
|
||||
public void centre(AltosLatLon lat_lon) {
|
||||
centre = lat_lon;
|
||||
set_transform();
|
||||
}
|
||||
|
||||
public void centre(double lat, double lon) {
|
||||
centre(new AltosLatLon(lat, lon));
|
||||
}
|
||||
|
||||
public void centre(AltosGPS gps) {
|
||||
if (!gps.locked && gps.nsat < 4)
|
||||
return;
|
||||
centre(gps.lat, gps.lon);
|
||||
}
|
||||
|
||||
public void centre(AltosState state) {
|
||||
centre(state.gps);
|
||||
}
|
||||
|
||||
public void maybe_centre(double lat, double lon) {
|
||||
AltosLatLon lat_lon = new AltosLatLon(lat, lon);
|
||||
if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
|
||||
centre(lat_lon);
|
||||
}
|
||||
|
||||
public AltosMapMark add_mark(double lat, double lon, int state, String label) {
|
||||
AltosMapMark mark;
|
||||
synchronized(marks) {
|
||||
mark = map_interface.new_mark(lat, lon, state, label);
|
||||
if (mark != null)
|
||||
marks.add(mark);
|
||||
}
|
||||
repaint();
|
||||
return mark;
|
||||
}
|
||||
|
||||
public AltosMapMark add_mark(double lat, double lon, int state) {
|
||||
return add_mark(lat, lon, state, null);
|
||||
}
|
||||
|
||||
public void del_mark(AltosMapMark mark) {
|
||||
marks.remove(mark);
|
||||
}
|
||||
|
||||
public void clear_marks() {
|
||||
synchronized(marks) {
|
||||
marks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void make_tiles() {
|
||||
AltosPointInt upper_left;
|
||||
AltosPointInt lower_right;
|
||||
|
||||
if (load_centre != null) {
|
||||
AltosPointInt centre = floor(transform.point(load_centre));
|
||||
|
||||
upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
|
||||
centre.y - load_radius * AltosMap.px_size);
|
||||
lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
|
||||
centre.y + load_radius * AltosMap.px_size);
|
||||
} else {
|
||||
upper_left = floor(transform.screen_point(new AltosPointInt(0, 0)));
|
||||
lower_right = floor(transform.screen_point(new AltosPointInt(width(), height())));
|
||||
}
|
||||
|
||||
Enumeration<AltosPointInt> keyEnumeration = tiles.keys();
|
||||
|
||||
while (keyEnumeration.hasMoreElements()) {
|
||||
AltosPointInt point = keyEnumeration.nextElement();
|
||||
if (point.x < upper_left.x || lower_right.x < point.x ||
|
||||
point.y < upper_left.y || lower_right.y < point.y) {
|
||||
tiles.remove(point);
|
||||
}
|
||||
}
|
||||
|
||||
cache.set_cache_size((width() / AltosMap.px_size + 2) * (height() / AltosMap.px_size + 2));
|
||||
|
||||
for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
|
||||
for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
|
||||
AltosPointInt point = new AltosPointInt(x, y);
|
||||
|
||||
if (!tiles.containsKey(point)) {
|
||||
AltosLatLon ul = transform.lat_lon(point);
|
||||
AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
|
||||
AltosMapTile tile = map_interface.new_tile(cache, ul, center, zoom, maptype, px_size, scale);
|
||||
int status = tile.store.status();
|
||||
if (status == AltosMapTile.fetching)
|
||||
debug("Fetching %.6f %.6f %d\n", center.lat, center.lon, zoom);
|
||||
tile.add_listener(this);
|
||||
tiles.put(point, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_load_params(int new_zoom, int new_type, double lat, double lon, int radius, AltosMapTileListener listener) {
|
||||
if (AltosMap.min_zoom <= new_zoom && new_zoom <= AltosMap.max_zoom)
|
||||
zoom = new_zoom;
|
||||
/* maptype = new_type; */
|
||||
load_centre = new AltosLatLon(lat, lon);
|
||||
load_radius = radius;
|
||||
load_listener = listener;
|
||||
centre(lat, lon);
|
||||
tiles.clear();
|
||||
make_tiles();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Map";
|
||||
}
|
||||
|
||||
public void paint() {
|
||||
if (centre != null)
|
||||
make_tiles();
|
||||
|
||||
if (transform == null)
|
||||
return;
|
||||
|
||||
for (AltosMapTile tile : tiles.values())
|
||||
tile.paint(transform);
|
||||
|
||||
synchronized(marks) {
|
||||
for (AltosMapMark mark : marks)
|
||||
mark.paint(transform);
|
||||
}
|
||||
|
||||
if (path != null)
|
||||
path.paint(transform);
|
||||
|
||||
if (line != null)
|
||||
line.paint(transform);
|
||||
}
|
||||
|
||||
/* AltosMapTileListener methods */
|
||||
public synchronized void notify_tile(AltosMapTile tile, int status) {
|
||||
Enumeration<AltosPointInt> keyEnumeration = tiles.keys();
|
||||
|
||||
while (keyEnumeration.hasMoreElements()) {
|
||||
AltosPointInt point = keyEnumeration.nextElement();
|
||||
if (tile == tiles.get(point)) {
|
||||
AltosPointInt screen = transform.screen(point);
|
||||
repaint(screen.x, screen.y, AltosMap.px_size, AltosMap.px_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* AltosMapStoreListener methods */
|
||||
public synchronized void notify_store(AltosMapStore store, int status) {
|
||||
if (load_listener != null) {
|
||||
for (AltosMapTile tile : tiles.values())
|
||||
if (store.equals(tile.store))
|
||||
load_listener.notify_tile(tile, status);
|
||||
}
|
||||
}
|
||||
|
||||
/* UI elements */
|
||||
|
||||
AltosPointInt drag_start;
|
||||
|
||||
boolean dragged;
|
||||
|
||||
static final double drag_far = 20;
|
||||
|
||||
private void drag(int x, int y) {
|
||||
if (drag_start == null)
|
||||
return;
|
||||
|
||||
int dx = x - drag_start.x;
|
||||
int dy = y - drag_start.y;
|
||||
|
||||
double distance = Math.hypot(dx, dy);
|
||||
|
||||
if (distance > drag_far)
|
||||
dragged = true;
|
||||
|
||||
if (transform == null)
|
||||
return;
|
||||
|
||||
AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointInt(width() / 2 - dx, height() / 2 - dy));
|
||||
centre(new_centre);
|
||||
drag_start = new AltosPointInt(x, y);
|
||||
}
|
||||
|
||||
private void drag_start(int x, int y) {
|
||||
drag_start = new AltosPointInt(x, y);
|
||||
dragged = false;
|
||||
}
|
||||
|
||||
private void drag_stop(int x, int y) {
|
||||
if (!dragged) {
|
||||
if (transform == null) {
|
||||
return;
|
||||
}
|
||||
map_interface.select_object (transform.screen_lat_lon(new AltosPointInt(x,y)));
|
||||
}
|
||||
}
|
||||
|
||||
private void line_start(int x, int y) {
|
||||
if (line != null && transform != null) {
|
||||
line.pressed(new AltosPointInt(x, y), transform);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void line(int x, int y) {
|
||||
if (line != null && transform != null) {
|
||||
line.dragged(new AltosPointInt(x, y), transform);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void touch_start(int x, int y, boolean is_drag) {
|
||||
notice_user_input();
|
||||
if (is_drag)
|
||||
drag_start(x, y);
|
||||
else
|
||||
line_start(x, y);
|
||||
}
|
||||
|
||||
public void touch_continue(int x, int y, boolean is_drag) {
|
||||
notice_user_input();
|
||||
if (is_drag)
|
||||
drag(x, y);
|
||||
else
|
||||
line(x, y);
|
||||
}
|
||||
|
||||
public void touch_stop(int x, int y, boolean is_drag) {
|
||||
notice_user_input();
|
||||
if (is_drag)
|
||||
drag_stop(x, y);
|
||||
}
|
||||
|
||||
public AltosMapPathPoint nearest(int x, int y) {
|
||||
notice_user_input();
|
||||
if (path == null)
|
||||
return null;
|
||||
if (transform == null)
|
||||
return null;
|
||||
AltosLatLon at = transform.screen_lat_lon(new AltosPointInt(x, y));
|
||||
return path.nearest(at);
|
||||
}
|
||||
|
||||
public AltosMap(AltosMapInterface map_interface, int scale) {
|
||||
this.map_interface = map_interface;
|
||||
this.scale = scale;
|
||||
cache = new AltosMapCache(map_interface);
|
||||
line = map_interface.new_line();
|
||||
path = map_interface.new_path();
|
||||
set_zoom_label();
|
||||
}
|
||||
|
||||
public AltosMap(AltosMapInterface map_interface) {
|
||||
this(map_interface, 1);
|
||||
}
|
||||
}
|
||||
171
altoslib/AltosMapCache.java
Normal file
171
altoslib/AltosMapCache.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
public class AltosMapCache implements AltosMapCacheListener {
|
||||
|
||||
/* An entry in the MapCache */
|
||||
class MapCacheElement implements AltosMapTileListener {
|
||||
|
||||
AltosMapTile tile; /* Notify when image has been loaded */
|
||||
AltosImage image;
|
||||
long used;
|
||||
|
||||
class loader implements Runnable {
|
||||
public void run() {
|
||||
if (image == null) {
|
||||
try {
|
||||
image = map_interface.load_image(tile.store.file);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
tile.notify_image(image);
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
loader l = new loader();
|
||||
Thread lt = new Thread(l);
|
||||
lt.start();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
if (image != null) {
|
||||
image.flush();
|
||||
image = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has_map() {
|
||||
return tile.status == AltosMapTile.loaded;
|
||||
}
|
||||
|
||||
public synchronized void notify_tile(AltosMapTile tile, int status) {
|
||||
if (status == AltosMapTile.fetched) {
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
public MapCacheElement(AltosMapTile tile) {
|
||||
this.tile = tile;
|
||||
this.image = null;
|
||||
this.used = 0;
|
||||
tile.add_listener(this);
|
||||
}
|
||||
}
|
||||
|
||||
int min_cache_size; /* configured minimum cache size */
|
||||
int cache_size; /* current cache size */
|
||||
int requested_cache_size; /* cache size computed by application */
|
||||
|
||||
private Object fetch_lock = new Object();
|
||||
private Object cache_lock = new Object();
|
||||
|
||||
AltosMapInterface map_interface;
|
||||
|
||||
MapCacheElement[] elements = new MapCacheElement[cache_size];
|
||||
|
||||
long used;
|
||||
|
||||
public void set_cache_size(int new_size) {
|
||||
|
||||
requested_cache_size = new_size;
|
||||
|
||||
if (new_size < min_cache_size)
|
||||
new_size = min_cache_size;
|
||||
|
||||
if (new_size == cache_size)
|
||||
return;
|
||||
|
||||
synchronized(cache_lock) {
|
||||
MapCacheElement[] new_elements = new MapCacheElement[new_size];
|
||||
|
||||
for (int i = 0; i < cache_size; i++) {
|
||||
if (i < new_size)
|
||||
new_elements[i] = elements[i];
|
||||
else if (elements[i] != null)
|
||||
elements[i].flush();
|
||||
}
|
||||
elements = new_elements;
|
||||
cache_size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
public AltosImage get(AltosMapTile tile) {
|
||||
int oldest = -1;
|
||||
long age = used;
|
||||
|
||||
synchronized(cache_lock) {
|
||||
MapCacheElement element = null;
|
||||
for (int i = 0; i < cache_size; i++) {
|
||||
element = elements[i];
|
||||
|
||||
if (element == null) {
|
||||
oldest = i;
|
||||
break;
|
||||
}
|
||||
if (tile.store.equals(element.tile.store)) {
|
||||
element.used = used++;
|
||||
return element.image;
|
||||
}
|
||||
if (element.used < age) {
|
||||
oldest = i;
|
||||
age = element.used;
|
||||
}
|
||||
}
|
||||
|
||||
element = new MapCacheElement(tile);
|
||||
element.used = used++;
|
||||
if (elements[oldest] != null)
|
||||
elements[oldest].flush();
|
||||
|
||||
elements[oldest] = element;
|
||||
return element.image;
|
||||
}
|
||||
}
|
||||
|
||||
public void map_cache_changed(int map_cache) {
|
||||
min_cache_size = map_cache;
|
||||
|
||||
set_cache_size(requested_cache_size);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
AltosPreferences.unregister_map_cache_listener(this);
|
||||
|
||||
for (int i = 0; i < cache_size; i++) {
|
||||
MapCacheElement element = elements[i];
|
||||
|
||||
if (element != null)
|
||||
element.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMapCache(AltosMapInterface map_interface) {
|
||||
this.map_interface = map_interface;
|
||||
min_cache_size = AltosPreferences.map_cache();
|
||||
|
||||
set_cache_size(0);
|
||||
|
||||
AltosPreferences.register_map_cache_listener(this);
|
||||
}
|
||||
}
|
||||
23
altoslib/AltosMapCacheListener.java
Normal file
23
altoslib/AltosMapCacheListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosMapCacheListener {
|
||||
public void map_cache_changed(int map_cache);
|
||||
}
|
||||
50
altoslib/AltosMapInterface.java
Normal file
50
altoslib/AltosMapInterface.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
public interface AltosMapInterface {
|
||||
public abstract AltosMapPath new_path();
|
||||
|
||||
public abstract AltosMapLine new_line();
|
||||
|
||||
public abstract AltosImage load_image(File file) throws Exception;
|
||||
|
||||
public abstract AltosMapMark new_mark(double lat, double lon, int state);
|
||||
|
||||
public abstract AltosMapMark new_mark(double lat, double lon, int state, String label);
|
||||
|
||||
public abstract AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale);
|
||||
|
||||
public abstract int width();
|
||||
|
||||
public abstract int height();
|
||||
|
||||
public abstract void repaint();
|
||||
|
||||
public abstract void repaint(AltosRectangle damage);
|
||||
|
||||
public abstract void set_zoom_label(String label);
|
||||
|
||||
public abstract void debug(String format, Object ... arguments);
|
||||
|
||||
public abstract void select_object(AltosLatLon latlon);
|
||||
}
|
||||
52
altoslib/AltosMapLine.java
Normal file
52
altoslib/AltosMapLine.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AltosMapLine {
|
||||
public AltosLatLon start, end;
|
||||
|
||||
static public int stroke_width = 6;
|
||||
|
||||
public abstract void paint(AltosMapTransform t);
|
||||
|
||||
private AltosLatLon lat_lon(AltosPointInt pt, AltosMapTransform t) {
|
||||
return t.screen_lat_lon(pt);
|
||||
}
|
||||
|
||||
public void dragged(AltosPointInt pt, AltosMapTransform t) {
|
||||
end = lat_lon(pt, t);
|
||||
}
|
||||
|
||||
public void pressed(AltosPointInt pt, AltosMapTransform t) {
|
||||
start = lat_lon(pt, t);
|
||||
end = null;
|
||||
}
|
||||
|
||||
public String line_dist() {
|
||||
AltosGreatCircle g = new AltosGreatCircle(start.lat, start.lon,
|
||||
end.lat, end.lon);
|
||||
|
||||
return AltosConvert.distance.show(7, g.distance);
|
||||
}
|
||||
}
|
||||
225
altoslib/AltosMapLoader.java
Normal file
225
altoslib/AltosMapLoader.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.text.*;
|
||||
import java.lang.Math;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
public class AltosMapLoader extends Thread implements AltosMapStoreListener {
|
||||
AltosMapLoaderListener listener;
|
||||
|
||||
double latitude, longitude;
|
||||
int min_z;
|
||||
int max_z;
|
||||
int cur_z;
|
||||
int all_types;
|
||||
int cur_type;
|
||||
double radius;
|
||||
int scale;
|
||||
|
||||
int tiles_loaded_layer;
|
||||
int tiles_loaded_total;
|
||||
int tiles_this_layer;
|
||||
int tiles_total;
|
||||
int layers_total;
|
||||
int layers_loaded;
|
||||
|
||||
private static final int MAX_LOADING = 200;
|
||||
|
||||
private Semaphore loading = new Semaphore(MAX_LOADING);
|
||||
|
||||
boolean abort;
|
||||
|
||||
int tile_radius(int zoom) {
|
||||
double delta_lon = AltosMapTransform.lon_from_distance(latitude, radius);
|
||||
|
||||
AltosMapTransform t = new AltosMapTransform(256, 256, zoom + AltosMap.default_zoom, new AltosLatLon(latitude, longitude));
|
||||
|
||||
AltosPointDouble center = t.point(new AltosLatLon(latitude, longitude));
|
||||
AltosPointDouble edge = t.point(new AltosLatLon(latitude, longitude + delta_lon));
|
||||
|
||||
int tile_radius = (int) Math.ceil(Math.abs(center.x - edge.x) / AltosMap.px_size);
|
||||
|
||||
return tile_radius;
|
||||
}
|
||||
|
||||
int tiles_per_layer(int zoom) {
|
||||
int tile_radius = tile_radius(zoom);
|
||||
return (tile_radius * 2 + 1) * (tile_radius * 2 + 1);
|
||||
}
|
||||
|
||||
private boolean do_load() {
|
||||
tiles_this_layer = tiles_per_layer(cur_z);
|
||||
tiles_loaded_layer = 0;
|
||||
listener.debug("tiles_this_layer %d (zoom %d)\n", tiles_this_layer, cur_z);
|
||||
|
||||
int load_radius = tile_radius(cur_z);
|
||||
int zoom = cur_z + AltosMap.default_zoom;
|
||||
int maptype = cur_type;
|
||||
AltosLatLon load_centre = new AltosLatLon(latitude, longitude);
|
||||
AltosMapTransform transform = new AltosMapTransform(256, 256, zoom, load_centre);
|
||||
|
||||
AltosPointInt upper_left;
|
||||
AltosPointInt lower_right;
|
||||
|
||||
AltosPointInt centre = AltosMap.floor(transform.point(load_centre));
|
||||
|
||||
upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
|
||||
centre.y - load_radius * AltosMap.px_size);
|
||||
lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
|
||||
centre.y + load_radius * AltosMap.px_size);
|
||||
|
||||
|
||||
for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
|
||||
for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
|
||||
try {
|
||||
loading.acquire();
|
||||
} catch (InterruptedException ie) {
|
||||
return false;
|
||||
}
|
||||
AltosPointInt point = new AltosPointInt(x, y);
|
||||
AltosLatLon ul = transform.lat_lon(point);
|
||||
AltosLatLon center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
|
||||
AltosMapStore store = AltosMapStore.get(center, zoom, maptype, AltosMap.px_size, scale);
|
||||
listener.debug("load state %s url %s\n", AltosMapTile.status_name(store.status()), store.url);
|
||||
store.add_listener(this);
|
||||
if (abort)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private int next_type(int start) {
|
||||
int next_type;
|
||||
for (next_type = start;
|
||||
next_type <= AltosMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
|
||||
next_type++)
|
||||
;
|
||||
return next_type;
|
||||
}
|
||||
|
||||
private boolean next_load() {
|
||||
int next_type = next_type(cur_type + 1);
|
||||
|
||||
if (next_type > AltosMap.maptype_terrain) {
|
||||
if (cur_z == max_z) {
|
||||
return false;
|
||||
} else {
|
||||
cur_z++;
|
||||
}
|
||||
next_type = next_type(0);
|
||||
}
|
||||
cur_type = next_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
|
||||
cur_z = min_z;
|
||||
int ntype = 0;
|
||||
|
||||
for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
|
||||
if ((all_types & (1 << t)) != 0)
|
||||
ntype++;
|
||||
if (ntype == 0) {
|
||||
all_types = (1 << AltosMap.maptype_hybrid);
|
||||
ntype = 1;
|
||||
}
|
||||
|
||||
cur_type = next_type(0);
|
||||
|
||||
tiles_total = 0;
|
||||
for (int z = min_z; z <= max_z; z++)
|
||||
tiles_total += tiles_per_layer(z) * ntype;
|
||||
|
||||
layers_total = (max_z - min_z + 1) * ntype;
|
||||
layers_loaded = 0;
|
||||
tiles_loaded_total = 0;
|
||||
|
||||
listener.debug("total tiles %d layers %d\n", tiles_total, layers_total);
|
||||
|
||||
listener.loader_start(tiles_total);
|
||||
do {
|
||||
if (!do_load())
|
||||
break;
|
||||
} while (next_load());
|
||||
if (abort)
|
||||
listener.loader_done(tiles_total);
|
||||
}
|
||||
|
||||
public synchronized void notify_store(AltosMapStore store, int status) {
|
||||
boolean do_next = false;
|
||||
if (status == AltosMapTile.fetching)
|
||||
return;
|
||||
|
||||
loading.release();
|
||||
|
||||
store.remove_listener(this);
|
||||
|
||||
if (layers_loaded >= layers_total)
|
||||
return;
|
||||
|
||||
++tiles_loaded_total;
|
||||
++tiles_loaded_layer;
|
||||
|
||||
listener.debug("AltosMapLoader.notify_store status %d total %d of %d layer %d of %d\n",
|
||||
status, tiles_loaded_total, tiles_total, tiles_loaded_layer, tiles_this_layer);
|
||||
|
||||
if (tiles_loaded_layer == tiles_this_layer) {
|
||||
++layers_loaded;
|
||||
listener.debug("%d layers loaded\n", layers_loaded);
|
||||
do_next = true;
|
||||
}
|
||||
|
||||
if (tiles_loaded_total == tiles_total)
|
||||
listener.loader_done(tiles_total);
|
||||
else
|
||||
listener.loader_notify(tiles_loaded_total,
|
||||
tiles_total, store.file.toString());
|
||||
}
|
||||
|
||||
public void abort() {
|
||||
this.abort = true;
|
||||
}
|
||||
|
||||
public AltosMapLoader(AltosMapLoaderListener listener,
|
||||
double latitude, double longitude, int min_z, int max_z, double radius, int all_types, int scale) {
|
||||
listener.debug("lat %f lon %f min_z %d max_z %d radius %f all_types %d\n",
|
||||
latitude, longitude, min_z, max_z, radius, all_types);
|
||||
this.listener = listener;
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.min_z = min_z;
|
||||
this.max_z = max_z;
|
||||
this.radius = radius;
|
||||
/*
|
||||
this.all_types = all_types;
|
||||
*/
|
||||
this.all_types = 1 << AltosMap.maptype_hybrid;
|
||||
this.scale = scale;
|
||||
this.abort = false;
|
||||
start();
|
||||
}
|
||||
}
|
||||
29
altoslib/AltosMapLoaderListener.java
Normal file
29
altoslib/AltosMapLoaderListener.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface AltosMapLoaderListener {
|
||||
public abstract void loader_start(int max);
|
||||
|
||||
public abstract void loader_notify(int cur, int max, String name);
|
||||
|
||||
public abstract void loader_done(int max);
|
||||
|
||||
public abstract void debug(String format, Object ... arguments);
|
||||
}
|
||||
49
altoslib/AltosMapMark.java
Normal file
49
altoslib/AltosMapMark.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AltosMapMark {
|
||||
|
||||
public AltosLatLon lat_lon;
|
||||
public int state;
|
||||
public String label;
|
||||
|
||||
static public int stroke_width = 6;
|
||||
|
||||
public abstract void paint(AltosMapTransform t);
|
||||
|
||||
public AltosMapMark (double lat, double lon, int state, String label) {
|
||||
lat_lon = new AltosLatLon(lat, lon);
|
||||
this.state = state;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public AltosMapMark (double lat, double lon, int state) {
|
||||
this(lat, lon, state, null);
|
||||
}
|
||||
|
||||
public AltosMapMark () {
|
||||
this(0, 0, 0);
|
||||
}
|
||||
}
|
||||
76
altoslib/AltosMapPath.java
Normal file
76
altoslib/AltosMapPath.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public abstract class AltosMapPath {
|
||||
|
||||
public LinkedList<AltosMapPathPoint> points = new LinkedList<AltosMapPathPoint>();
|
||||
public AltosMapPathPoint last_point = null;
|
||||
|
||||
static public int stroke_width = 6;
|
||||
|
||||
public abstract void paint(AltosMapTransform t);
|
||||
|
||||
public AltosMapRectangle add(AltosGPS gps, double time, int state, double gps_height) {
|
||||
AltosMapPathPoint point = new AltosMapPathPoint(gps, time, state, gps_height);
|
||||
AltosMapRectangle rect = null;
|
||||
|
||||
if (!point.equals(last_point)) {
|
||||
if (last_point != null)
|
||||
rect = new AltosMapRectangle(last_point.gps.lat_lon(), point.gps.lat_lon());
|
||||
points.add (point);
|
||||
last_point = point;
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
private double dist(AltosLatLon lat_lon, AltosMapPathPoint point) {
|
||||
return (new AltosGreatCircle(lat_lon.lat,
|
||||
lat_lon.lon,
|
||||
point.gps.lat,
|
||||
point.gps.lon)).distance;
|
||||
}
|
||||
|
||||
public AltosMapPathPoint nearest(AltosLatLon lat_lon) {
|
||||
AltosMapPathPoint nearest = null;
|
||||
double nearest_dist = 0;
|
||||
for (AltosMapPathPoint point : points) {
|
||||
if (nearest == null) {
|
||||
nearest = point;
|
||||
nearest_dist = dist(lat_lon, point);
|
||||
} else {
|
||||
double d = dist(lat_lon, point);
|
||||
if (d < nearest_dist) {
|
||||
nearest = point;
|
||||
nearest_dist = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
points = new LinkedList<AltosMapPathPoint>();
|
||||
}
|
||||
}
|
||||
55
altoslib/AltosMapPathPoint.java
Normal file
55
altoslib/AltosMapPathPoint.java
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosMapPathPoint {
|
||||
public AltosGPS gps;
|
||||
public double time;
|
||||
public int state;
|
||||
public double gps_height;
|
||||
|
||||
public int hashCode() {
|
||||
return Double.valueOf(gps.lat).hashCode() ^ Double.valueOf(gps.lon).hashCode() ^ state;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
if (!(o instanceof AltosMapPathPoint))
|
||||
return false;
|
||||
|
||||
AltosMapPathPoint other = (AltosMapPathPoint) o;
|
||||
|
||||
return gps.lat == other.gps.lat && gps.lon == other.gps.lon && state == other.state;
|
||||
}
|
||||
|
||||
public AltosMapPathPoint(AltosGPS gps, double time, int state, double gps_height) {
|
||||
this.gps = gps;
|
||||
this.time = time;
|
||||
this.state = state;
|
||||
this.gps_height = gps_height;
|
||||
}
|
||||
}
|
||||
|
||||
46
altoslib/AltosMapRectangle.java
Normal file
46
altoslib/AltosMapRectangle.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosMapRectangle {
|
||||
AltosLatLon ul, lr;
|
||||
|
||||
public AltosMapRectangle(AltosLatLon a, AltosLatLon b) {
|
||||
double ul_lat, ul_lon;
|
||||
double lr_lat, lr_lon;
|
||||
|
||||
if (a.lat > b.lat) {
|
||||
ul_lat = a.lat;
|
||||
lr_lat = b.lat;
|
||||
} else {
|
||||
ul_lat = b.lat;
|
||||
lr_lat = a.lat;
|
||||
}
|
||||
if (a.lon < b.lon) {
|
||||
ul_lon = a.lon;
|
||||
lr_lon = b.lon;
|
||||
} else {
|
||||
ul_lon = b.lon;
|
||||
lr_lon = a.lon;
|
||||
}
|
||||
|
||||
ul = new AltosLatLon(ul_lat, ul_lon);
|
||||
lr = new AltosLatLon(lr_lat, lr_lon);
|
||||
}
|
||||
}
|
||||
290
altoslib/AltosMapStore.java
Normal file
290
altoslib/AltosMapStore.java
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
public class AltosMapStore {
|
||||
String url;
|
||||
public File file;
|
||||
LinkedList<AltosMapStoreListener> listeners = new LinkedList<AltosMapStoreListener>();
|
||||
|
||||
int status;
|
||||
|
||||
private static File map_file(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
double lat = center.lat;
|
||||
double lon = center.lon;
|
||||
char chlat = lat < 0 ? 'S' : 'N';
|
||||
char chlon = lon < 0 ? 'W' : 'E';
|
||||
|
||||
if (lat < 0) lat = -lat;
|
||||
if (lon < 0) lon = -lon;
|
||||
String maptype_string = String.format("%s-", AltosMap.maptype_names[maptype]);
|
||||
String format_string;
|
||||
if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
|
||||
format_string = "jpg";
|
||||
else
|
||||
format_string = "png";
|
||||
return new File(AltosPreferences.mapdir(),
|
||||
String.format("map-%c%.6f,%c%.6f-%s%d%s.%s",
|
||||
chlat, lat, chlon, lon, maptype_string, zoom, scale == 1 ? "" : String.format("-%d", scale), format_string));
|
||||
}
|
||||
|
||||
public static String google_maps_api_key = null;
|
||||
|
||||
private static String google_map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale, String format_string) {
|
||||
return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s&key=%s",
|
||||
center.lat, center.lon, zoom, px_size, px_size, scale,
|
||||
AltosMap.maptype_names[maptype], format_string, google_maps_api_key);
|
||||
}
|
||||
|
||||
private static String altos_map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale, String format_string) {
|
||||
return String.format("https://maps.altusmetrum.org/cgi-bin/altos-map?lat=%.6f&lon=%.6f&zoom=%d",
|
||||
center.lat, center.lon, zoom);
|
||||
}
|
||||
|
||||
private static String map_url(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
String format_string;
|
||||
|
||||
if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
|
||||
format_string = "jpg";
|
||||
else
|
||||
format_string = "png32";
|
||||
|
||||
for (int s = 1; s < scale; s <<= 1)
|
||||
zoom--;
|
||||
|
||||
px_size /= scale;
|
||||
|
||||
if (google_maps_api_key != null)
|
||||
return google_map_url(center, zoom, maptype, px_size, scale, format_string);
|
||||
else
|
||||
return altos_map_url(center, zoom, maptype, px_size, scale, format_string);
|
||||
}
|
||||
|
||||
public synchronized int status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public synchronized void add_listener(AltosMapStoreListener listener) {
|
||||
if (!listeners.contains(listener))
|
||||
listeners.add(listener);
|
||||
listener.notify_store(this, status);
|
||||
}
|
||||
|
||||
public synchronized void remove_listener(AltosMapStoreListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
private synchronized void notify_listeners(int status) {
|
||||
this.status = status;
|
||||
for (AltosMapStoreListener listener : listeners)
|
||||
listener.notify_store(this, status);
|
||||
}
|
||||
|
||||
private int fetch_url() {
|
||||
URL u;
|
||||
|
||||
try {
|
||||
u = new URL(url);
|
||||
} catch (java.net.MalformedURLException e) {
|
||||
return AltosMapTile.bad_request;
|
||||
}
|
||||
|
||||
byte[] data = null;
|
||||
URLConnection uc = null;
|
||||
|
||||
int status = AltosMapTile.failed;
|
||||
int tries = 0;
|
||||
|
||||
while (tries < 10 && status != AltosMapTile.fetched) {
|
||||
try {
|
||||
uc = u.openConnection();
|
||||
String type = uc.getContentType();
|
||||
int contentLength = uc.getContentLength();
|
||||
if (uc instanceof HttpURLConnection) {
|
||||
int response = ((HttpURLConnection) uc).getResponseCode();
|
||||
switch (response) {
|
||||
case HttpURLConnection.HTTP_FORBIDDEN:
|
||||
case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
|
||||
case HttpURLConnection.HTTP_UNAUTHORIZED:
|
||||
return AltosMapTile.forbidden;
|
||||
}
|
||||
}
|
||||
InputStream in = new BufferedInputStream(uc.getInputStream());
|
||||
int bytesRead = 0;
|
||||
int offset = 0;
|
||||
data = new byte[contentLength];
|
||||
while (offset < contentLength) {
|
||||
bytesRead = in.read(data, offset, data.length - offset);
|
||||
if (bytesRead == -1)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
}
|
||||
in.close();
|
||||
|
||||
if (offset == contentLength)
|
||||
status = AltosMapTile.fetched;
|
||||
else
|
||||
status = AltosMapTile.failed;
|
||||
|
||||
} catch (IOException e) {
|
||||
status = AltosMapTile.failed;
|
||||
}
|
||||
|
||||
if (status != AltosMapTile.fetched) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
tries++;
|
||||
System.out.printf("Fetch failed, retrying %d\n", tries);
|
||||
}
|
||||
}
|
||||
|
||||
if (status != AltosMapTile.fetched)
|
||||
return status;
|
||||
|
||||
try {
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
if (data != null)
|
||||
out.write(data);
|
||||
out.flush();
|
||||
out.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
return AltosMapTile.bad_request;
|
||||
} catch (IOException e) {
|
||||
if (file.exists())
|
||||
file.delete();
|
||||
return AltosMapTile.bad_request;
|
||||
}
|
||||
return AltosMapTile.fetched;
|
||||
}
|
||||
|
||||
static Object fetch_lock = new Object();
|
||||
|
||||
static Object fetcher_lock = new Object();
|
||||
|
||||
static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
|
||||
static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
|
||||
|
||||
static int concurrent_fetchers() {
|
||||
if (google_maps_api_key == null)
|
||||
return 16;
|
||||
return 128;
|
||||
}
|
||||
|
||||
static void start_fetchers() {
|
||||
while (!waiting.isEmpty() && running.size() < concurrent_fetchers()) {
|
||||
AltosMapStore s = waiting.remove();
|
||||
running.add(s);
|
||||
Thread lt = s.make_fetcher_thread();
|
||||
lt.start();
|
||||
}
|
||||
}
|
||||
|
||||
void finish_fetcher() {
|
||||
synchronized(fetcher_lock) {
|
||||
running.remove(this);
|
||||
start_fetchers();
|
||||
}
|
||||
}
|
||||
|
||||
void add_fetcher() {
|
||||
synchronized(fetcher_lock) {
|
||||
waiting.add(this);
|
||||
start_fetchers();
|
||||
}
|
||||
}
|
||||
|
||||
class fetcher implements Runnable {
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
if (file.exists()) {
|
||||
notify_listeners(AltosMapTile.fetched);
|
||||
return;
|
||||
}
|
||||
|
||||
int new_status;
|
||||
|
||||
new_status = fetch_url();
|
||||
|
||||
notify_listeners(new_status);
|
||||
} finally {
|
||||
finish_fetcher();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Thread make_fetcher_thread() {
|
||||
return new Thread(new fetcher());
|
||||
}
|
||||
|
||||
private void fetch() {
|
||||
add_fetcher();
|
||||
}
|
||||
|
||||
private AltosMapStore (String url, File file) {
|
||||
this.url = url;
|
||||
this.file = file;
|
||||
|
||||
if (file.exists())
|
||||
status = AltosMapTile.fetched;
|
||||
else {
|
||||
status = AltosMapTile.fetching;
|
||||
fetch();
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return url.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (o == null)
|
||||
return false;
|
||||
|
||||
if (!(o instanceof AltosMapStore))
|
||||
return false;
|
||||
|
||||
AltosMapStore other = (AltosMapStore) o;
|
||||
return url.equals(other.url);
|
||||
}
|
||||
|
||||
static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
|
||||
|
||||
public static AltosMapStore get(AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
String url = map_url(center, zoom, maptype, px_size, scale);
|
||||
|
||||
AltosMapStore store;
|
||||
synchronized(stores) {
|
||||
if (stores.containsKey(url)) {
|
||||
store = stores.get(url);
|
||||
} else {
|
||||
store = new AltosMapStore(url, map_file(center, zoom, maptype, px_size, scale));
|
||||
stores.put(url, store);
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
}
|
||||
23
altoslib/AltosMapStoreListener.java
Normal file
23
altoslib/AltosMapStoreListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosMapStoreListener {
|
||||
abstract void notify_store(AltosMapStore store, int status);
|
||||
}
|
||||
119
altoslib/AltosMapTile.java
Normal file
119
altoslib/AltosMapTile.java
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class AltosMapTile implements AltosFontListener, AltosMapStoreListener {
|
||||
LinkedList<AltosMapTileListener> listeners = new LinkedList<AltosMapTileListener>();
|
||||
public AltosLatLon upper_left, center;
|
||||
public int px_size;
|
||||
int zoom;
|
||||
int maptype;
|
||||
int scale;
|
||||
private AltosMapCache cache;
|
||||
public AltosMapStore store;
|
||||
public int status;
|
||||
|
||||
static public final int loaded = 0; /* loaded from file */
|
||||
static public final int fetched = 1; /* downloaded to file */
|
||||
static public final int fetching = 2; /* downloading from net */
|
||||
static public final int failed = 3; /* loading from file failed */
|
||||
static public final int bad_request = 4;/* downloading failed */
|
||||
static public final int forbidden = 5; /* downloading failed */
|
||||
|
||||
static public String status_name(int status) {
|
||||
switch (status) {
|
||||
case loaded:
|
||||
return "loaded";
|
||||
case fetched:
|
||||
return "fetched";
|
||||
case fetching:
|
||||
return "fetching";
|
||||
case failed:
|
||||
return "failed";
|
||||
case bad_request:
|
||||
return "bad_request";
|
||||
case forbidden:
|
||||
return "forbidden";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public void font_size_changed(int font_size) {
|
||||
}
|
||||
|
||||
private synchronized void notify_listeners(int status) {
|
||||
this.status = status;
|
||||
for (AltosMapTileListener listener : listeners)
|
||||
listener.notify_tile(this, status);
|
||||
}
|
||||
|
||||
public void notify_store(AltosMapStore store, int status) {
|
||||
notify_listeners(status);
|
||||
}
|
||||
|
||||
public void notify_image(AltosImage image) {
|
||||
if (image == null)
|
||||
status = failed;
|
||||
else
|
||||
status = loaded;
|
||||
notify_listeners(status);
|
||||
}
|
||||
|
||||
public void paint(AltosMapTransform t) {
|
||||
}
|
||||
|
||||
public AltosImage get_image() {
|
||||
if (cache == null)
|
||||
return null;
|
||||
return cache.get(this);
|
||||
}
|
||||
|
||||
public synchronized void add_listener(AltosMapTileListener listener) {
|
||||
if (!listeners.contains(listener))
|
||||
listeners.add(listener);
|
||||
listener.notify_tile(this, status);
|
||||
}
|
||||
|
||||
public synchronized void remove_listener(AltosMapTileListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public AltosMapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||
this.cache = cache;
|
||||
this.upper_left = upper_left;
|
||||
|
||||
while (center.lon < -180.0)
|
||||
center.lon += 360.0;
|
||||
while (center.lon > 180.0)
|
||||
center.lon -= 360.0;
|
||||
|
||||
this.center = center;
|
||||
this.zoom = zoom;
|
||||
this.maptype = maptype;
|
||||
this.px_size = px_size;
|
||||
this.scale = scale;
|
||||
|
||||
store = AltosMapStore.get(center, zoom, maptype, px_size, scale);
|
||||
store.add_listener(this);
|
||||
}
|
||||
}
|
||||
23
altoslib/AltosMapTileListener.java
Normal file
23
altoslib/AltosMapTileListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosMapTileListener {
|
||||
abstract public void notify_tile(AltosMapTile tile, int status);
|
||||
}
|
||||
175
altoslib/AltosMapTransform.java
Normal file
175
altoslib/AltosMapTransform.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.Math;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosMapTransform {
|
||||
|
||||
double scale_x, scale_y;
|
||||
|
||||
double offset_x, offset_y;
|
||||
|
||||
int width, height;
|
||||
|
||||
public AltosLatLon lat_lon (AltosPointDouble point) {
|
||||
double lat, lon;
|
||||
double rads;
|
||||
|
||||
lon = point.x/scale_x;
|
||||
rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
|
||||
lat = Math.toDegrees(rads - Math.PI/2);
|
||||
|
||||
return new AltosLatLon(lat,lon);
|
||||
}
|
||||
|
||||
public AltosLatLon lat_lon (AltosPointInt point) {
|
||||
return lat_lon(new AltosPointDouble(point.x, point.y));
|
||||
}
|
||||
|
||||
public AltosPointDouble screen_point(AltosPointInt screen) {
|
||||
return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
|
||||
}
|
||||
|
||||
public AltosPointDouble screen_point(AltosPointDouble screen) {
|
||||
return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
|
||||
}
|
||||
|
||||
public double hypot(AltosLatLon a, AltosLatLon b) {
|
||||
AltosPointDouble a_pt = point(a);
|
||||
AltosPointDouble b_pt = point(b);
|
||||
|
||||
return Math.hypot(a_pt.x - b_pt.x, a_pt.y - b_pt.y);
|
||||
}
|
||||
|
||||
public AltosLatLon screen_lat_lon(AltosPointInt screen) {
|
||||
return lat_lon(screen_point(screen));
|
||||
}
|
||||
|
||||
public AltosLatLon screen_lat_lon(AltosPointDouble screen) {
|
||||
return lat_lon(screen_point(screen));
|
||||
}
|
||||
|
||||
public AltosPointDouble point(double lat, double lon) {
|
||||
double x, y;
|
||||
double e;
|
||||
|
||||
x = lon * scale_x;
|
||||
|
||||
e = Math.sin(Math.toRadians(lat));
|
||||
e = Math.max(e,-(1-1.0E-15));
|
||||
e = Math.min(e, 1-1.0E-15 );
|
||||
|
||||
y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
|
||||
|
||||
return new AltosPointDouble(x, y);
|
||||
}
|
||||
|
||||
public AltosPointDouble point(AltosLatLon lat_lon) {
|
||||
return point(lat_lon.lat, lat_lon.lon);
|
||||
}
|
||||
|
||||
public AltosPointDouble screen(AltosPointDouble point) {
|
||||
return new AltosPointDouble(point.x - offset_x, point.y - offset_y);
|
||||
}
|
||||
|
||||
public AltosPointInt screen(AltosPointInt point) {
|
||||
return new AltosPointInt((int) (point.x - offset_x + 0.5),
|
||||
(int) (point.y - offset_y + 0.5));
|
||||
}
|
||||
|
||||
public AltosRectangle screen(AltosMapRectangle map_rect) {
|
||||
AltosPointDouble ul = screen(map_rect.ul);
|
||||
AltosPointDouble lr = screen(map_rect.lr);
|
||||
|
||||
return new AltosRectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
|
||||
}
|
||||
|
||||
public AltosPointDouble screen(AltosLatLon lat_lon) {
|
||||
return screen(point(lat_lon));
|
||||
}
|
||||
|
||||
public AltosPointDouble screen(double lat, double lon) {
|
||||
return screen(point(lat, lon));
|
||||
}
|
||||
|
||||
/* Return first longitude value which ends up on-screen */
|
||||
public double first_lon(double lon) {
|
||||
/* Find a longitude left of the screen */
|
||||
for (;;) {
|
||||
double x = lon * scale_x - offset_x;
|
||||
if (x < 0)
|
||||
break;
|
||||
lon -= 360.0;
|
||||
}
|
||||
/* Find the first longitude on the screen */
|
||||
for (;;) {
|
||||
double x = lon * scale_x - offset_x;
|
||||
if (x >= 0)
|
||||
break;
|
||||
lon += 360.0;
|
||||
}
|
||||
return lon;
|
||||
}
|
||||
|
||||
/* Return last longitude value which ends up on-screen */
|
||||
public double last_lon(double lon) {
|
||||
lon = first_lon(lon);
|
||||
|
||||
for(;;) {
|
||||
double next_lon = lon + 360.0;
|
||||
double next_x = next_lon * scale_x - offset_x;
|
||||
if (next_x >= width)
|
||||
break;
|
||||
lon = next_lon;
|
||||
}
|
||||
return lon;
|
||||
}
|
||||
|
||||
private boolean has_location;
|
||||
|
||||
public boolean has_location() {
|
||||
return has_location;
|
||||
}
|
||||
|
||||
public AltosMapTransform(int width, int height, int zoom, AltosLatLon centre_lat_lon) {
|
||||
scale_x = 256/360.0 * Math.pow(2, zoom);
|
||||
scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
AltosPointDouble centre_pt = point(centre_lat_lon);
|
||||
|
||||
has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
|
||||
offset_x = centre_pt.x - width / 2.0;
|
||||
offset_y = centre_pt.y - height / 2.0;
|
||||
}
|
||||
|
||||
public static double lon_from_distance(double lat, double distance) {
|
||||
double c = AltosGreatCircle.earth_radius * Math.cos(lat * Math.PI / 180) * 2 * Math.PI;
|
||||
|
||||
if (c < 10)
|
||||
return 0;
|
||||
return distance/c * 360.0;
|
||||
}
|
||||
}
|
||||
23
altoslib/AltosMapTypeListener.java
Normal file
23
altoslib/AltosMapTypeListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosMapTypeListener {
|
||||
public void map_type_changed(int map_type);
|
||||
}
|
||||
23
altoslib/AltosMapZoomListener.java
Normal file
23
altoslib/AltosMapZoomListener.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public interface AltosMapZoomListener {
|
||||
abstract public void zoom_changed(int zoom);
|
||||
}
|
||||
78
altoslib/AltosMma655x.java
Normal file
78
altoslib/AltosMma655x.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class AltosMma655x implements Cloneable {
|
||||
|
||||
private int accel;
|
||||
|
||||
public boolean parse_line(String line) throws NumberFormatException {
|
||||
if (line.startsWith("MMA655X value")) {
|
||||
String[] items = line.split("\\s+");
|
||||
if (items.length >= 3) {
|
||||
accel = Integer.parseInt(items[2]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AltosMma655x() {
|
||||
accel = AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public AltosMma655x clone() {
|
||||
AltosMma655x n = new AltosMma655x();
|
||||
|
||||
n.accel = accel;
|
||||
return n;
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException, AltosUnknownProduct {
|
||||
try {
|
||||
AltosMma655x mma655x = new AltosMma655x(link);
|
||||
AltosCalData cal_data = listener.cal_data();
|
||||
|
||||
if (mma655x != null) {
|
||||
int accel = mma655x.accel;
|
||||
if (cal_data.mma655x_inverted)
|
||||
accel = 4095 - accel;
|
||||
if (cal_data.pad_orientation == AltosLib.AO_PAD_ORIENTATION_ANTENNA_DOWN)
|
||||
accel = 4095 - accel;
|
||||
listener.set_acceleration(cal_data.acceleration(accel));
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
} catch (NumberFormatException ne) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMma655x(AltosLink link) throws InterruptedException, TimeoutException, NumberFormatException {
|
||||
this();
|
||||
link.printf("A\n");
|
||||
for (;;) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
if (parse_line(line))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
157
altoslib/AltosMs5607.java
Normal file
157
altoslib/AltosMs5607.java
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.io.*;
|
||||
|
||||
public class AltosMs5607 {
|
||||
public int reserved = AltosLib.MISSING;
|
||||
public int sens = AltosLib.MISSING;
|
||||
public int off = AltosLib.MISSING;
|
||||
public int tcs = AltosLib.MISSING;
|
||||
public int tco = AltosLib.MISSING;
|
||||
public int tref = AltosLib.MISSING;
|
||||
public int tempsens = AltosLib.MISSING;
|
||||
public int crc = AltosLib.MISSING;
|
||||
private boolean ms5611 = false;
|
||||
|
||||
public boolean valid_config() {
|
||||
return reserved != AltosLib.MISSING &&
|
||||
sens != AltosLib.MISSING &&
|
||||
off != AltosLib.MISSING &&
|
||||
tcs != AltosLib.MISSING &&
|
||||
tco != AltosLib.MISSING &&
|
||||
tref != AltosLib.MISSING &&
|
||||
tempsens != AltosLib.MISSING &&
|
||||
crc != AltosLib.MISSING;
|
||||
}
|
||||
|
||||
public AltosPresTemp pres_temp(int raw_pres, int raw_temp) {
|
||||
int dT;
|
||||
int TEMP;
|
||||
long OFF;
|
||||
long SENS;
|
||||
int P;
|
||||
|
||||
if (raw_pres == AltosLib.MISSING || raw_temp == AltosLib.MISSING)
|
||||
return new AltosPresTemp(AltosLib.MISSING, AltosLib.MISSING);
|
||||
|
||||
dT = raw_temp - ((int) tref << 8);
|
||||
|
||||
TEMP = (int) (2000 + (((long) dT * (long) tempsens) >> 23));
|
||||
|
||||
if (ms5611) {
|
||||
OFF = ((long) off << 16) + (((long) tco * (long) dT) >> 7);
|
||||
|
||||
SENS = ((long) sens << 15) + (((long) tcs * (long) dT) >> 8);
|
||||
} else {
|
||||
OFF = ((long) off << 17) + (((long) tco * (long) dT) >> 6);
|
||||
|
||||
SENS = ((long) sens << 16) + (((long) tcs * (long) dT) >> 7);
|
||||
}
|
||||
|
||||
if (TEMP < 2000) {
|
||||
int T2 = (int) (((long) dT * (long) dT) >> 31);
|
||||
int TEMPM = TEMP - 2000;
|
||||
long OFF2 = ((long) 61 * (long) TEMPM * (long) TEMPM) >> 4;
|
||||
long SENS2 = (long) 2 * (long) TEMPM * (long) TEMPM;
|
||||
if (TEMP < -1500) {
|
||||
int TEMPP = TEMP + 1500;
|
||||
long TEMPP2 = (long) TEMPP * (long) TEMPP;
|
||||
OFF2 = OFF2 + 15 * TEMPP2;
|
||||
SENS2 = SENS2 + 8 * TEMPP2;
|
||||
}
|
||||
TEMP -= T2;
|
||||
OFF -= OFF2;
|
||||
SENS -= SENS2;
|
||||
}
|
||||
|
||||
P = (int) (((((long) raw_pres * SENS) >> 21) - OFF) >> 15);
|
||||
|
||||
return new AltosPresTemp(P, TEMP / 100.0);
|
||||
}
|
||||
|
||||
public AltosPresTemp pres_temp(AltosLink link) throws InterruptedException, TimeoutException {
|
||||
int raw_pres = AltosLib.MISSING;
|
||||
int raw_temp = AltosLib.MISSING;
|
||||
boolean done = false;
|
||||
|
||||
link.printf("B\n");
|
||||
while (!done) {
|
||||
String line = link.get_reply_no_dialog(5000);
|
||||
if (line == null)
|
||||
throw new TimeoutException();
|
||||
|
||||
String[] items = line.split("\\s+");
|
||||
if (line.startsWith("Pressure:")) {
|
||||
if (items.length >= 2) {
|
||||
raw_pres = Integer.parseInt(items[1]);
|
||||
}
|
||||
} else if (line.startsWith("Temperature:")) {
|
||||
if (items.length >= 2)
|
||||
raw_temp = Integer.parseInt(items[1]);
|
||||
} else if (line.startsWith("Altitude:")) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
return pres_temp(raw_pres, raw_temp);
|
||||
}
|
||||
|
||||
public AltosMs5607(boolean ms5611) {
|
||||
this.ms5611 = ms5611;
|
||||
}
|
||||
|
||||
public AltosMs5607() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public AltosMs5607(AltosMs5607 old) {
|
||||
reserved = old.reserved;
|
||||
sens = old.sens;
|
||||
off = old.off;
|
||||
tcs = old.tcs;
|
||||
tco = old.tco;
|
||||
tref = old.tref;
|
||||
tempsens = old.tempsens;
|
||||
crc = old.crc;
|
||||
}
|
||||
|
||||
static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
|
||||
try {
|
||||
AltosCalData cal_data = listener.cal_data();
|
||||
AltosMs5607 ms5607 = cal_data.ms5607;
|
||||
|
||||
if (ms5607 != null) {
|
||||
AltosPresTemp pt = ms5607.pres_temp(link);
|
||||
listener.set_temperature(pt.temp);
|
||||
listener.set_pressure(pt.pres);
|
||||
}
|
||||
} catch (TimeoutException te) {
|
||||
}
|
||||
}
|
||||
|
||||
public AltosMs5607(AltosConfigData config_data) {
|
||||
this(config_data.ms5607());
|
||||
}
|
||||
|
||||
public AltosMs5607 (AltosLink link, AltosConfigData config_data) throws InterruptedException, TimeoutException {
|
||||
this(config_data);
|
||||
}
|
||||
}
|
||||
25
altoslib/AltosNoSymbol.java
Normal file
25
altoslib/AltosNoSymbol.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.altoslib_14;
|
||||
|
||||
public class AltosNoSymbol extends Exception {
|
||||
public AltosNoSymbol(String name) {
|
||||
super(String.format("No such symbol \"%s\"", name));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user