Merge remote-tracking branch 'mjb/altosui_mjb'
[fw/altos] / altosui / 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; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package altosui;
19
20 import java.io.*;
21 import java.util.*;
22 import org.altusmetrum.AltosLib.*;
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         LinkedList<AltosRecord> pad_records;
31         AltosState              state;
32
33         static final int ALTOS_CSV_VERSION = 5;
34
35         /* Version 4 format:
36          *
37          * General info
38          *      version number
39          *      serial number
40          *      flight number
41          *      callsign
42          *      time (seconds since boost)
43          *      clock (tick count / 100)
44          *      rssi
45          *      link quality
46          *
47          * Flight status
48          *      state
49          *      state name
50          *
51          * Basic sensors
52          *      acceleration (m/s²)
53          *      pressure (mBar)
54          *      altitude (m)
55          *      height (m)
56          *      accelerometer speed (m/s)
57          *      barometer speed (m/s)
58          *      temp (°C)
59          *      battery (V)
60          *      drogue (V)
61          *      main (V)
62          *
63          * Advanced sensors (if available)
64          *      accel_x (m/s²)
65          *      accel_y (m/s²)
66          *      accel_z (m/s²)
67          *      gyro_x (d/s)
68          *      gyro_y (d/s)
69          *      gyro_z (d/s)
70          *      mag_x (g)
71          *      mag_y (g)
72          *      mag_z (g)
73          *
74          * GPS data (if available)
75          *      connected (1/0)
76          *      locked (1/0)
77          *      nsat (used for solution)
78          *      latitude (°)
79          *      longitude (°)
80          *      altitude (m)
81          *      year (e.g. 2010)
82          *      month (1-12)
83          *      day (1-31)
84          *      hour (0-23)
85          *      minute (0-59)
86          *      second (0-59)
87          *      from_pad_dist (m)
88          *      from_pad_azimuth (deg true)
89          *      from_pad_range (m)
90          *      from_pad_elevation (deg from horizon)
91          *      hdop
92          *
93          * GPS Sat data
94          *      C/N0 data for all 32 valid SDIDs
95          *
96          * Companion data
97          *      companion_id (1-255. 10 is TeleScience)
98          *      time of last companion data (seconds since boost)
99          *      update_period (0.1-2.55 minimum telemetry interval)
100          *      channels (0-12)
101          *      channel data for all 12 possible channels
102          */
103
104         void write_general_header() {
105                 out.printf("version,serial,flight,call,time,clock,rssi,lqi");
106         }
107
108         void write_general(AltosRecord record) {
109                 out.printf("%s, %d, %d, %s, %8.2f, %8.2f, %4d, %3d",
110                            ALTOS_CSV_VERSION, record.serial, record.flight, record.callsign,
111                            (double) record.time, (double) record.tick / 100.0,
112                            record.rssi,
113                            record.status & 0x7f);
114         }
115
116         void write_flight_header() {
117                 out.printf("state,state_name");
118         }
119
120         void write_flight(AltosRecord record) {
121                 out.printf("%d,%8s", record.state, record.state());
122         }
123
124         void write_basic_header() {
125                 out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,battery_voltage,drogue_voltage,main_voltage");
126         }
127
128         void write_basic(AltosRecord record) {
129                 out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f",
130                            record.acceleration(),
131                            record.raw_pressure(),
132                            record.raw_altitude(),
133                            record.raw_height(),
134                            record.accel_speed(),
135                            state.baro_speed,
136                            record.temperature(),
137                            record.battery_voltage(),
138                            record.drogue_voltage(),
139                            record.main_voltage());
140         }
141
142         void write_advanced_header() {
143                 out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z");
144         }
145
146         void write_advanced(AltosRecord record) {
147                 AltosIMU        imu = record.imu();
148                 AltosMag        mag = record.mag();
149
150                 if (imu == null)
151                         imu = new AltosIMU();
152                 if (mag == null)
153                         mag = new AltosMag();
154                 out.printf("%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d",
155                            imu.accel_x, imu.accel_y, imu.accel_z,
156                            imu.gyro_x, imu.gyro_y, imu.gyro_z,
157                            mag.x, mag.y, mag.z);
158         }
159
160         void write_gps_header() {
161                 out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop");
162         }
163
164         void write_gps(AltosRecord record) {
165                 AltosGPS        gps = record.gps;
166                 if (gps == null)
167                         gps = new AltosGPS();
168
169                 AltosGreatCircle from_pad = state.from_pad;
170                 if (from_pad == null)
171                         from_pad = new AltosGreatCircle();
172
173                 out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f",
174                            gps.connected?1:0,
175                            gps.locked?1:0,
176                            gps.nsat,
177                            gps.lat,
178                            gps.lon,
179                            gps.alt,
180                            gps.year,
181                            gps.month,
182                            gps.day,
183                            gps.hour,
184                            gps.minute,
185                            gps.second,
186                            from_pad.distance,
187                            state.range,
188                            from_pad.bearing,
189                            state.elevation,
190                            gps.hdop);
191         }
192
193         void write_gps_sat_header() {
194                 for(int i = 1; i <= 32; i++) {
195                         out.printf("sat%02d", i);
196                         if (i != 32)
197                                 out.printf(",");
198                 }
199         }
200
201         void write_gps_sat(AltosRecord record) {
202                 AltosGPS        gps = record.gps;
203                 for(int i = 1; i <= 32; i++) {
204                         int     c_n0 = 0;
205                         if (gps != null && gps.cc_gps_sat != null) {
206                                 for(int j = 0; j < gps.cc_gps_sat.length; j++)
207                                         if (gps.cc_gps_sat[j].svid == i) {
208                                                 c_n0 = gps.cc_gps_sat[j].c_n0;
209                                                 break;
210                                         }
211                         }
212                         out.printf ("%3d", c_n0);
213                         if (i != 32)
214                                 out.printf(",");
215                 }
216         }
217
218         void write_companion_header() {
219                 out.printf("companion_id,companion_time,companion_update,companion_channels");
220                 for (int i = 0; i < 12; i++)
221                         out.printf(",companion_%02d", i);
222         }
223
224         void write_companion(AltosRecord record) {
225                 AltosRecordCompanion companion = record.companion;
226
227                 int     channels_written = 0;
228                 if (companion == null) {
229                         out.printf("0,0,0,0");
230                 } else {
231                         out.printf("%3d,%5.2f,%5.2f,%2d",
232                                    companion.board_id,
233                                    (companion.tick - boost_tick) / 100.0,
234                                    companion.update_period / 100.0,
235                                    companion.channels);
236                         for (; channels_written < companion.channels; channels_written++)
237                                 out.printf(",%5d", companion.companion_data[channels_written]);
238                 }
239                 for (; channels_written < 12; channels_written++)
240                         out.printf(",0");
241         }
242
243         void write_header(boolean advanced, boolean gps, boolean companion) {
244                 out.printf("#"); write_general_header();
245                 out.printf(","); write_flight_header();
246                 out.printf(","); write_basic_header();
247                 if (advanced)
248                         out.printf(","); write_advanced_header();
249                 if (gps) {
250                         out.printf(","); write_gps_header();
251                         out.printf(","); write_gps_sat_header();
252                 }
253                 if (companion) {
254                         out.printf(","); write_companion_header();
255                 }
256                 out.printf ("\n");
257         }
258
259         void write_one(AltosRecord record) {
260                 state = new AltosState(record, state);
261                 write_general(record); out.printf(",");
262                 write_flight(record); out.printf(",");
263                 write_basic(record); out.printf(",");
264                 if (record.imu() != null || record.mag() != null)
265                         write_advanced(record);
266                 if (record.gps != null) {
267                         out.printf(",");
268                         write_gps(record); out.printf(",");
269                         write_gps_sat(record);
270                 }
271                 if (record.companion != null) {
272                         out.printf(",");
273                         write_companion(record);
274                 }
275                 out.printf ("\n");
276         }
277
278         void flush_pad() {
279                 while (!pad_records.isEmpty()) {
280                         write_one (pad_records.remove());
281                 }
282         }
283
284         public void write(AltosRecord record) {
285                 if (record.state == Altos.ao_flight_startup)
286                         return;
287                 if (!header_written) {
288                         write_header(record.imu() != null || record.mag() != null,
289                                      record.gps != null, record.companion != null);
290                         header_written = true;
291                 }
292                 if (!seen_boost) {
293                         if (record.state >= Altos.ao_flight_boost) {
294                                 seen_boost = true;
295                                 boost_tick = record.tick;
296                                 flush_pad();
297                         }
298                 }
299                 if (seen_boost)
300                         write_one(record);
301                 else
302                         pad_records.add(record);
303         }
304
305         public PrintStream out() {
306                 return out;
307         }
308
309         public void close() {
310                 if (!pad_records.isEmpty()) {
311                         boost_tick = pad_records.element().tick;
312                         flush_pad();
313                 }
314                 out.close();
315         }
316
317         public void write(AltosRecordIterable iterable) {
318                 iterable.write_comments(out());
319                 for (AltosRecord r : iterable)
320                         write(r);
321         }
322
323         public AltosCSV(PrintStream in_out, File in_name) {
324                 name = in_name;
325                 out = in_out;
326                 pad_records = new LinkedList<AltosRecord>();
327         }
328
329         public AltosCSV(File in_name) throws FileNotFoundException {
330                 this(new PrintStream(in_name), in_name);
331         }
332
333         public AltosCSV(String in_string) throws FileNotFoundException {
334                 this(new File(in_string));
335         }
336 }