Initial Commit - Copy from Altus Metrum AltOS

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

4
altoslib/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
bin
classaltoslib.stamp
altoslib*.jar
AltosVersion.java

52
altoslib/AltosAccel.java Normal file
View 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
View 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);
}
}

View 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
View 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;
}
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright © 2010 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altoslib_14;
public class AltosCRCException extends Exception {
public int rssi;
public AltosCRCException (int in_rssi) {
rssi = in_rssi;
}
}

578
altoslib/AltosCSV.java Normal file
View 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
View 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);
}
}

View 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];
}
}

File diff suppressed because it is too large Load Diff

View 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));
}
}

View 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
View 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];
}
}

View 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;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright © 2013 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altoslib_14;
public interface AltosDataProvider {
public void provide_data(AltosDataListener listener) throws InterruptedException, AltosUnknownProduct;
}

285
altoslib/AltosDebug.java Normal file
View 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
View 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
View 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() {
}
}

View 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;
}
}
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}
}

View 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;
}
}

View 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();
}

View 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;
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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));
}
}

View 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);
}
}

View 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
View 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");
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright © 2017 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
package org.altusmetrum.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
View 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");
}
}
}

View 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);
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright © 2010 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altoslib_14;
public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
void reset();
void show(AltosState state, AltosListenerState listener_state);
String getName();
}

View 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; }
}

View 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);
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright © 2011 Keith Packard <keithp@keithp.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.altusmetrum.altoslib_14;
public interface AltosFontListener {
void font_size_changed(int font_size);
}

49
altoslib/AltosForce.java Normal file
View 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;
}
}

View 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
View 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
View 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() {
}
}

View 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
View 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;
}
}

View 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
View 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
View 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
View 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
View 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
View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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();
}

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

354
altoslib/AltosKML.java Normal file
View 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
View 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;
}
}

View 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"; }
}

View 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);
}
}
}

View 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);
}

View 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
View 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
View 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
View 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;
}
}

View 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;
}
}

View 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
View 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);
}
}

View 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);
}

View 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
View 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
View 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
View 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);
}
}

View 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);
}

View 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);
}

View 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);
}
}

View 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();
}
}

View 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);
}

View 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);
}
}

View 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>();
}
}

View 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;
}
}

View 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
View 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;
}
}

View 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
View 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);
}
}

View 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);
}

View 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;
}
}

View 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);
}

View 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);
}

View 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
View 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);
}
}

View 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