altoslib: Remove spurious debug printf in AltosCalData
[fw/altos] / altoslib / AltosKML.java
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.altoslib_13;
20
21 import java.io.*;
22 import java.util.*;
23
24 class KMLWriter extends PrintWriter {
25         public PrintWriter printf(String format, Object ... arguments) {
26                 return printf(Locale.ROOT, format, arguments);
27         }
28
29         public KMLWriter(File name) throws FileNotFoundException {
30                 super(name);
31         }
32 }
33
34 public class AltosKML implements AltosWriter {
35
36         File                    name;
37         PrintWriter             out;
38         int                     flight_state = -1;
39         AltosGPS                prev = null;
40         double                  gps_start_altitude = AltosLib.MISSING;
41         AltosFlightSeries       series;
42         AltosFlightStats        stats;
43         AltosCalData            cal_data;
44
45         static final String[] kml_state_colors = {
46                 "FF000000",     // startup
47                 "FF000000",     // idle
48                 "FF000000",     // pad
49                 "FF0000FF",     // boost
50                 "FF8040FF",     // coast
51                 "FF4080FF",     // fast
52                 "FF00FFFF",     // drogue
53                 "FF00FF00",     // main
54                 "FF000000",     // landed
55                 "FFFFFFFF",     // invalid
56                 "FFFF0000",     // stateless
57         };
58
59         static String state_color(int state) {
60                 if (state < 0 || kml_state_colors.length <= state)
61                         return kml_state_colors[AltosLib.ao_flight_invalid];
62                 return kml_state_colors[state];
63         }
64
65         static final String[] kml_style_colors = {
66                 "FF0000FF",     // baro
67                 "FFFF0000",     // gps
68         };
69
70         static String style_color(int style) {
71                 if (style < 0 || kml_style_colors.length <= style)
72                         return kml_style_colors[0];
73                 return kml_style_colors[style];
74         }
75
76         static final String kml_header_start =
77                 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
78                 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
79                 "<Document>\n" +
80                 "  <name>AO Flight#%d S/N: %03d</name>\n" +
81                 "  <Snippet maxLines=\"8\">\n";
82
83         static final String kml_header_end =
84                 "  </Snippet>\n" +
85                 "  <open>1</open>\n";
86
87         static final String kml_folder_start =
88                 "  <Folder>\n" +
89                 "    <name>%s</name>\n";
90
91         static final String kml_path_style_start =
92                 "    <Style id=\"ao-style-%s\">\n" +
93                 "      <LineStyle><color>%s</color><width>8</width></LineStyle>\n" +
94                 "      <BalloonStyle>\n" +
95                 "        <text>\n";
96
97         static final String kml_path_style_end =
98                 "        </text>\n" +
99                 "      </BalloonStyle>\n" +
100                 "    </Style>\n";
101
102         static final String kml_point_style_start =
103                 "    <Style id=\"ao-style-%s\">\n" +
104                 "      <LabelStyle><color>%s</color></LabelStyle>\n" +
105                 "      <IconStyle><color>%s</color></IconStyle>\n" +
106                 "      <BalloonStyle>\n" +
107                 "        <text>\n";
108
109         static final String kml_point_style_end =
110                 "        </text>\n" +
111                 "      </BalloonStyle>\n" +
112                 "    </Style>\n";
113
114         static final String kml_path_start =
115                 "    <Placemark>\n" +
116                 "      <name>%s</name>\n" +
117                 "      <styleUrl>#ao-style-%s</styleUrl>\n" +
118                 "      <LineString>\n" +
119                 "        <tessellate>1</tessellate>\n" +
120                 "        <altitudeMode>absolute</altitudeMode>\n" +
121                 "        <coordinates>\n";
122
123         static final String kml_coord_fmt =
124         "          %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
125
126         static final String kml_path_end =
127                 "        </coordinates>\n" +
128                 "      </LineString>\n" +
129                 "    </Placemark>\n";
130
131         static final String kml_point_start =
132                 "    <Placemark>\n" +
133                 "      <name>%s</name>\n" +
134                 "      <styleUrl>#ao-style-%s</styleUrl>\n" +
135                 "      <Point>\n" +
136                 "        <tessellate>1</tessellate>\n" +
137                 "        <altitudeMode>absolute</altitudeMode>\n" +
138                 "        <coordinates>\n";
139
140         static final String kml_point_end =
141                 "        </coordinates>\n" +
142                 "      </Point>\n" +
143                 "    </Placemark>\n";
144
145         static final String kml_folder_end =
146                 "  </Folder>\n";
147
148         static final String kml_footer =
149                 "</Document>\n" +
150                 "</kml>\n";
151
152         void start () {
153                 AltosGPS gps = cal_data.gps_pad;
154
155                 gps_start_altitude = cal_data.gps_pad_altitude;
156                 out.printf(kml_header_start, cal_data.flight, cal_data.serial);
157                 out.printf("Product: %s\n", stats.product);
158                 out.printf("Firmware: %s\n", stats.firmware_version);
159                 out.printf("Date: %04d-%02d-%02d\n",
160                            gps.year, gps.month, gps.day);
161                 out.printf("Time: %2d:%02d:%02d\n",
162                            gps.hour, gps.minute, gps.second);
163                 if (stats.max_height != AltosLib.MISSING)
164                         out.printf("Max baro height: %s\n", AltosConvert.height.show(6, stats.max_height));
165                 if (stats.max_gps_height != AltosLib.MISSING)
166                         out.printf("Max GPS Height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
167                 if (stats.max_speed != AltosLib.MISSING)
168                         out.printf("Max speed: %s\n", AltosConvert.speed.show(6, stats.max_speed));
169                 if (stats.max_acceleration != AltosLib.MISSING)
170                         out.printf("Max accel: %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
171                 out.printf("%s", kml_header_end);
172         }
173
174         void folder_start(String folder_name) {
175                 out.printf(kml_folder_start, folder_name);
176         }
177
178         void folder_end() {
179                 out.printf(kml_folder_end);
180         }
181
182         void path_style_start(String style, String color) {
183                 out.printf(kml_path_style_start, style, color);
184         }
185
186         void path_style_end() {
187                 out.printf(kml_path_style_end);
188         }
189
190         void point_style_start(String style, String color) {
191                 out.printf(kml_point_style_start, style, color, color);
192         }
193
194         void point_style_end() {
195                 out.printf(kml_point_style_end);
196         }
197
198         void path_start(String name, String style) {
199                 out.printf(kml_path_start, name, style);
200         }
201
202         void path_end() {
203                 out.printf(kml_path_end);
204         }
205
206         void point_start(String name, String style) {
207                 out.printf(kml_point_start, name, style);
208         }
209
210         void point_end() {
211                 out.printf(kml_point_end);
212         }
213
214         boolean started = false;
215
216         private double baro_altitude(AltosFlightSeries series, double time) {
217                 double height = series.value(AltosFlightSeries.height_name, time);
218
219                 if (height == AltosLib.MISSING)
220                         return AltosLib.MISSING;
221                 if (cal_data.gps_pad_altitude == AltosLib.MISSING)
222                         return AltosLib.MISSING;
223
224                 return height + cal_data.gps_pad_altitude;
225         }
226
227         void coord(double time, AltosGPS gps, double altitude) {
228                 out.printf(kml_coord_fmt,
229                            gps.lon, gps.lat,
230                            altitude, (double) gps.alt,
231                            time, gps.nsat);
232         }
233
234         void end() {
235                 out.printf("%s", kml_footer);
236         }
237
238         public void close() {
239                 if (out != null) {
240                         out.close();
241                         out = null;
242                 }
243         }
244
245         public void write(AltosGPS gps, double alt)
246         {
247                 if (gps == null)
248                         return;
249                 if (gps.lat == AltosLib.MISSING)
250                         return;
251                 if (gps.lon == AltosLib.MISSING)
252                         return;
253                 if (alt == AltosLib.MISSING) {
254                         alt = cal_data.gps_pad_altitude;
255                         if (alt == AltosLib.MISSING)
256                                 return;
257                 }
258                 coord(0, gps, alt);
259                 prev = gps;
260         }
261
262         public void write_point(AltosTimeValue tv, boolean is_gps) {
263                 int             state = (int) tv.value;
264                 String          style_prefix = is_gps ? "gps-" : "baro-";
265                 String          state_name = AltosLib.state_name(state);
266                 String          state_label = AltosLib.state_name_capital(state);
267                 String          style_name = style_prefix + state_name;
268                 String          folder_name = is_gps ? "GPS" : "Baro";
269                 String          full_name = state_label + " (" + folder_name + ")";
270                 AltosGPS        gps = series.gps_before(tv.time);
271                 double          altitude = is_gps ? gps.alt : baro_altitude(series, tv.time);
272
273                 point_style_start(style_name, state_color(state));
274                 out.printf("%s\n", full_name);
275                 switch (state) {
276                 case AltosLib.ao_flight_boost:
277                         out.printf("Max accel %s\n", AltosConvert.accel.show(6, stats.max_acceleration));
278                         out.printf("Max speed %s\n", AltosConvert.speed.show(6, stats.max_speed));
279                         break;
280                 case AltosLib.ao_flight_coast:
281                 case AltosLib.ao_flight_fast:
282                         out.printf("Entry speed %s\n", AltosConvert.speed.show(6, stats.state_enter_speed[state]));
283                         out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
284                         break;
285                 case AltosLib.ao_flight_drogue:
286                         out.printf("Max height %s\n", AltosConvert.height.show(6, is_gps ? stats.max_gps_height : stats.max_height));
287                         out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
288                         break;
289                 case AltosLib.ao_flight_main:
290                         out.printf("Entry speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
291                         out.printf("Entry height %s\n", AltosConvert.height.show(6, altitude - cal_data.gps_pad_altitude));
292                         out.printf("Average descent rate %s\n", AltosConvert.speed.show(6, -stats.state_speed[state]));
293                         break;
294                 case AltosLib.ao_flight_landed:
295                         out.printf("Landing speed %s\n", AltosConvert.speed.show(6, -stats.state_enter_speed[state]));
296                         break;
297                 }
298                 point_style_end();
299                 point_start(full_name, style_name);
300                 gps = series.gps_before(tv.time);
301                 write(gps, altitude);
302                 point_end();
303         }
304
305         public void write(AltosFlightSeries series) {
306                 this.series = series;
307                 series.finish();
308                 stats = new AltosFlightStats(series);
309                 cal_data = series.cal_data();
310                 start();
311                 if (series.height_series != null) {
312                         folder_start("Barometric Altitude");
313                         path_style_start("baro", style_color(0));
314                         out.printf("Barometric Altitude\n");
315                         out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_height));
316                         path_style_end();
317                         path_start("Barometric Altitude", "baro");
318                         for (AltosGPSTimeValue gtv : series.gps_series)
319                                 write(gtv.gps, baro_altitude(series, gtv.time));
320                         path_end();
321                         if (series.state_series != null) {
322                                 for (AltosTimeValue tv : series.state_series) {
323                                         write_point(tv, false);
324                                 }
325                         }
326                         folder_end();
327                 }
328                 folder_start("GPS Altitude");
329                 path_style_start("gps", style_color(1));
330                 out.printf("GPS Altitude");
331                 out.printf("Max height: %s\n", AltosConvert.height.show(6, stats.max_gps_height));
332                 path_style_end();
333                 path_start("GPS Altitude", "gps");
334                 for (AltosGPSTimeValue gtv : series.gps_series)
335                         write(gtv.gps, gtv.gps.alt);
336                 path_end();
337                 if (series.state_series != null) {
338                         for (AltosTimeValue tv : series.state_series) {
339                                 write_point(tv, true);
340                         }
341                 }
342                 folder_end();
343                 end();
344         }
345
346         public AltosKML(File in_name) throws FileNotFoundException {
347                 name = in_name;
348                 out = new KMLWriter(name);
349         }
350
351         public AltosKML(String in_string) throws FileNotFoundException {
352                 this(new File(in_string));
353         }
354 }