0c0500f9fbec6cdeff4cdd1e4357aea0c47fe598
[fw/altos] / ao-tools / ao-eeprom / ao-eeprom.c
1 /*
2  * Copyright © 2009 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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <unistd.h>
23 #include <getopt.h>
24 #include <ao-eeprom-read.h>
25
26 static const struct option options[] = {
27         { .name = "raw", .has_arg = 0, .val = 'r' },
28         { .name = "csum", .has_arg = 0, .val = 'c' },
29         { .name = "verbose", .has_arg = 0, .val = 'v' },
30         { .name = "len", .has_arg = 1, .val = 'l' },
31         { 0, 0, 0, 0},
32 };
33
34 static void usage(char *program)
35 {
36         fprintf(stderr, "usage: %s [--raw] [--csum] [--verbose] [--len <record-len>] {flight.eeprom} ...\n", program);
37         exit(1);
38 }
39
40 static bool
41 ao_csum_valid(uint8_t *d, int len)
42 {
43         uint8_t sum = 0x5a;
44         int i;
45         for (i = 0; i < len; i++)
46                 sum += d[i];
47         return sum == 0;
48 }
49
50 static void
51 ao_ms5607(uint32_t pres, uint32_t temp, struct ao_eeprom *eeprom, bool is_ms5611)
52 {
53         struct ao_ms5607_sample ms5607_sample = { .pres = pres, .temp = temp };
54         struct ao_ms5607_value ms5607_value;
55
56         ao_ms5607_convert(&ms5607_sample, &ms5607_value,
57                           &eeprom->ms5607_prom, is_ms5611);
58         printf(" pres %9u temp %9u (%7.3f kPa %6.2f°C)",
59                pres,
60                temp,
61                ms5607_value.pres / 1000.0,
62                ms5607_value.temp / 100.0);
63 }
64
65 #define GRAVITY 9.80665
66
67 static void
68 ao_accel(int16_t accel, struct ao_eeprom *eeprom)
69 {
70         double accel_2g = eeprom->config.accel_minus_g - eeprom->config.accel_plus_g;
71         double accel_scale = GRAVITY * 2.0 / accel_2g;
72         printf(" accel %6d (%7.2f m/s²)",
73                accel, (eeprom->config.accel_plus_g - accel) * accel_scale);
74 }
75
76 static const char *state_names[] = {
77         "startup",
78         "idle",
79         "pad",
80         "boost",
81         "fast",
82         "coast",
83         "drogue",
84         "main",
85         "landed",
86         "invalid"
87 };
88
89 #define NUM_STATE       (sizeof state_names/sizeof state_names[0])
90
91 static const char *
92 ao_state_name(uint16_t state)
93 {
94         if (state < NUM_STATE)
95                 return state_names[state];
96         return "UNKNOWN";
97 }
98
99 static void
100 ao_state(uint16_t state, uint16_t reason)
101 {
102         printf(" state %5u %s reason %5u",
103                state, ao_state_name(state), reason);
104 }
105
106 static void
107 ao_volts(const char *name, int16_t value, int16_t max_adc, double ref, double r1, double r2)
108 {
109         printf(" %s %5d",
110                name, value);
111         if (r1 && r2 && ref)
112                printf("(%6.3f V)",
113                       ref * ((double) value / max_adc) * (r1 + r2) / r2);
114 }
115
116 #if 0
117 static uint16_t
118 uint16(uint8_t *bytes, int off)
119 {
120         return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
121 }
122
123 static int16_t
124 int16(uint8_t *bytes, int off)
125 {
126         return (int16_t) uint16(bytes, off);
127 }
128
129 static uint32_t
130 uint32(uint8_t *bytes, int off)
131 {
132         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
133                 (((uint32_t) bytes[off+2]) << 16) |
134                 (((uint32_t) bytes[off+3]) << 24);
135 }
136
137 static int32_t
138 int32(uint8_t *bytes, int off)
139 {
140         return (int32_t) uint32(bytes, off);
141 }
142 #endif
143
144 static uint32_t
145 uint24(uint8_t *bytes, int off)
146 {
147         return (uint32_t) bytes[off] | (((uint32_t) bytes[off+1]) << 8) |
148                 (((uint32_t) bytes[off+2]) << 16);
149 }
150
151 static int32_t
152 int24(uint8_t *bytes, int off)
153 {
154         return (int32_t) uint24(bytes, off);
155 }
156
157 int
158 main (int argc, char **argv)
159 {
160         struct ao_eeprom        *eeprom;
161         FILE                    *file;
162         int                     c;
163         bool                    raw = false;
164         bool                    csum = false;
165         bool                    verbose = false;
166         int                     arg_len = 0;
167         char                    *end;
168         int                     ret = 0;
169         int                     i;
170
171         while ((c = getopt_long(argc, argv, "rcvl:", options, NULL)) != -1) {
172                 switch (c) {
173                 case 'r':
174                         raw = true;
175                         break;
176                 case 'c':
177                         csum = true;
178                         break;
179                 case 'v':
180                         verbose = true;
181                         break;
182                 case 'l':
183                         arg_len = strtol(optarg, &end, 0);
184                         if (!*optarg || *end)
185                                 usage(argv[0]);
186                         break;
187                 default:
188                         usage(argv[0]);
189                         break;
190                 }
191         }
192         for (i = optind; i < argc; i++) {
193                 file = fopen(argv[i], "r");
194                 if (!file) {
195                         perror(argv[i]);
196                         ret++;
197                         continue;
198                 }
199                 eeprom = ao_eeprom_read(file);
200                 fclose(file);
201                 if (!eeprom) {
202                         perror(argv[i]);
203                         ret++;
204                         continue;
205                 }
206                 int     len = 0;
207                 bool    is_ms5611 = false;
208
209                 double  sense_r1 = 0.0, sense_r2 = 0.0;
210                 double  batt_r1 = 0.0, batt_r2 = 0.0;
211                 double  adc_ref = 0.0;
212                 int16_t max_adc = 0;
213
214                 switch (eeprom->log_format) {
215                 case AO_LOG_FORMAT_TELEMEGA_OLD:
216                         len = 32;
217                         break;
218                 case AO_LOG_FORMAT_EASYMINI1:
219                         len = 16;
220                         max_adc = 32767;
221                         if (eeprom->serial_number < 1000)
222                                 adc_ref = 3.0;
223                         else
224                                 adc_ref = 3.3;
225                         batt_r1 = sense_r1 = 100e3;
226                         batt_r2 = sense_r2 = 27e3;
227                         break;
228                 case AO_LOG_FORMAT_TELEMETRUM:
229                         len = 16;
230                         max_adc = 4095;
231                         adc_ref = 3.3;
232                         batt_r1 = 5600;
233                         batt_r2 = 10000;
234                         sense_r1 = 100e3;
235                         sense_r2 = 27e3;
236                         break;
237                 case AO_LOG_FORMAT_TELEMINI2:
238                         len = 16;
239                         break;
240                 case AO_LOG_FORMAT_TELEGPS:
241                         len = 32;
242                         break;
243                 case AO_LOG_FORMAT_TELEMEGA:
244                         len = 32;
245                         max_adc = 4095;
246                         adc_ref = 3.3;
247                         batt_r1 = 5600;
248                         batt_r2 = 10000;
249                         sense_r1 = 100e3;
250                         sense_r2 = 27e3;
251                         break;
252                 case AO_LOG_FORMAT_DETHERM:
253                         len = 16;
254                         break;
255                 case AO_LOG_FORMAT_TELEMINI3:
256                         len = 16;
257                         max_adc = 4095;
258                         adc_ref = 3.3;
259                         batt_r1 = 5600;
260                         batt_r2 = 10000;
261                         sense_r1 = 100e3;
262                         sense_r2 = 27e3;
263                         break;
264                 case AO_LOG_FORMAT_TELEFIRETWO:
265                         len = 32;
266                         break;
267                 case AO_LOG_FORMAT_EASYMINI2:
268                         len = 16;
269                         max_adc = 4095;
270                         adc_ref = 3.3;
271                         batt_r1 = sense_r1 = 100e3;
272                         batt_r2 = sense_r2 = 27e3;
273                         break;
274                 case AO_LOG_FORMAT_TELEMEGA_3:
275                         len = 32;
276                         max_adc = 4095;
277                         adc_ref = 3.3;
278                         batt_r1 = 5600;
279                         batt_r2 = 10000;
280                         sense_r1 = 100e3;
281                         sense_r2 = 27e3;
282                         break;
283                 case AO_LOG_FORMAT_EASYMEGA_2:
284                         len = 32;
285                         max_adc = 4095;
286                         adc_ref = 3.3;
287                         batt_r1 = 5600;
288                         batt_r2 = 10000;
289                         sense_r1 = 100e3;
290                         sense_r2 = 27e3;
291                         break;
292                 case AO_LOG_FORMAT_TELESTATIC:
293                         len = 32;
294                         break;
295                 case AO_LOG_FORMAT_MICROPEAK2:
296                         len = 2;
297                         break;
298                 }
299                 if (arg_len)
300                         len = arg_len;
301                 printf("config major %d minor %d log format %d total %u len %d\n",
302                        eeprom->config.major,
303                        eeprom->config.minor,
304                        eeprom->log_format,
305                        eeprom->len,
306                        len);
307                 uint32_t        pos;
308                 for (pos = 0; pos < eeprom->len; pos += len) {
309                         int i;
310                         if (raw) {
311                                 printf("%9u", pos);
312                                 for (i = 0; i < len; i++)
313                                         printf(" %02x", eeprom->data[pos + i]);
314                         } else {
315                                 struct ao_log_mega *log_mega;
316                                 struct ao_log_mini *log_mini;
317                                 struct ao_log_metrum *log_metrum;
318                                 struct ao_log_gps *log_gps;
319
320                                 if (!csum && !ao_csum_valid(&eeprom->data[pos], len)) {
321                                         if (verbose)
322                                                 printf("\tchecksum error at %d\n", pos);
323                                         continue;
324                                 }
325
326                                 struct ao_log_header *log_header = (struct ao_log_header *) &eeprom->data[pos];
327
328                                 printf("type %c tick %5u", log_header->type, log_header->tick);
329
330                                 switch (eeprom->log_format) {
331                                 case AO_LOG_FORMAT_TELEMEGA_OLD:
332                                 case AO_LOG_FORMAT_TELEMEGA:
333                                 case AO_LOG_FORMAT_TELEMEGA_3:
334                                 case AO_LOG_FORMAT_EASYMEGA_2:
335                                         log_mega = (struct ao_log_mega *) &eeprom->data[pos];
336                                         switch (log_mega->type) {
337                                         case AO_LOG_FLIGHT:
338                                                 printf(" serial %5u flight %5u ground_accel %6d ground_pres %9u",
339                                                        eeprom->serial_number,
340                                                        log_mega->u.flight.flight,
341                                                        log_mega->u.flight.ground_accel,
342                                                        log_mega->u.flight.ground_pres);
343                                                 printf(" along %6d aross %6d through %6d",
344                                                        log_mega->u.flight.ground_accel_along,
345                                                        log_mega->u.flight.ground_accel_across,
346                                                        log_mega->u.flight.ground_accel_through);
347                                                 printf(" roll %6d pitch %6d yaw %6d",
348                                                        log_mega->u.flight.ground_roll,
349                                                        log_mega->u.flight.ground_pitch,
350                                                        log_mega->u.flight.ground_yaw);
351                                                 break;
352                                         case AO_LOG_STATE:
353                                                 ao_state(log_mega->u.state.state,
354                                                          log_mega->u.state.reason);
355                                                 break;
356                                         case AO_LOG_SENSOR:
357                                                 ao_ms5607(log_mega->u.sensor.pres,
358                                                           log_mega->u.sensor.temp,
359                                                           eeprom, is_ms5611);
360                                                 printf(" accel_x %6d accel_y %6d accel_z %6d",
361                                                        log_mega->u.sensor.accel_x,
362                                                        log_mega->u.sensor.accel_y,
363                                                        log_mega->u.sensor.accel_z);
364                                                 printf (" gyro_x %6d gyro_y %6d gyro_z %6d",
365                                                        log_mega->u.sensor.gyro_x,
366                                                        log_mega->u.sensor.gyro_y,
367                                                        log_mega->u.sensor.gyro_z);
368                                                 printf (" mag_x %6d mag_y %6d mag_z %6d",
369                                                        log_mega->u.sensor.mag_x,
370                                                        log_mega->u.sensor.mag_y,
371                                                        log_mega->u.sensor.mag_z);
372                                                 ao_accel(log_mega->u.sensor.accel, eeprom);
373                                                 break;
374                                         case AO_LOG_TEMP_VOLT:
375                                                 ao_volts("v_batt",
376                                                          log_mega->u.volt.v_batt,
377                                                          max_adc,
378                                                          adc_ref,
379                                                          batt_r1, batt_r2);
380                                                 ao_volts("v_pbatt",
381                                                          log_mega->u.volt.v_pbatt,
382                                                          max_adc,
383                                                          adc_ref,
384                                                          sense_r1, sense_r2);
385                                                 printf(" n_sense %1d",
386                                                        log_mega->u.volt.n_sense);
387                                                 for (i = 0; i < log_mega->u.volt.n_sense; i++) {
388                                                         char name[10];
389                                                         sprintf(name, "sense%d", i);
390                                                         ao_volts(name,
391                                                                  log_mega->u.volt.sense[i],
392                                                                  max_adc,
393                                                                  adc_ref,
394                                                                  sense_r1, sense_r2);
395                                                 }
396                                                 printf(" pyro %04x", log_mega->u.volt.pyro);
397                                                 break;
398                                         case AO_LOG_GPS_TIME:
399                                                 printf(" lat %10.7f° lon %10.7f° alt %8d m",
400                                                        log_mega->u.gps.latitude / 10000000.0,
401                                                        log_mega->u.gps.longitude/ 10000000.0,
402                                                        (int32_t) (log_mega->u.gps.altitude_low |
403                                                                   (log_mega->u.gps.altitude_high << 16)));
404                                                 printf(" time %02d:%02d:%02d 20%02d-%02d-%02d flags %02x",
405                                                        log_mega->u.gps.hour,
406                                                        log_mega->u.gps.minute,
407                                                        log_mega->u.gps.second,
408                                                        log_mega->u.gps.year,
409                                                        log_mega->u.gps.month,
410                                                        log_mega->u.gps.day,
411                                                        log_mega->u.gps.flags);
412                                                 printf(" course %3d ground_speed %5u climb_rate %6d pdop %3d hdop %3d vdop %3d mode %3d",
413                                                        log_mega->u.gps.course,
414                                                        log_mega->u.gps.ground_speed,
415                                                        log_mega->u.gps.climb_rate,
416                                                        log_mega->u.gps.pdop,
417                                                        log_mega->u.gps.hdop,
418                                                        log_mega->u.gps.vdop,
419                                                        log_mega->u.gps.mode);
420                                                 break;
421                                         case AO_LOG_GPS_SAT:
422                                                 printf(" channels %2d",
423                                                        log_mega->u.gps_sat.channels);
424                                                 for (i = 0; i < 12; i++) {
425                                                         printf(" svid %3d c_n %2d",
426                                                                log_mega->u.gps_sat.sats[i].svid,
427                                                                log_mega->u.gps_sat.sats[i].c_n);
428                                                 }
429                                                 break;
430                                         }
431                                         break;
432                                 case AO_LOG_FORMAT_EASYMINI1:
433                                 case AO_LOG_FORMAT_EASYMINI2:
434                                 case AO_LOG_FORMAT_TELEMINI2:
435                                 case AO_LOG_FORMAT_TELEMINI3:
436                                         log_mini = (struct ao_log_mini *) &eeprom->data[pos];
437                                         switch (log_mini->type) {
438                                         case AO_LOG_FLIGHT:
439                                                 printf(" serial %5u flight %5u ground_pres %9u",
440                                                        eeprom->serial_number,
441                                                        log_mini->u.flight.flight,
442                                                        log_mini->u.flight.ground_pres);
443                                                 break;
444                                         case AO_LOG_STATE:
445                                                 ao_state(log_mini->u.state.state,
446                                                          log_mini->u.state.reason);
447                                                 break;
448                                         case AO_LOG_SENSOR:
449                                                 ao_ms5607(int24(log_mini->u.sensor.pres, 0),
450                                                           int24(log_mini->u.sensor.temp, 0),
451                                                           eeprom, is_ms5611);
452                                                 ao_volts("sense_a",
453                                                          log_mini->u.sensor.sense_a, max_adc,
454                                                          adc_ref, sense_r1, sense_r2);
455                                                 ao_volts("sense_m",
456                                                          log_mini->u.sensor.sense_m, max_adc,
457                                                          adc_ref, sense_r1, sense_r2);
458                                                 ao_volts("v_batt",
459                                                          log_mini->u.sensor.v_batt, max_adc,
460                                                          adc_ref, batt_r1, batt_r2);
461                                                 break;
462                                         } /*  */
463                                         break;
464                                 case AO_LOG_FORMAT_TELEMETRUM:
465                                         log_metrum = (struct ao_log_metrum *) &eeprom->data[pos];
466                                         switch (log_metrum->type) {
467                                         case AO_LOG_FLIGHT:
468                                                 printf(" serial %5u flight %5u ground_accel %6d ground_pres %9u ground_temp %9u",
469                                                        eeprom->serial_number,
470                                                        log_metrum->u.flight.flight,
471                                                        log_metrum->u.flight.ground_accel,
472                                                        log_metrum->u.flight.ground_pres,
473                                                        log_metrum->u.flight.ground_temp);
474                                                 break;
475                                         case AO_LOG_SENSOR:
476                                                 ao_ms5607(log_metrum->u.sensor.pres,
477                                                           log_metrum->u.sensor.temp,
478                                                           eeprom, is_ms5611);
479                                                 ao_accel(log_metrum->u.sensor.accel, eeprom);
480                                                 break;
481                                         case AO_LOG_TEMP_VOLT:
482                                                 ao_volts("v_batt",
483                                                          log_metrum->u.volt.v_batt, max_adc,
484                                                          adc_ref, batt_r1, batt_r2);
485                                                 ao_volts("sense_a",
486                                                          log_metrum->u.volt.sense_a, max_adc,
487                                                          adc_ref, sense_r1, sense_r2);
488                                                 ao_volts("sense_m",
489                                                          log_metrum->u.volt.sense_m, max_adc,
490                                                          adc_ref, sense_r1, sense_r2);
491                                                 break;
492                                         case AO_LOG_DEPLOY:
493                                                 break;
494                                         case AO_LOG_STATE:
495                                                 ao_state(log_metrum->u.state.state,
496                                                          log_metrum->u.state.reason);
497                                                 break;
498                                         case AO_LOG_GPS_TIME:
499                                                 printf(" time %02d:%02d:%02d 20%02d-%02d-%02d flags %02x pdop %3u",
500                                                        log_metrum->u.gps_time.hour,
501                                                        log_metrum->u.gps_time.minute,
502                                                        log_metrum->u.gps_time.second,
503                                                        log_metrum->u.gps_time.year,
504                                                        log_metrum->u.gps_time.month,
505                                                        log_metrum->u.gps_time.day,
506                                                        log_metrum->u.gps_time.flags,
507                                                        log_metrum->u.gps_time.pdop);
508                                                 break;
509                                         case AO_LOG_GPS_SAT:
510                                                 printf(" channels %2d more %1d",
511                                                        log_metrum->u.gps_sat.channels,
512                                                        log_metrum->u.gps_sat.more);
513                                                 for (i = 0; i < 4; i++) {
514                                                         printf(" svid %3d c_n %2d",
515                                                                log_metrum->u.gps_sat.sats[i].svid,
516                                                                log_metrum->u.gps_sat.sats[i].c_n);
517                                                 }
518                                                 break;
519                                         case AO_LOG_GPS_POS:
520                                                 printf(" lat %10.7f° lon %10.7f° alt %8d m",
521                                                        log_metrum->u.gps.latitude / 10000000.0,
522                                                        log_metrum->u.gps.longitude/ 10000000.0,
523                                                        (int32_t) (log_metrum->u.gps.altitude_low |
524                                                                   (log_metrum->u.gps.altitude_high << 16)));
525                                                 break;
526                                         default:
527                                                 printf(" unknown");
528                                         }
529                                         break;
530                                 case AO_LOG_FORMAT_TELEGPS:
531                                         log_gps = (struct ao_log_gps *) &eeprom->data[pos];
532                                         (void) log_gps;
533                                         break;
534                                 case AO_LOG_FORMAT_DETHERM:
535                                         break;
536                                 }
537                         }
538                         printf("\n");
539                 }
540         }
541         return ret;
542 }