Merge branch 'bdale-ui' into telestatic-v4
[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                 /* presence of thrust data means this is a test stand */
187                 if (has_thrust)
188                         out.printf("pressure,thrust");
189                 else {
190                         if (has_accel)
191                                 out.printf("acceleration,");
192                         if (has_baro)
193                                 out.printf("pressure,altitude,");
194                         out.printf("height,speed");
195                         if (has_baro)
196                                 out.printf(",temperature");
197                         if (has_pyro)
198                                 out.printf(",drogue_voltage,main_voltage");
199                 }
200         }
201
202         double pressure() { return series.value(AltosFlightSeries.pressure_name, indices); }
203         double thrust() { return series.value(AltosFlightSeries.thrust_name, indices); }
204
205         void write_basic() {
206                 /* presence of thrust data means this is a test stand */
207                 if (has_thrust)
208                         out.printf("%10.2f,%10.2f", 
209                                 pressure(), 
210                                 thrust());
211                 else {
212                         if (has_accel)
213                                 out.printf("%8.2f,", acceleration());
214                         if (has_baro)
215                                 out.printf("%10.2f,%8.2f,",
216                                         pressure(), altitude());
217                         out.printf("%8.2f,%8.2f",
218                                 height(), speed());
219                         if (has_baro)
220                                 out.printf(",%5.1f", temperature());
221                         if (has_pyro)
222                                 out.printf(",%5.2f,%5.2f",
223                                         apogee_voltage(),
224                                         main_voltage());
225                 }
226         }
227
228         void write_battery_header() {
229                 out.printf("battery_voltage");
230         }
231
232         double battery_voltage() { return series.value(AltosFlightSeries.battery_voltage_name, indices); }
233
234         void write_battery() {
235                 out.printf("%5.2f", battery_voltage());
236         }
237
238         void write_motor_pressure_header() {
239                 out.printf("motor_pressure");
240         }
241
242         double motor_pressure() { return series.value(AltosFlightSeries.motor_pressure_name, indices); }
243
244         void write_motor_pressure() {
245                 out.printf("%10.1f", motor_pressure());
246         }
247
248         void write_3d_accel_header() {
249                 out.printf("accel_x,accel_y,accel_z");
250         }
251
252         double accel_along() { return series.value(AltosFlightSeries.accel_along_name, indices); }
253         double accel_across() { return series.value(AltosFlightSeries.accel_across_name, indices); }
254         double accel_through() { return series.value(AltosFlightSeries.accel_through_name, indices); }
255
256         void write_3d_accel() {
257                 out.printf("%7.2f,%7.2f,%7.2f",
258                            accel_along(), accel_across(), accel_through());
259         }
260
261         void write_imu_header() {
262                 out.printf("gyro_roll,gyro_pitch,gyro_yaw,mag_x,mag_y,mag_z,tilt");
263         }
264
265         double gyro_roll() { return series.value(AltosFlightSeries.gyro_roll_name, indices); }
266         double gyro_pitch() { return series.value(AltosFlightSeries.gyro_pitch_name, indices); }
267         double gyro_yaw() { return series.value(AltosFlightSeries.gyro_yaw_name, indices); }
268
269         double mag_along() { return series.value(AltosFlightSeries.mag_along_name, indices); }
270         double mag_across() { return series.value(AltosFlightSeries.mag_across_name, indices); }
271         double mag_through() { return series.value(AltosFlightSeries.mag_through_name, indices); }
272
273         double tilt() { return series.value(AltosFlightSeries.orient_name, indices); }
274
275         void write_imu() {
276                 out.printf("%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f,%7.2f",
277                            gyro_roll(), gyro_pitch(), gyro_yaw(),
278                            mag_along(), mag_across(), mag_through(),
279                            tilt());
280         }
281
282         void write_igniter_header() {
283                 out.printf("pyro");
284                 for (int i = 0; i < series.igniter_voltage.length; i++)
285                         out.printf(",%s", AltosLib.igniter_short_name(i));
286         }
287
288         double pyro() { return series.value(AltosFlightSeries.pyro_voltage_name, indices); }
289
290         double igniter_value(int channel) { return series.value(series.igniter_voltage_name(channel), indices); }
291
292         void write_igniter() {
293                 out.printf("%5.2f", pyro());
294                 for (int i = 0; i < series.igniter_voltage.length; i++)
295                         out.printf(",%5.2f", igniter_value(i));
296         }
297
298         void write_gps_header() {
299                 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");
300         }
301
302         void write_gps() {
303                 AltosGPS        gps = series.gps_before(series.time(indices));
304
305                 AltosGreatCircle from_pad;
306
307                 if (series.cal_data().gps_pad != null && gps != null)
308                         from_pad = new AltosGreatCircle(series.cal_data().gps_pad, gps);
309                 else
310                         from_pad = new AltosGreatCircle();
311
312                 if (gps == null)
313                         gps = new AltosGPS();
314
315                 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",
316                            gps.connected?1:0,
317                            gps.locked?1:0,
318                            gps.nsat,
319                            gps.lat,
320                            gps.lon,
321                            gps.alt,
322                            gps.year,
323                            gps.month,
324                            gps.day,
325                            gps.hour,
326                            gps.minute,
327                            gps.second,
328                            from_pad.distance,
329                            from_pad.range,
330                            from_pad.bearing,
331                            from_pad.elevation,
332                            gps.pdop,
333                            gps.hdop,
334                            gps.vdop);
335         }
336
337         void write_gps_sat_header() {
338                 for(int i = 1; i <= 32; i++) {
339                         out.printf("sat%02d", i);
340                         if (i != 32)
341                                 out.printf(",");
342                 }
343         }
344
345         void write_gps_sat() {
346                 AltosGPS        gps = series.gps_before(series.time(indices));
347                 for(int i = 1; i <= 32; i++) {
348                         int     c_n0 = 0;
349                         if (gps != null && gps.cc_gps_sat != null) {
350                                 for(int j = 0; j < gps.cc_gps_sat.length; j++)
351                                         if (gps.cc_gps_sat[j].svid == i) {
352                                                 c_n0 = gps.cc_gps_sat[j].c_n0;
353                                                 break;
354                                         }
355                         }
356                         out.printf ("%3d", c_n0);
357                         if (i != 32)
358                                 out.printf(",");
359                 }
360         }
361
362         void write_companion_header() {
363 /*
364                 out.printf("companion_id,companion_time,companion_update,companion_channels");
365                 for (int i = 0; i < 12; i++)
366                         out.printf(",companion_%02d", i);
367 */
368         }
369
370         void write_companion() {
371 /*
372                 AltosCompanion companion = state.companion;
373
374                 int     channels_written = 0;
375                 if (companion == null) {
376                         out.printf("0,0,0,0");
377                 } else {
378                         out.printf("%3d,%5.2f,%5.2f,%2d",
379                                    companion.board_id,
380                                    (companion.tick - boost_tick) / 100.0,
381                                    companion.update_period / 100.0,
382                                    companion.channels);
383                         for (; channels_written < companion.channels; channels_written++)
384                                 out.printf(",%5d", companion.companion_data[channels_written]);
385                 }
386                 for (; channels_written < 12; channels_written++)
387                         out.printf(",0");
388 */
389         }
390
391         void write_header() {
392                 out.printf("#"); write_general_header();
393                 if (has_radio) {
394                         out.printf(",");
395                         write_radio_header();
396                 }
397                 if (has_flight_state) {
398                         out.printf(",");
399                         write_flight_header();
400                 }
401                 if (has_basic) {
402                         out.printf(",");
403                         write_basic_header();
404                 }
405                 if (has_battery) {
406                         out.printf(",");
407                         write_battery_header();
408                 }
409                 if (has_motor_pressure) {
410                         out.printf(",");
411                         write_motor_pressure_header();
412                 }
413                 if (has_3d_accel) {
414                         out.printf(",");
415                         write_3d_accel_header();
416                 }
417                 if (has_imu) {
418                         out.printf(",");
419                         write_imu_header();
420                 }
421                 if (has_igniter) {
422                         out.printf(",");
423                         write_igniter_header();
424                 }
425                 if (has_gps) {
426                         out.printf(",");
427                         write_gps_header();
428                 }
429                 if (has_gps_sat) {
430                         out.printf(",");
431                         write_gps_sat_header();
432                 }
433                 if (has_companion) {
434                         out.printf(",");
435                         write_companion_header();
436                 }
437                 out.printf ("\n");
438         }
439
440         void write_one() {
441                 write_general();
442                 if (has_radio) {
443                         out.printf(",");
444                         write_radio();
445                 }
446                 if (has_flight_state) {
447                         out.printf(",");
448                         write_flight();
449                 }
450                 if (has_basic) {
451                         out.printf(",");
452                         write_basic();
453                 }
454                 if (has_battery) {
455                         out.printf(",");
456                         write_battery();
457                 }
458                 if (has_motor_pressure) {
459                         out.printf(",");
460                         write_motor_pressure();
461                 }
462                 if (has_3d_accel) {
463                         out.printf(",");
464                         write_3d_accel();
465                 }
466                 if (has_imu) {
467                         out.printf(",");
468                         write_imu();
469                 }
470                 if (has_igniter) {
471                         out.printf(",");
472                         write_igniter();
473                 }
474                 if (has_gps) {
475                         out.printf(",");
476                         write_gps();
477                 }
478                 if (has_gps_sat) {
479                         out.printf(",");
480                         write_gps_sat();
481                 }
482                 if (has_companion) {
483                         out.printf(",");
484                         write_companion();
485                 }
486                 out.printf ("\n");
487         }
488
489         private void write() {
490                 if (state() == AltosLib.ao_flight_startup)
491                         return;
492                 if (!header_written) {
493                         write_header();
494                         header_written = true;
495                 }
496                 write_one();
497         }
498
499         private PrintStream out() {
500                 return out;
501         }
502
503         public void close() {
504                 out.close();
505         }
506
507         public void write(AltosFlightSeries series) {
508 //              series.write_comments(out());
509
510                 this.series = series;
511
512                 series.finish();
513
514                 has_radio = false;
515                 has_flight_state = false;
516                 has_basic = false;
517                 has_accel = false;
518                 has_baro = false;
519                 has_pyro = false;
520                 has_battery = false;
521                 has_motor_pressure = false;
522                 has_3d_accel = false;
523                 has_imu = false;
524                 has_igniter = false;
525                 has_gps = false;
526                 has_gps_sat = false;
527                 has_companion = false;
528
529                 if (series.has_series(AltosFlightSeries.rssi_name))
530                         has_radio = true;
531                 if (series.has_series(AltosFlightSeries.state_name))
532                         has_flight_state = true;
533                 if (series.has_series(AltosFlightSeries.accel_name)) {
534                         has_basic = true;
535                         has_accel = true;
536                 }
537
538                 if (series.has_series(AltosFlightSeries.thrust_name)) 
539                         has_thrust = true;
540
541                 if (series.has_series(AltosFlightSeries.pressure_name)) {
542                         has_basic = true;
543                         has_baro = true;
544                 }
545                 if (series.has_series(AltosFlightSeries.apogee_voltage_name))
546                         has_pyro = true;
547                 if (series.has_series(AltosFlightSeries.battery_voltage_name))
548                         has_battery = true;
549                 if (series.has_series(AltosFlightSeries.motor_pressure_name))
550                         has_motor_pressure = true;
551                 if (series.has_series(AltosFlightSeries.accel_across_name))
552                         has_3d_accel = true;
553                 if (series.has_series(AltosFlightSeries.gyro_roll_name))
554                         has_imu = true;
555                 if (series.has_series(AltosFlightSeries.pyro_voltage_name))
556                         has_igniter = true;
557
558                 if (series.gps_series != null)
559                         has_gps = true;
560                 if (series.sats_in_view != null)
561                         has_gps_sat = true;
562                 /*
563                 if (state.companion != null)
564                         has_companion = true;
565                 */
566
567                 indices = series.indices();
568
569                 for (;;) {
570                         write();
571                         if (!series.step_indices(indices))
572                                 break;
573                 }
574         }
575
576         public AltosCSV(PrintStream in_out, File in_name) {
577                 name = in_name;
578                 out = in_out;
579         }
580
581         public AltosCSV(File in_name) throws FileNotFoundException {
582                 this(new PrintStream(in_name), in_name);
583         }
584
585         public AltosCSV(String in_string) throws FileNotFoundException {
586                 this(new File(in_string));
587         }
588 }