/* * Copyright © 2010 Keith Packard * * This program is free software; 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_13; 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 = "\n" + "\n" + "\n" + " AO Flight#%d S/N: %03d\n" + " \n"; static final String kml_header_end = " \n" + " 1\n"; static final String kml_folder_start = " \n" + " %s\n"; static final String kml_path_style_start = " \n"; static final String kml_point_style_start = " \n"; static final String kml_path_start = " \n" + " %s\n" + " #ao-style-%s\n" + " \n" + " 1\n" + " absolute\n" + " \n"; static final String kml_coord_fmt = " %.7f,%.7f,%.7f \n"; static final String kml_path_end = " \n" + " \n" + " \n"; static final String kml_point_start = " \n" + " %s\n" + " #ao-style-%s\n" + " \n" + " 1\n" + " absolute\n" + " \n"; static final String kml_point_end = " \n" + " \n" + " \n"; static final String kml_folder_end = " \n"; static final String kml_footer = "\n" + "\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)); } }