altoslib: Reset transient AltosCalData values before processing data
[fw/altos] / altoslib / AltosCSV.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_11;
20
21 import java.io.*;
22 import java.util.*;
23
24 public class AltosCSV implements AltosWriter {
25         File                    name;
26         PrintStream             out;
27         boolean                 header_written;
28         boolean                 seen_boost;
29         int                     boost_tick;
30
31         boolean                 has_basic;
32         boolean                 has_battery;
33         boolean                 has_flight_state;
34         boolean                 has_advanced;
35         boolean                 has_gps;
36         boolean                 has_gps_sat;
37         boolean                 has_companion;
38
39         AltosFlightSeries       series;
40         int[]                   indices;
41
42         static final int ALTOS_CSV_VERSION = 5;
43
44         /* Version 4 format:
45          *
46          * General info
47          *      version number
48          *      serial number
49          *      flight number
50          *      callsign
51          *      time (seconds since boost)
52          *      clock (tick count / 100)
53          *      rssi
54          *      link quality
55          *
56          * Flight status
57          *      state
58          *      state name
59          *
60          * Basic sensors
61          *      acceleration (m/s²)
62          *      pressure (mBar)
63          *      altitude (m)
64          *      height (m)
65          *      accelerometer speed (m/s)
66          *      barometer speed (m/s)
67          *      temp (°C)
68          *      drogue (V)
69          *      main (V)
70          *
71          * Battery
72          *      battery (V)
73          *
74          * Advanced sensors (if available)
75          *      accel_x (m/s²)
76          *      accel_y (m/s²)
77          *      accel_z (m/s²)
78          *      gyro_x (d/s)
79          *      gyro_y (d/s)
80          *      gyro_z (d/s)
81          *      mag_x (g)
82          *      mag_y (g)
83          *      mag_z (g)
84          *
85          * GPS data (if available)
86          *      connected (1/0)
87          *      locked (1/0)
88          *      nsat (used for solution)
89          *      latitude (°)
90          *      longitude (°)
91          *      altitude (m)
92          *      year (e.g. 2010)
93          *      month (1-12)
94          *      day (1-31)
95          *      hour (0-23)
96          *      minute (0-59)
97          *      second (0-59)
98          *      from_pad_dist (m)
99          *      from_pad_azimuth (deg true)
100          *      from_pad_range (m)
101          *      from_pad_elevation (deg from horizon)
102          *      pdop
103          *      hdop
104          *      vdop
105          *
106          * GPS Sat data
107          *      C/N0 data for all 32 valid SDIDs
108          *
109          * Companion data
110          *      companion_id (1-255. 10 is TeleScience)
111          *      time of last companion data (seconds since boost)
112          *      update_period (0.1-2.55 minimum telemetry interval)
113          *      channels (0-12)
114          *      channel data for all 12 possible channels
115          */
116
117         void write_general_header() {
118                 out.printf("version,serial,flight,call,time,clock,rssi,lqi");
119         }
120
121         double time() {
122                 return series.time(indices);
123         }
124
125         int rssi() {
126                 return (int) series.value(AltosFlightSeries.rssi_name, indices);
127         }
128
129         int status() {
130                 return (int) series.value(AltosFlightSeries.status_name, indices);
131         }
132
133         void write_general() {
134                 double time = time();
135                 out.printf("%s, %d, %d, %s, %8.2f, %8.2f, %4d, %3d",
136                            ALTOS_CSV_VERSION, series.cal_data.serial,
137                            series.cal_data.flight, series.cal_data.callsign,
138                            time, time,
139                            rssi(), status() & 0x7f);
140         }
141
142         void write_flight_header() {
143                 out.printf("state,state_name");
144         }
145
146         int state() {
147                 return (int) series.value(AltosFlightSeries.state_name, indices);
148         }
149
150         void write_flight() {
151                 int state = state();
152                 out.printf("%d,%8s", state, AltosLib.state_name(state));
153         }
154
155         void write_basic_header() {
156                 out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,drogue_voltage,main_voltage");
157         }
158
159         double acceleration() { return series.value(AltosFlightSeries.accel_name, indices); }
160         double pressure() { return series.value(AltosFlightSeries.pressure_name, indices); }
161         double altitude() { return series.value(AltosFlightSeries.altitude_name, indices); }
162         double height() { return series.value(AltosFlightSeries.height_name, indices); }
163         double speed() { return series.value(AltosFlightSeries.speed_name, indices); }
164         double temperature() { return series.value(AltosFlightSeries.temperature_name, indices); }
165         double apogee_voltage() { return series.value(AltosFlightSeries.apogee_voltage_name, indices); }
166         double main_voltage() { return series.value(AltosFlightSeries.main_voltage_name, indices); }
167
168         void write_basic() {
169                 out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f",
170                            acceleration(),
171                            pressure(),
172                            altitude(),
173                            height(),
174                            speed(),
175                            speed(),
176                            temperature(),
177                            apogee_voltage(),
178                            main_voltage());
179         }
180
181         void write_battery_header() {
182                 out.printf("battery_voltage");
183         }
184
185         double battery_voltage() { return series.value(AltosFlightSeries.battery_voltage_name, indices); }
186
187         void write_battery() {
188                 out.printf("%5.2f", battery_voltage());
189         }
190
191         void write_advanced_header() {
192                 out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z");
193         }
194
195         double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); }
196         double accel_across() { return series.value(AltosFlightSeries.accel_across_name, indices); }
197         double accel_through() { return series.value(AltosFlightSeries.accel_through_name, indices); }
198
199         double gyro_roll() { return series.value(AltosFlightSeries.gyro_roll_name, indices); }
200         double gyro_pitch() { return series.value(AltosFlightSeries.gyro_pitch_name, indices); }
201         double gyro_yaw() { return series.value(AltosFlightSeries.gyro_yaw_name, indices); }
202
203         double mag_along() { return series.value(AltosFlightSeries.mag_along_name, indices); }
204         double mag_across() { return series.value(AltosFlightSeries.mag_across_name, indices); }
205         double mag_through() { return series.value(AltosFlightSeries.mag_through_name, indices); }
206
207         void write_advanced() {
208                 out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
209                            accel_along(), accel_across(), accel_through(),
210                            gyro_roll(), gyro_pitch(), gyro_yaw(),
211                            mag_along(), mag_across(), mag_through());
212         }
213
214         void write_gps_header() {
215                 out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,pdop,hdop,vdop");
216         }
217
218         void write_gps() {
219                 AltosGPS        gps = series.gps_before(series.time(indices));
220
221                 AltosGreatCircle from_pad;
222
223                 if (series.cal_data.gps_pad != null && gps != null)
224                         from_pad = new AltosGreatCircle(series.cal_data.gps_pad, gps);
225                 else
226                         from_pad = new AltosGreatCircle();
227
228                 if (gps == null)
229                         gps = new AltosGPS();
230
231                 out.printf("%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",
232                            gps.connected?1:0,
233                            gps.locked?1:0,
234                            gps.nsat,
235                            gps.lat,
236                            gps.lon,
237                            gps.alt,
238                            gps.year,
239                            gps.month,
240                            gps.day,
241                            gps.hour,
242                            gps.minute,
243                            gps.second,
244                            from_pad.distance,
245                            from_pad.range,
246                            from_pad.bearing,
247                            from_pad.elevation,
248                            gps.pdop,
249                            gps.hdop,
250                            gps.vdop);
251         }
252
253         void write_gps_sat_header() {
254                 for(int i = 1; i <= 32; i++) {
255                         out.printf("sat%02d", i);
256                         if (i != 32)
257                                 out.printf(",");
258                 }
259         }
260
261         void write_gps_sat() {
262                 AltosGPS        gps = series.gps_before(series.time(indices));
263                 for(int i = 1; i <= 32; i++) {
264                         int     c_n0 = 0;
265                         if (gps != null && gps.cc_gps_sat != null) {
266                                 for(int j = 0; j < gps.cc_gps_sat.length; j++)
267                                         if (gps.cc_gps_sat[j].svid == i) {
268                                                 c_n0 = gps.cc_gps_sat[j].c_n0;
269                                                 break;
270                                         }
271                         }
272                         out.printf ("%3d", c_n0);
273                         if (i != 32)
274                                 out.printf(",");
275                 }
276         }
277
278         void write_companion_header() {
279 /*
280                 out.printf("companion_id,companion_time,companion_update,companion_channels");
281                 for (int i = 0; i < 12; i++)
282                         out.printf(",companion_%02d", i);
283 */
284         }
285
286         void write_companion() {
287 /*
288                 AltosCompanion companion = state.companion;
289
290                 int     channels_written = 0;
291                 if (companion == null) {
292                         out.printf("0,0,0,0");
293                 } else {
294                         out.printf("%3d,%5.2f,%5.2f,%2d",
295                                    companion.board_id,
296                                    (companion.tick - boost_tick) / 100.0,
297                                    companion.update_period / 100.0,
298                                    companion.channels);
299                         for (; channels_written < companion.channels; channels_written++)
300                                 out.printf(",%5d", companion.companion_data[channels_written]);
301                 }
302                 for (; channels_written < 12; channels_written++)
303                         out.printf(",0");
304 */
305         }
306
307         void write_header() {
308                 out.printf("#"); write_general_header();
309                 if (has_flight_state) {
310                         out.printf(",");
311                         write_flight_header();
312                 }
313                 if (has_basic) {
314                         out.printf(",");
315                         write_basic_header();
316                 }
317                 if (has_battery) {
318                         out.printf(",");
319                         write_battery_header();
320                 }
321                 if (has_advanced) {
322                         out.printf(",");
323                         write_advanced_header();
324                 }
325                 if (has_gps) {
326                         out.printf(",");
327                         write_gps_header();
328                 }
329                 if (has_gps_sat) {
330                         out.printf(",");
331                         write_gps_sat_header();
332                 }
333                 if (has_companion) {
334                         out.printf(",");
335                         write_companion_header();
336                 }
337                 out.printf ("\n");
338         }
339
340         void write_one() {
341                 write_general();
342                 if (has_flight_state) {
343                         out.printf(",");
344                         write_flight();
345                 }
346                 if (has_basic) {
347                         out.printf(",");
348                         write_basic();
349                 }
350                 if (has_battery) {
351                         out.printf(",");
352                         write_battery();
353                 }
354                 if (has_advanced) {
355                         out.printf(",");
356                         write_advanced();
357                 }
358                 if (has_gps) {
359                         out.printf(",");
360                         write_gps();
361                 }
362                 if (has_gps_sat) {
363                         out.printf(",");
364                         write_gps_sat();
365                 }
366                 if (has_companion) {
367                         out.printf(",");
368                         write_companion();
369                 }
370                 out.printf ("\n");
371         }
372
373         private void write() {
374                 if (state() == AltosLib.ao_flight_startup)
375                         return;
376                 if (!header_written) {
377                         write_header();
378                         header_written = true;
379                 }
380                 write_one();
381         }
382
383         private PrintStream out() {
384                 return out;
385         }
386
387         public void close() {
388                 out.close();
389         }
390
391         public void write(AltosFlightSeries series) {
392 //              series.write_comments(out());
393
394                 this.series = series;
395
396                 series.finish();
397
398                 has_flight_state = false;
399                 has_basic = false;
400                 has_battery = false;
401                 has_advanced = false;
402                 has_gps = false;
403                 has_gps_sat = false;
404                 has_companion = false;
405
406                 if (series.has_series(AltosFlightSeries.state_name))
407                         has_flight_state = true;
408                 if (series.has_series(AltosFlightSeries.accel_name) || series.has_series(AltosFlightSeries.pressure_name))
409                         has_basic = true;
410                 if (series.has_series(AltosFlightSeries.battery_voltage_name))
411                         has_battery = true;
412                 if (series.has_series(AltosFlightSeries.accel_across_name))
413                         has_advanced = true;
414
415                 if (series.gps_series != null)
416                         has_gps = true;
417                 if (series.sats_in_view != null)
418                         has_gps_sat = true;
419                 /*
420                 if (state.companion != null)
421                         has_companion = true;
422                 */
423
424                 indices = series.indices();
425
426                 for (;;) {
427                         write();
428                         if (!series.step_indices(indices))
429                                 break;
430                 }
431         }
432
433         public AltosCSV(PrintStream in_out, File in_name) {
434                 name = in_name;
435                 out = in_out;
436         }
437
438         public AltosCSV(File in_name) throws FileNotFoundException {
439                 this(new PrintStream(in_name), in_name);
440         }
441
442         public AltosCSV(String in_string) throws FileNotFoundException {
443                 this(new File(in_string));
444         }
445 }