765eb44598f337b6fd9e58da4413c5346a021ebf
[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_14;
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_call;
32         boolean                 has_basic;
33         boolean                 has_accel;
34         boolean                 has_baro;
35         boolean                 has_pyro;
36         boolean                 has_radio;
37         boolean                 has_battery;
38         boolean                 has_flight_state;
39         boolean                 has_3d_accel;
40         boolean                 has_imu;
41         boolean                 has_igniter;
42         boolean                 has_gps;
43         boolean                 has_gps_sat;
44         boolean                 has_companion;
45         boolean                 has_motor_pressure;
46
47         AltosFlightSeries       series;
48         int[]                   indices;
49
50         static final int ALTOS_CSV_VERSION = 6;
51
52         /* Version 4 format:
53          *
54          * General info
55          *      version number
56          *      serial number
57          *      flight number
58          *      callsign
59          *      time (seconds since boost)
60          *
61          * Radio info (if available)
62          *      rssi
63          *      link quality
64          *
65          * Flight status
66          *      state
67          *      state name
68          *
69          * Basic sensors
70          *      acceleration (m/s²)
71          *      pressure (mBar)
72          *      altitude (m)
73          *      height (m)
74          *      accelerometer speed (m/s)
75          *      barometer speed (m/s)
76          *      temp (°C)
77          *      drogue (V)
78          *      main (V)
79          *
80          * Battery
81          *      battery (V)
82          *
83          * Advanced sensors (if available)
84          *      accel_x (m/s²)
85          *      accel_y (m/s²)
86          *      accel_z (m/s²)
87          *      gyro_x (d/s)
88          *      gyro_y (d/s)
89          *      gyro_z (d/s)
90          *      mag_x (g)
91          *      mag_y (g)
92          *      mag_z (g)
93          *      tilt (d)
94          *
95          * Extra igniter voltages (if available)
96          *      pyro (V)
97          *      igniter_a (V)
98          *      igniter_b (V)
99          *      igniter_c (V)
100          *      igniter_d (V)
101          *
102          * GPS data (if available)
103          *      connected (1/0)
104          *      locked (1/0)
105          *      nsat (used for solution)
106          *      latitude (°)
107          *      longitude (°)
108          *      altitude (m)
109          *      year (e.g. 2010)
110          *      month (1-12)
111          *      day (1-31)
112          *      hour (0-23)
113          *      minute (0-59)
114          *      second (0-59)
115          *      from_pad_dist (m)
116          *      from_pad_azimuth (deg true)
117          *      from_pad_range (m)
118          *      from_pad_elevation (deg from horizon)
119          *      pdop
120          *      hdop
121          *      vdop
122          *
123          * GPS Sat data
124          *      C/N0 data for all 32 valid SDIDs
125          *
126          * Companion data
127          *      companion_id (1-255. 10 is TeleScience)
128          *      time of last companion data (seconds since boost)
129          *      update_period (0.1-2.55 minimum telemetry interval)
130          *      channels (0-12)
131          *      channel data for all 12 possible channels
132          */
133
134         void write_general_header() {
135                 out.printf("version,serial,flight");
136                 if (series.cal_data().callsign != null)
137                         out.printf(",call");
138                 out.printf(",time");
139         }
140
141         double time() {
142                 return series.time(indices);
143         }
144
145         void write_general() {
146                 out.printf("%s, %d, %d",
147                            ALTOS_CSV_VERSION,
148                            series.cal_data().serial,
149                            series.cal_data().flight);
150                 if (series.cal_data().callsign != null)
151                         out.printf(",%s", series.cal_data().callsign);
152                 out.printf(", %8.2f", time());
153         }
154
155         void write_radio_header() {
156                 out.printf("rssi,lqi");
157         }
158
159         int rssi() {
160                 return (int) series.value(AltosFlightSeries.rssi_name, indices);
161         }
162
163         int status() {
164                 return (int) series.value(AltosFlightSeries.status_name, indices);
165         }
166
167         void write_radio() {
168                 out.printf("%4d, %3d",
169                            rssi(), status() & 0x7f);
170         }
171
172         void write_flight_header() {
173                 out.printf("state,state_name");
174         }
175
176         int state() {
177                 return (int) series.value(AltosFlightSeries.state_name, indices);
178         }
179
180         void write_flight() {
181                 int state = state();
182                 out.printf("%2d,%8s", state, AltosLib.state_name(state));
183         }
184
185         void write_basic_header() {
186                 if (has_accel)
187                         out.printf("acceleration,");
188                 if (has_baro)
189                         out.printf("pressure,altitude,");
190                 out.printf("height,speed");
191                 if (has_baro)
192                         out.printf(",temperature");
193                 if (has_pyro)
194                         out.printf(",drogue_voltage,main_voltage");
195         }
196
197         double acceleration() { return series.value(AltosFlightSeries.accel_name, indices); }
198         double pressure() { return series.value(AltosFlightSeries.pressure_name, indices); }
199         double altitude() { return series.value(AltosFlightSeries.altitude_name, indices); }
200         double height() { return series.value(AltosFlightSeries.height_name, indices); }
201         double speed() { return series.value(AltosFlightSeries.speed_name, indices); }
202         double temperature() { return series.value(AltosFlightSeries.temperature_name, indices); }
203         double apogee_voltage() { return series.value(AltosFlightSeries.apogee_voltage_name, indices); }
204         double main_voltage() { return series.value(AltosFlightSeries.main_voltage_name, indices); }
205
206         void write_basic() {
207                 if (has_accel)
208                         out.printf("%8.2f,", acceleration());
209                 if (has_baro)
210                         out.printf("%10.2f,%8.2f,",
211                                    pressure(), altitude());
212                 out.printf("%8.2f,%8.2f",
213                            height(), speed());
214                 if (has_baro)
215                         out.printf(",%5.1f", temperature());
216                 if (has_pyro)
217                         out.printf(",%5.2f,%5.2f",
218                                    apogee_voltage(),
219                                    main_voltage());
220         }
221
222         void write_battery_header() {
223                 out.printf("battery_voltage");
224         }
225
226         double battery_voltage() { return series.value(AltosFlightSeries.battery_voltage_name, indices); }
227
228         void write_battery() {
229                 out.printf("%5.2f", battery_voltage());
230         }
231
232         void write_motor_pressure_header() {
233                 out.printf("motor_pressure");
234         }
235
236         double motor_pressure() { return series.value(AltosFlightSeries.motor_pressure_name, indices); }
237
238         void write_motor_pressure() {
239                 out.printf("%10.1f", motor_pressure());
240         }
241
242         void write_3d_accel_header() {
243                 out.printf("accel_x,accel_y,accel_z");
244         }
245
246         double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); }
247         double accel_across() { return series.value(AltosFlightSeries.accel_across_name, indices); }
248         double accel_through() { return series.value(AltosFlightSeries.accel_through_name, indices); }
249
250         void write_3d_accel() {
251                 out.printf("%7.2f,%7.2f,%7.2f",
252                            accel_along(), accel_across(), accel_through());
253         }
254
255         void write_imu_header() {
256                 out.printf("gyro_roll,gyro_pitch,gyro_yaw,mag_x,mag_y,mag_z,tilt");
257         }
258
259         double gyro_roll() { return series.value(AltosFlightSeries.gyro_roll_name, indices); }
260         double gyro_pitch() { return series.value(AltosFlightSeries.gyro_pitch_name, indices); }
261         double gyro_yaw() { return series.value(AltosFlightSeries.gyro_yaw_name, indices); }
262
263         double mag_along() { return series.value(AltosFlightSeries.mag_along_name, indices); }
264         double mag_across() { return series.value(AltosFlightSeries.mag_across_name, indices); }
265         double mag_through() { return series.value(AltosFlightSeries.mag_through_name, indices); }
266
267         double tilt() { return series.value(AltosFlightSeries.orient_name, indices); }
268
269         void write_imu() {
270                 out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
271                            gyro_roll(), gyro_pitch(), gyro_yaw(),
272                            mag_along(), mag_across(), mag_through(),
273                            tilt());
274         }
275
276         void write_igniter_header() {
277                 out.printf("pyro");
278                 for (int i = 0; i < series.igniter_voltage.length; i++)
279                         out.printf(",%s", AltosLib.igniter_short_name(i));
280         }
281
282         double pyro() { return series.value(AltosFlightSeries.pyro_voltage_name, indices); }
283
284         double igniter_value(int channel) { return series.value(series.igniter_voltage_name(channel), indices); }
285
286         void write_igniter() {
287                 out.printf("%5.2f", pyro());
288                 for (int i = 0; i < series.igniter_voltage.length; i++)
289                         out.printf(",%5.2f", igniter_value(i));
290         }
291
292         void write_gps_header() {
293                 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");
294         }
295
296         void write_gps() {
297                 AltosGPS        gps = series.gps_before(series.time(indices));
298
299                 AltosGreatCircle from_pad;
300
301                 if (series.cal_data().gps_pad != null && gps != null)
302                         from_pad = new AltosGreatCircle(series.cal_data().gps_pad, gps);
303                 else
304                         from_pad = new AltosGreatCircle();
305
306                 if (gps == null)
307                         gps = new AltosGPS();
308
309                 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",
310                            gps.connected?1:0,
311                            gps.locked?1:0,
312                            gps.nsat,
313                            gps.lat,
314                            gps.lon,
315                            gps.alt,
316                            gps.year,
317                            gps.month,
318                            gps.day,
319                            gps.hour,
320                            gps.minute,
321                            gps.second,
322                            from_pad.distance,
323                            from_pad.range,
324                            from_pad.bearing,
325                            from_pad.elevation,
326                            gps.pdop,
327                            gps.hdop,
328                            gps.vdop);
329         }
330
331         void write_gps_sat_header() {
332                 for(int i = 1; i <= 32; i++) {
333                         out.printf("sat%02d", i);
334                         if (i != 32)
335                                 out.printf(",");
336                 }
337         }
338
339         void write_gps_sat() {
340                 AltosGPS        gps = series.gps_before(series.time(indices));
341                 for(int i = 1; i <= 32; i++) {
342                         int     c_n0 = 0;
343                         if (gps != null && gps.cc_gps_sat != null) {
344                                 for(int j = 0; j < gps.cc_gps_sat.length; j++)
345                                         if (gps.cc_gps_sat[j].svid == i) {
346                                                 c_n0 = gps.cc_gps_sat[j].c_n0;
347                                                 break;
348                                         }
349                         }
350                         out.printf ("%3d", c_n0);
351                         if (i != 32)
352                                 out.printf(",");
353                 }
354         }
355
356         void write_companion_header() {
357 /*
358                 out.printf("companion_id,companion_time,companion_update,companion_channels");
359                 for (int i = 0; i < 12; i++)
360                         out.printf(",companion_%02d", i);
361 */
362         }
363
364         void write_companion() {
365 /*
366                 AltosCompanion companion = state.companion;
367
368                 int     channels_written = 0;
369                 if (companion == null) {
370                         out.printf("0,0,0,0");
371                 } else {
372                         out.printf("%3d,%5.2f,%5.2f,%2d",
373                                    companion.board_id,
374                                    (companion.tick - boost_tick) / 100.0,
375                                    companion.update_period / 100.0,
376                                    companion.channels);
377                         for (; channels_written < companion.channels; channels_written++)
378                                 out.printf(",%5d", companion.companion_data[channels_written]);
379                 }
380                 for (; channels_written < 12; channels_written++)
381                         out.printf(",0");
382 */
383         }
384
385         void write_header() {
386                 out.printf("#"); write_general_header();
387                 if (has_radio) {
388                         out.printf(",");
389                         write_radio_header();
390                 }
391                 if (has_flight_state) {
392                         out.printf(",");
393                         write_flight_header();
394                 }
395                 if (has_basic) {
396                         out.printf(",");
397                         write_basic_header();
398                 }
399                 if (has_battery) {
400                         out.printf(",");
401                         write_battery_header();
402                 }
403                 if (has_motor_pressure) {
404                         out.printf(",");
405                         write_motor_pressure_header();
406                 }
407                 if (has_3d_accel) {
408                         out.printf(",");
409                         write_3d_accel_header();
410                 }
411                 if (has_igniter) {
412                         out.printf(",");
413                         write_igniter_header();
414                 }
415                 if (has_gps) {
416                         out.printf(",");
417                         write_gps_header();
418                 }
419                 if (has_gps_sat) {
420                         out.printf(",");
421                         write_gps_sat_header();
422                 }
423                 if (has_companion) {
424                         out.printf(",");
425                         write_companion_header();
426                 }
427                 out.printf ("\n");
428         }
429
430         void write_one() {
431                 write_general();
432                 if (has_radio) {
433                         out.printf(",");
434                         write_radio();
435                 }
436                 if (has_flight_state) {
437                         out.printf(",");
438                         write_flight();
439                 }
440                 if (has_basic) {
441                         out.printf(",");
442                         write_basic();
443                 }
444                 if (has_battery) {
445                         out.printf(",");
446                         write_battery();
447                 }
448                 if (has_motor_pressure) {
449                         out.printf(",");
450                         write_motor_pressure();
451                 }
452                 if (has_3d_accel) {
453                         out.printf(",");
454                         write_3d_accel();
455                 }
456                 if (has_imu) {
457                         out.printf(",");
458                         write_imu();
459                 }
460                 if (has_igniter) {
461                         out.printf(",");
462                         write_igniter();
463                 }
464                 if (has_gps) {
465                         out.printf(",");
466                         write_gps();
467                 }
468                 if (has_gps_sat) {
469                         out.printf(",");
470                         write_gps_sat();
471                 }
472                 if (has_companion) {
473                         out.printf(",");
474                         write_companion();
475                 }
476                 out.printf ("\n");
477         }
478
479         private void write() {
480                 if (state() == AltosLib.ao_flight_startup)
481                         return;
482                 if (!header_written) {
483                         write_header();
484                         header_written = true;
485                 }
486                 write_one();
487         }
488
489         private PrintStream out() {
490                 return out;
491         }
492
493         public void close() {
494                 out.close();
495         }
496
497         public void write(AltosFlightSeries series) {
498 //              series.write_comments(out());
499
500                 this.series = series;
501
502                 series.finish();
503
504                 has_radio = false;
505                 has_flight_state = false;
506                 has_basic = false;
507                 has_accel = false;
508                 has_baro = false;
509                 has_pyro = false;
510                 has_battery = false;
511                 has_motor_pressure = false;
512                 has_3d_accel = false;
513                 has_imu = false;
514                 has_igniter = false;
515                 has_gps = false;
516                 has_gps_sat = false;
517                 has_companion = false;
518
519                 if (series.has_series(AltosFlightSeries.rssi_name))
520                         has_radio = true;
521                 if (series.has_series(AltosFlightSeries.state_name))
522                         has_flight_state = true;
523                 if (series.has_series(AltosFlightSeries.accel_name)) {
524                         has_basic = true;
525                         has_accel = true;
526                 }
527                 if (series.has_series(AltosFlightSeries.pressure_name)) {
528                         has_basic = true;
529                         has_baro = true;
530                 }
531                 if (series.has_series(AltosFlightSeries.apogee_voltage_name))
532                         has_pyro = true;
533                 if (series.has_series(AltosFlightSeries.battery_voltage_name))
534                         has_battery = true;
535                 if (series.has_series(AltosFlightSeries.motor_pressure_name))
536                         has_motor_pressure = true;
537                 if (series.has_series(AltosFlightSeries.accel_across_name))
538                         has_3d_accel = true;
539                 if (series.has_series(AltosFlightSeries.gyro_roll_name))
540                         has_imu = true;
541                 if (series.has_series(AltosFlightSeries.pyro_voltage_name))
542                         has_igniter = true;
543
544                 if (series.gps_series != null)
545                         has_gps = true;
546                 if (series.sats_in_view != null)
547                         has_gps_sat = true;
548                 /*
549                 if (state.companion != null)
550                         has_companion = true;
551                 */
552
553                 indices = series.indices();
554
555                 for (;;) {
556                         write();
557                         if (!series.step_indices(indices))
558                                 break;
559                 }
560         }
561
562         public AltosCSV(PrintStream in_out, File in_name) {
563                 name = in_name;
564                 out = in_out;
565         }
566
567         public AltosCSV(File in_name) throws FileNotFoundException {
568                 this(new PrintStream(in_name), in_name);
569         }
570
571         public AltosCSV(String in_string) throws FileNotFoundException {
572                 this(new File(in_string));
573         }
574 }