From: Bdale Garbee Date: Sun, 6 Sep 2009 16:39:23 +0000 (-0600) Subject: Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos X-Git-Tag: debian/0.5+77+gc57bd7f~24 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=35c54b3a278fa9bc2bc7f4b5ee04866697c93ba0;hp=4f8eff7401ee2d8092ab36fa33411f9b23dda880 Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos --- diff --git a/.gitignore b/.gitignore index b3d2d562..0ca4bed4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,9 +22,13 @@ ao-teleterra.h ao-tidongle.h ao-tools/ao-bitbang/ao-bitbang ao-tools/ao-dbg/ao-dbg +ao-tools/ao-dumplog/ao-dumplog ao-tools/ao-eeprom/ao-eeprom +ao-tools/ao-list/ao-list ao-tools/ao-load/ao-load +ao-tools/ao-postflight/ao-postflight ao-tools/ao-rawload/ao-rawload +ao-tools/ao-view/ao-view ao-view/Makefile ao-view/ao-view autom4te.cache diff --git a/ao-tools/Makefile.am b/ao-tools/Makefile.am index 28e77b08..2850e909 100644 --- a/ao-tools/Makefile.am +++ b/ao-tools/Makefile.am @@ -1 +1 @@ -SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list ao-load ao-view +SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view diff --git a/ao-tools/ao-dbg/ao-dbg-main.c b/ao-tools/ao-dbg/ao-dbg-main.c index f1e2c111..21b83a3d 100644 --- a/ao-tools/ao-dbg/ao-dbg-main.c +++ b/ao-tools/ao-dbg/ao-dbg-main.c @@ -34,6 +34,7 @@ struct ccdbg *s51_dbg; int s51_interrupted = 0; int s51_monitor = 0; char *s51_tty = NULL; +char *s51_device = NULL; static FILE *s51_input; static FILE *s51_output; @@ -52,6 +53,7 @@ void s51_sigint() static const struct option options[] = { { .name = "tty", .has_arg = 1, .val = 'T' }, + { .name = "device", .has_arg = 1, .val = 'D' }, { 0, 0, 0, 0 }, }; @@ -114,6 +116,9 @@ main(int argc, char **argv) case 'T': s51_tty = optarg; break; + case 'D': + s51_device = optarg; + break; } } if (s51_port) { diff --git a/ao-tools/ao-dbg/ao-dbg-parse.c b/ao-tools/ao-dbg/ao-dbg-parse.c index 825d0e9c..dcb9099d 100644 --- a/ao-tools/ao-dbg/ao-dbg-parse.c +++ b/ao-tools/ao-dbg/ao-dbg-parse.c @@ -195,6 +195,11 @@ command_read (void) enum command_result result; struct command_function *func; + if (!s51_tty) { + if (!s51_device) + s51_device = getenv("AO_DBG_DEVICE"); + s51_tty = cc_usbdevs_find_by_arg(s51_device, "TIDongle"); + } s51_dbg = ccdbg_open (s51_tty); if (!s51_dbg) exit(1); diff --git a/ao-tools/ao-dbg/ao-dbg.1 b/ao-tools/ao-dbg/ao-dbg.1 index a850c454..00d3ac86 100644 --- a/ao-tools/ao-dbg/ao-dbg.1 +++ b/ao-tools/ao-dbg/ao-dbg.1 @@ -35,6 +35,9 @@ ao-dbg \- hex debugger for cc1111 processors [\-h] [\-m] [\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] .SH DESCRIPTION .I ao-dbg connects to a cc1111 processor through either a suitable cc1111 board @@ -80,11 +83,26 @@ This should print a usage message, but does nothing useful currently. .IP "\-m" This option is not present in the original 8051 emulator, and causes ao-dbg to dump all commands and replies that are received from and sent to sdcdb. -.IP "\-T" +.TP +\-T tty-device | --tty tty-device This selects which tty device the debugger uses to communicate with the target device. The special name 'BITBANG' directs ao-dbg to use the cp2103 connection, otherwise this should be a usb serial port connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMetrum:2 +.br +TeleMetrum +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. .SH COMMANDS Once started, ao-dbg connects to the cc1111 and then reads and executes commands, either from stdin, or the nework connection to diff --git a/ao-tools/ao-dbg/ao-dbg.h b/ao-tools/ao-dbg/ao-dbg.h index c1789d10..edc650a5 100644 --- a/ao-tools/ao-dbg/ao-dbg.h +++ b/ao-tools/ao-dbg/ao-dbg.h @@ -17,12 +17,14 @@ */ #include +#include extern char *s51_prompt; extern struct ccdbg *s51_dbg; extern int s51_interrupted; extern int s51_monitor; extern char *s51_tty; +extern char *s51_device; enum command_result { command_success, command_debug, command_syntax, command_interrupt, command_error, diff --git a/ao-tools/ao-dumplog/Makefile.am b/ao-tools/ao-dumplog/Makefile.am new file mode 100644 index 00000000..a80cac33 --- /dev/null +++ b/ao-tools/ao-dumplog/Makefile.am @@ -0,0 +1,12 @@ +bin_PROGRAMS=ao-dumplog + +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) +AO_DUMPLOG_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a + +ao_dumplog_DEPENDENCIES = $(AO_DUMPLOG_LIBS) + +ao_dumplog_LDADD=$(AO_DUMPLOG_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS) + +ao_dumplog_SOURCES = ao-dumplog.c + +man_MANS = ao-dumplog.1 diff --git a/ao-tools/ao-dumplog/ao-dumplog.1 b/ao-tools/ao-dumplog/ao-dumplog.1 new file mode 100644 index 00000000..8c2df7c6 --- /dev/null +++ b/ao-tools/ao-dumplog/ao-dumplog.1 @@ -0,0 +1,68 @@ +.\" +.\" Copyright © 2009 Keith Packard +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH AO-DUMPLOG 1 "ao-dumplog" "" +.SH NAME +ao-dumplog \- Store flight log from TeleMetrum device +.SH SYNOPSIS +.B "ao-dumplog" +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device ao-dumplog uses to communicate with +the target device. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMetrum:2 +.br +TeleMetrum +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. +.SH DESCRIPTION +.I ao-dumplog +downloads the flight log from a connected TeleMetrum device and stores +it to the configured flight log directory using a name of the form +.IP +\fIyyyy\fP-\fImm\fP-\fIdd\fP-serialP-\fIsss\fP-flight-\fIfff\fP.eeprom +.PP +\fIyyyy\fP is the current year +.br +\fImm\fP is the current month +.br +\fIdd\fP is the current day +.br +\fIsss\fP is the device serial number +.br +\fIfff\fP is a flight sequence number (to make filenames unique) +.SH USAGE +.I ao-dumplog +connects to the specified target device and dumps the stored flight +log. +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-dumplog/ao-dumplog.c b/ao-tools/ao-dumplog/ao-dumplog.c new file mode 100644 index 00000000..b930f0e5 --- /dev/null +++ b/ao-tools/ao-dumplog/ao-dumplog.c @@ -0,0 +1,104 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include "cc-usb.h" +#include "cc.h" + +#define NUM_BLOCK 512 + +static const struct option options[] = { + { .name = "tty", .has_arg = 1, .val = 'T' }, + { .name = "device", .has_arg = 1, .val = 'D' }, + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s [--tty ] [--device \n", program); + exit(1); +} + +int +main (int argc, char **argv) +{ + struct cc_usb *cc; + char *tty = NULL; + char *device = NULL; + int c; + char line[8192]; + FILE *out; + char *filename; + int serial_number; + char cmd; + int tick, a, b; + + while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) { + switch (c) { + case 'T': + tty = optarg; + break; + case 'D': + device = optarg; + break; + default: + usage(argv[0]); + break; + } + } + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "TeleMetrum"); + if (!tty) + tty = getenv("ALTOS_TTY"); + if (!tty) + tty="/dev/ttyACM0"; + cc = cc_usb_open(tty); + if (!cc) + exit(1); + /* send a 'version' command followed by a 'log' command */ + cc_usb_printf(cc, "v\nl\n"); + out = NULL; + for (;;) { + cc_usb_getline(cc, line, sizeof (line)); + if (!strcmp (line, "end")) + break; + if (sscanf(line, "serial-number %u", &serial_number) == 1) { + filename = cc_make_filename(serial_number, "eeprom"); + out = fopen (filename, "w"); + if (!out) { + perror(filename); + } + fprintf (out, "%s\n", line); + } else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) { + if (out) { + fprintf(out, "%s\n", line); + if (cmd == 'S' && a == 8) { + fclose(out); + out = NULL; + } + } + } + } + if (out) + fclose (out); + cc_usb_close(cc); + exit (0); +} diff --git a/ao-tools/ao-eeprom/ao-eeprom.1 b/ao-tools/ao-eeprom/ao-eeprom.1 index 8caff9d1..ed498147 100644 --- a/ao-tools/ao-eeprom/ao-eeprom.1 +++ b/ao-tools/ao-eeprom/ao-eeprom.1 @@ -21,7 +21,31 @@ ao-eeprom \- Fetch eeprom contents from TeleMetrum device .SH SYNOPSIS .B "ao-eeprom" -[\-tty \fItty-device\fP] +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device the debugger uses to communicate with +the target device. The special name 'BITBANG' directs ao-dbg to use +the cp2103 connection, otherwise this should be a usb serial port +connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMetrum:2 +.br +TeleMetrum +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. .SH DESCRIPTION .I ao-eeprom downloads the eeprom contents from a connected TeleMetrum device. diff --git a/ao-tools/ao-load/ao-load.1 b/ao-tools/ao-load/ao-load.1 index 10484f3b..eb2bc0d8 100644 --- a/ao-tools/ao-load/ao-load.1 +++ b/ao-tools/ao-load/ao-load.1 @@ -21,13 +21,37 @@ ao-load \- flash a program to a AltOS device .SH SYNOPSIS .B "ao-load" -[\-tty \fItty-device\fP] +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] \fIfile.ihx\fP \fIdevice serial number\fP .SH DESCRIPTION .I ao-load loads the specified .ihx file into the target device flash memory, customizing the AltOS image with the specified serial number. +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device the debugger uses to communicate with +the target device. The special name 'BITBANG' directs ao-dbg to use +the cp2103 connection, otherwise this should be a usb serial port +connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMetrum:2 +.br +TeleMetrum +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. .SH USAGE .I ao-load reads the specified .ihx file into memory, locates the matching .map diff --git a/ao-tools/ao-load/ao-load.c b/ao-tools/ao-load/ao-load.c index c27fcbe9..f5466612 100644 --- a/ao-tools/ao-load/ao-load.c +++ b/ao-tools/ao-load/ao-load.c @@ -22,6 +22,7 @@ #include #include #include "ccdbg.h" +#include "cc.h" #define AO_USB_DESC_STRING 3 @@ -91,12 +92,13 @@ rewrite(struct hex_image *image, unsigned addr, char *data, int len) static const struct option options[] = { { .name = "tty", .has_arg = 1, .val = 'T' }, + { .name = "device", .has_arg = 1, .val = 'D' }, { 0, 0, 0, 0}, }; static void usage(char *program) { - fprintf(stderr, "usage: %s [--tty ] file.ihx serial-number\n", program); + fprintf(stderr, "usage: %s [--tty ] [--device ] file.ihx serial-number\n", program); exit(1); } @@ -122,13 +124,17 @@ main (int argc, char **argv) unsigned usb_descriptors; int string_num; char *tty = NULL; + char *device = NULL; int c; - while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) { switch (c) { case 'T': tty = optarg; break; + case 'D': + device = optarg; + break; default: usage(argv[0]); break; @@ -219,6 +225,8 @@ main (int argc, char **argv) if (!rewrite(image, usb_descriptors + 2 + image->address, serial_ucs2, serial_ucs2_len)) usage(argv[0]); + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "TIDongle"); dbg = ccdbg_open(tty); if (!dbg) exit (1); diff --git a/ao-tools/ao-postflight/Makefile.am b/ao-tools/ao-postflight/Makefile.am new file mode 100644 index 00000000..301ac454 --- /dev/null +++ b/ao-tools/ao-postflight/Makefile.am @@ -0,0 +1,12 @@ +bin_PROGRAMS=ao-postflight + +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) +AO_POSTFLIGHT_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a + +ao_postflight_DEPENDENCIES = $(AO_POSTFLIGHT_LIBS) + +ao_postflight_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS) + +ao_postflight_SOURCES = ao-postflight.c + +man_MANS = ao-postflight.1 diff --git a/ao-tools/ao-postflight/ao-postflight.1 b/ao-tools/ao-postflight/ao-postflight.1 new file mode 100644 index 00000000..fe02587f --- /dev/null +++ b/ao-tools/ao-postflight/ao-postflight.1 @@ -0,0 +1,29 @@ +.\" +.\" Copyright © 2009 Keith Packard +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License along +.\" with this program; if not, write to the Free Software Foundation, Inc., +.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +.\" +.\" +.TH AO-POSTFLIGHT 1 "ao-postflight" "" +.SH NAME +ao-postflight \- Analyse a flight log (either telemetry or eeprom) +.SH SYNOPSIS +.B "ao-postflight" +{flight.eeprom|flight.telem} +.SH DESCRIPTION +.I ao-postflight +reads the specified flight log and produces a summary of the flight on stdout. +.SH AUTHOR +Keith Packard diff --git a/ao-tools/ao-postflight/ao-postflight.c b/ao-tools/ao-postflight/ao-postflight.c new file mode 100644 index 00000000..9371f351 --- /dev/null +++ b/ao-tools/ao-postflight/ao-postflight.c @@ -0,0 +1,180 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include "cc-usb.h" +#include "cc.h" + +#define NUM_BLOCK 512 + +static const struct option options[] = { + { 0, 0, 0, 0}, +}; + +static void usage(char *program) +{ + fprintf(stderr, "usage: %s {flight-log} ...\n", program); + exit(1); +} + +static const char *state_names[] = { + "startup", + "idle", + "pad", + "boost", + "fast", + "coast", + "drogue", + "main", + "landed", + "invalid" +}; + +void +analyse_flight(struct cc_flightraw *f) +{ + double height; + double accel; + double boost_start, boost_stop; + double min_pres; + int i; + int pres_i, accel_i; + int boost_start_set = 0; + int boost_stop_set = 0; + enum ao_flight_state state; + double state_start, state_stop; + + printf ("Flight: %9d\nSerial: %9d\n", + f->flight, f->serial); + boost_start = f->accel.data[0].time; + boost_stop = f->accel.data[f->accel.num-1].time; + for (i = 0; i < f->state.num; i++) { + if (f->state.data[i].value == ao_flight_boost && !boost_start_set) { + boost_start = f->state.data[i].time; + boost_start_set = 1; + } + if (f->state.data[i].value > ao_flight_boost && !boost_stop_set) { + boost_stop = f->state.data[i].time; + boost_stop_set = 1; + } + } + + pres_i = cc_timedata_min(&f->pres, f->pres.data[0].time, + f->pres.data[f->pres.num-1].time); + if (pres_i) + { + min_pres = f->pres.data[pres_i].value; + height = cc_barometer_to_altitude(min_pres) - + cc_barometer_to_altitude(f->ground_pres); + printf ("Max height: %9.2fm %9.2fft %9.2fs\n", + height, height * 100 / 2.54 / 12, + (f->pres.data[pres_i].time - boost_start) / 100.0); + } + + accel_i = cc_timedata_min(&f->accel, boost_start, boost_stop); + if (accel_i) + { + accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value, + f->ground_accel); + printf ("Max accel: %9.2fm/s² %9.2fg %9.2fs\n", + accel, accel / 9.80665, + (f->accel.data[accel_i].time - boost_start) / 100.0); + } + for (i = 0; i < f->state.num; i++) { + state = f->state.data[i].value; + state_start = f->state.data[i].time; + while (i < f->state.num - 1 && f->state.data[i+1].value == state) + i++; + if (i < f->state.num - 1) + state_stop = f->state.data[i + 1].time; + else + state_stop = f->accel.data[f->accel.num-1].time; + printf("State: %s\n", state_names[state]); + printf("\tStart: %9.2fs\n", (state_start - boost_start) / 100.0); + printf("\tDuration: %9.2fs\n", (state_stop - state_start) / 100.0); + accel_i = cc_timedata_min(&f->accel, state_start, state_stop); + if (accel_i >= 0) + { + accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value, + f->ground_accel); + printf("\tMax accel: %9.2fm/s² %9.2fg %9.2fs\n", + accel, accel / 9.80665, + (f->accel.data[accel_i].time - boost_start) / 100.0); + } + + pres_i = cc_timedata_min(&f->pres, state_start, state_stop); + if (pres_i >= 0) + { + min_pres = f->pres.data[pres_i].value; + height = cc_barometer_to_altitude(min_pres) - + cc_barometer_to_altitude(f->ground_pres); + printf ("\tMax height: %9.2fm %9.2fft %9.2fs\n", + height, height * 100 / 2.54 / 12, + (f->pres.data[pres_i].time - boost_start) / 100.0); + } + } +} + +int +main (int argc, char **argv) +{ + FILE *file; + int i; + int ret = 0; + struct cc_flightraw *raw; + int c; + int serial; + char *s; + + while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) { + switch (c) { + default: + usage(argv[0]); + break; + } + } + for (i = optind; i < argc; i++) { + file = fopen(argv[i], "r"); + if (!file) { + perror(argv[i]); + ret++; + continue; + } + s = strstr(argv[i], "-serial-"); + if (s) + serial = atoi(s + 8); + else + serial = 0; + raw = cc_log_read(file); + if (!raw) { + perror(argv[i]); + ret++; + continue; + } + if (!raw->serial) + raw->serial = serial; + analyse_flight(raw); + cc_flightraw_free(raw); + } + return ret; +} diff --git a/ao-tools/ao-rawload/ao-rawload.1 b/ao-tools/ao-rawload/ao-rawload.1 index e79645f1..6b6a6e2c 100644 --- a/ao-tools/ao-rawload/ao-rawload.1 +++ b/ao-tools/ao-rawload/ao-rawload.1 @@ -21,12 +21,36 @@ ao-rawload \- flash a program to a AltOS device .SH SYNOPSIS .B "ao-rawload" -[\-tty \fItty-device\fP] +[\-T \fItty-device\fP] +[\--tty \fItty-device\fP] +[\-D \fIaltos-device\fP] +[\--device \fIaltos-device\fP] \fIfile.ihx\fP .SH DESCRIPTION .I ao-rawload loads the specified .ihx file, without modification, into the target device flash memory. +.SH OPTIONS +.TP +\-T tty-device | --tty tty-device +This selects which tty device the debugger uses to communicate with +the target device. The special name 'BITBANG' directs ao-dbg to use +the cp2103 connection, otherwise this should be a usb serial port +connected to a suitable cc1111 debug node. +.TP +\-D AltOS-device | --device AltOS-device +Search for a connected device. This requires an argument of one of the +following forms: +.IP +TeleMetrum:2 +.br +TeleMetrum +.br +2 +.IP +Leaving out the product name will cause the tool to select a suitable +product, leaving out the serial number will cause the tool to match +one of the available devices. .SH USAGE .I ao-rawload reads the specified .ihx file into memory. It then connects to the diff --git a/ao-tools/ao-rawload/ao-rawload.c b/ao-tools/ao-rawload/ao-rawload.c index 1f1537b9..255f63ec 100644 --- a/ao-tools/ao-rawload/ao-rawload.c +++ b/ao-tools/ao-rawload/ao-rawload.c @@ -22,12 +22,13 @@ static const struct option options[] = { { .name = "tty", .has_arg = 1, .val = 'T' }, + { .name = "device", .has_arg = 1, .val = 'D' }, { 0, 0, 0, 0}, }; static void usage(char *program) { - fprintf(stderr, "usage: %s [--tty ] file.ihx\n", program); + fprintf(stderr, "usage: %s [--tty ] [--device ] file.ihx\n", program); exit(1); } @@ -42,13 +43,17 @@ main (int argc, char **argv) char *filename; FILE *file; char *tty = NULL; + char *device = NULL; int c; - while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) { switch (c) { case 'T': tty = optarg; break; + case 'D': + device = optarg; + break; default: usage(argv[0]); break; @@ -75,6 +80,8 @@ main (int argc, char **argv) } ccdbg_hex_file_free(hex); + if (!tty) + tty = cc_usbdevs_find_by_arg(device, "TIDongle"); dbg = ccdbg_open(tty); if (!dbg) exit (1); diff --git a/ao-tools/lib/Makefile.am b/ao-tools/lib/Makefile.am index f66ee0a9..e682f757 100644 --- a/ao-tools/lib/Makefile.am +++ b/ao-tools/lib/Makefile.am @@ -1,6 +1,6 @@ noinst_LIBRARIES = libao-tools.a -AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS) +AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) libao_tools_a_SOURCES = \ ccdbg-command.c \ @@ -14,6 +14,9 @@ libao_tools_a_SOURCES = \ ccdbg-memory.c \ ccdbg-rom.c \ ccdbg-state.c \ + cc-analyse.c \ + cc-convert.c \ + cc-log.c \ cc-usb.c \ cc-usb.h \ cc.h \ @@ -21,5 +24,7 @@ libao_tools_a_SOURCES = \ cc-util.c \ cc-bitbang.c \ cc-bitbang.h \ + cc-logfile.c \ + cc-telem.c \ cp-usb-async.c \ cp-usb-async.h diff --git a/ao-tools/lib/cc-analyse.c b/ao-tools/lib/cc-analyse.c new file mode 100644 index 00000000..fc8a8417 --- /dev/null +++ b/ao-tools/lib/cc-analyse.c @@ -0,0 +1,58 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "cc.h" + +int +cc_timedata_min(struct cc_timedata *d, double min_time, double max_time) +{ + int i; + int set = 0; + int min_i = -1; + double min; + + if (d->num == 0) + return -1; + for (i = 0; i < d->num; i++) + if (min_time <= d->data[i].time && d->data[i].time <= max_time) + if (!set || d->data[i].value < min) { + min_i = i; + min = d->data[i].value; + set = 1; + } + return min_i; +} + +int +cc_timedata_max(struct cc_timedata *d, double min_time, double max_time) +{ + int i; + double max; + int max_i = -1; + int set = 0; + + if (d->num == 0) + return -1; + for (i = 0; i < d->num; i++) + if (min_time <= d->data[i].time && d->data[i].time <= max_time) + if (!set || d->data[i].value > max) { + max_i = i; + max = d->data[i].value; + set = 1; + } + return max_i; +} diff --git a/ao-tools/lib/cc-convert.c b/ao-tools/lib/cc-convert.c new file mode 100644 index 00000000..ac6962ba --- /dev/null +++ b/ao-tools/lib/cc-convert.c @@ -0,0 +1,275 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "cc.h" +#include + +/* + * Pressure Sensor Model, version 1.1 + * + * written by Holly Grimes + * + * Uses the International Standard Atmosphere as described in + * "A Quick Derivation relating altitude to air pressure" (version 1.03) + * from the Portland State Aerospace Society, except that the atmosphere + * is divided into layers with each layer having a different lapse rate. + * + * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 + * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ + return 0; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted altitude */ + for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + base_pressure *= pow(base, exponent); + } + base_temperature += delta_z * lapse_rate[layer_number]; + } + + /* calculate the pressure at the inputted altitude */ + delta_z = altitude - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + pressure = base_pressure * exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + pressure = base_pressure * pow(base, exponent); + } + + return pressure; +} + + +/* outputs the altitude associated with the given pressure. the altitude + returned is measured with respect to the mean sea level */ +double +cc_pressure_to_altitude(double pressure) +{ + + double next_base_temperature = LAYER0_BASE_TEMPERATURE; + double next_base_pressure = LAYER0_BASE_PRESSURE; + + double altitude; + double base_pressure; + double base_temperature; + double base; /* base for function to determine base pressure of next layer */ + double exponent; /* exponent for function to determine base pressure + of next layer */ + double coefficient; + int layer_number; /* identifies layer in the atmosphere */ + int delta_z; /* difference between two altitudes */ + + if (pressure < 0) /* illegal pressure */ + return -1; + if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ + return MAXIMUM_ALTITUDE; + + /* calculate the base temperature and pressure for the atmospheric layer + associated with the inputted pressure. */ + layer_number = -1; + do { + layer_number++; + base_pressure = next_base_pressure; + base_temperature = next_base_temperature; + delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; + if (lapse_rate[layer_number] == 0.0) { + exponent = GRAVITATIONAL_ACCELERATION * delta_z + / AIR_GAS_CONSTANT / base_temperature; + next_base_pressure *= exp(exponent); + } + else { + base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; + exponent = GRAVITATIONAL_ACCELERATION / + (AIR_GAS_CONSTANT * lapse_rate[layer_number]); + next_base_pressure *= pow(base, exponent); + } + next_base_temperature += delta_z * lapse_rate[layer_number]; + } + while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); + + /* calculate the altitude associated with the inputted pressure */ + if (lapse_rate[layer_number] == 0.0) { + coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) + * base_temperature; + altitude = base_altitude[layer_number] + + coefficient * log(pressure / base_pressure); + } + else { + base = pressure / base_pressure; + exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] + / GRAVITATIONAL_ACCELERATION; + coefficient = base_temperature / lapse_rate[layer_number]; + altitude = base_altitude[layer_number] + + coefficient * (pow(base, exponent) - 1); + } + + return altitude; +} + +/* + * Values for our MP3H6115A pressure sensor + * + * From the data sheet: + * + * Pressure range: 15-115 kPa + * Voltage at 115kPa: 2.82 + * Output scale: 27mV/kPa + * + * + * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa + * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa + */ + +static const double counts_per_kPa = 27 * 2047 / 3300; +static const double counts_at_101_3kPa = 1674.0; + +double +cc_barometer_to_pressure(double count) +{ + return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0; +} + +double +cc_barometer_to_altitude(double baro) +{ + double Pa = cc_barometer_to_pressure(baro); + return cc_pressure_to_altitude(Pa); +} + +static const double count_per_mss = 27.0; + +double +cc_accelerometer_to_acceleration(double accel, double ground_accel) +{ + return (ground_accel - accel) / count_per_mss; +} + +double +cc_thermometer_to_temperature(double thermo) +{ + return ((thermo / 32767 * 3.3) - 0.5) / 0.01; +} + +double +cc_battery_to_voltage(double battery) +{ + return battery / 32767.0 * 5.0; +} + +double +cc_ignitor_to_voltage(double ignite) +{ + return ignite / 32767 * 15.0; +} + +static inline double sqr(double a) { return a * a; } + +void +cc_great_circle (double start_lat, double start_lon, + double end_lat, double end_lon, + double *dist, double *bearing) +{ + const double rad = M_PI / 180; + const double earth_radius = 6371.2 * 1000; /* in meters */ + double lat1 = rad * start_lat; + double lon1 = rad * -start_lon; + double lat2 = rad * end_lat; + double lon2 = rad * -end_lon; + +// double d_lat = lat2 - lat1; + double d_lon = lon2 - lon1; + + /* From http://en.wikipedia.org/wiki/Great-circle_distance */ + double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) + + sqr(cos(lat1) * sin(lat2) - + sin(lat1) * cos(lat2) * cos(d_lon))); + double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon); + double d = atan2(vdn,vdd); + double course; + + if (cos(lat1) < 1e-20) { + if (lat1 > 0) + course = M_PI; + else + course = -M_PI; + } else { + if (d < 1e-10) + course = 0; + else + course = acos((sin(lat2)-sin(lat1)*cos(d)) / + (sin(d)*cos(lat1))); + if (sin(lon2-lon1) > 0) + course = 2 * M_PI-course; + } + *dist = d * earth_radius; + *bearing = course * 180/M_PI; +} diff --git a/ao-tools/lib/cc-log.c b/ao-tools/lib/cc-log.c new file mode 100644 index 00000000..dd8177f4 --- /dev/null +++ b/ao-tools/lib/cc-log.c @@ -0,0 +1,114 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include "cc.h" + +static char *cc_file_dir; + +#define ALTOS_DIR_PATH "/apps/aoview/log_dir" +#define DEFAULT_DIR "AltOS" + +static void +cc_file_save_conf(void) +{ + GConfClient *gconf_client; + + g_type_init(); + gconf_client = gconf_client_get_default(); + if (gconf_client) + { + gconf_client_set_string(gconf_client, + ALTOS_DIR_PATH, + cc_file_dir, + NULL); + g_object_unref(G_OBJECT(gconf_client)); + } +} + +static void +cc_file_load_conf(void) +{ + char *file_dir; + GConfClient *gconf_client; + + g_type_init(); + gconf_client = gconf_client_get_default(); + if (gconf_client) + { + file_dir = gconf_client_get_string(gconf_client, + ALTOS_DIR_PATH, + NULL); + g_object_unref(G_OBJECT(gconf_client)); + if (file_dir) + cc_file_dir = strdup(file_dir); + } +} + +void +cc_set_log_dir(char *dir) +{ + cc_file_dir = strdup(dir); + cc_file_save_conf(); +} + +char * +cc_get_log_dir(void) +{ + cc_file_load_conf(); + if (!cc_file_dir) { + cc_file_dir = cc_fullname(getenv("HOME"), DEFAULT_DIR); + cc_file_save_conf(); + } + return cc_file_dir; +} + +char * +cc_make_filename(int serial, char *ext) +{ + char base[50]; + struct tm tm; + time_t now; + char *full; + int r; + int sequence; + + now = time(NULL); + (void) localtime_r(&now, &tm); + cc_mkdir(cc_get_log_dir()); + sequence = 0; + for (;;) { + snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d.%s", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + serial, + sequence, + ext); + full = cc_fullname(cc_get_log_dir(), base); + r = access(full, F_OK); + if (r < 0) + return full; + free(full); + sequence++; + } + +} diff --git a/ao-tools/lib/cc-logfile.c b/ao-tools/lib/cc-logfile.c new file mode 100644 index 00000000..4abf7eb6 --- /dev/null +++ b/ao-tools/lib/cc-logfile.c @@ -0,0 +1,251 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "cc.h" +#include +#include +#include + +static int +timedata_add(struct cc_timedata *data, double time, double value) +{ + struct cc_timedataelt *newdata; + int newsize; + if (data->size == data->num) { + if (data->size == 0) + newdata = malloc((newsize = 256) * sizeof (struct cc_timedataelt)); + else + newdata = realloc (data->data, (newsize = data->size * 2) + * sizeof (struct cc_timedataelt)); + if (!newdata) + return 0; + data->size = newsize; + data->data = newdata; + } + time += data->time_offset; + if (data->num && data->data[data->num-1].time > time) { + data->time_offset += 65536; + time += 65536; + } + data->data[data->num].time = time; + data->data[data->num].value = value; + data->num++; + return 1; +} + +static void +timedata_free(struct cc_timedata *data) +{ + if (data->data) + free(data->data); +} + +static int +gpsdata_add(struct cc_gpsdata *data, struct cc_gpselt *elt) +{ + struct cc_gpselt *newdata; + int newsize; + if (data->size == data->num) { + if (data->size == 0) + newdata = malloc((newsize = 256) * sizeof (struct cc_gpselt)); + else + newdata = realloc (data->data, (newsize = data->size * 2) + * sizeof (struct cc_gpselt)); + if (!newdata) + return 0; + data->size = newsize; + data->data = newdata; + } + elt->time += data->time_offset; + if (data->num && data->data[data->num-1].time > elt->time) { + data->time_offset += 65536; + elt->time += 65536; + } + data->data[data->num] = *elt; + data->num++; + return 1; +} + +static void +gpsdata_free(struct cc_gpsdata *data) +{ + if (data->data) + free(data->data); +} + +#define AO_LOG_FLIGHT 'F' +#define AO_LOG_SENSOR 'A' +#define AO_LOG_TEMP_VOLT 'T' +#define AO_LOG_DEPLOY 'D' +#define AO_LOG_STATE 'S' +#define AO_LOG_GPS_TIME 'G' +#define AO_LOG_GPS_LAT 'N' +#define AO_LOG_GPS_LON 'W' +#define AO_LOG_GPS_ALT 'H' +#define AO_LOG_GPS_SAT 'V' + +#define AO_LOG_POS_NONE (~0UL) + +static int +read_eeprom(const char *line, struct cc_flightraw *f, double *ground_pres, int *ground_pres_count) +{ + char type; + int tick; + int a, b; + struct cc_gpselt gps; + int serial; + + if (sscanf(line, "serial-number %u", &serial) == 1) { + f->serial = serial; + return 1; + } + if (sscanf(line, "%c %x %x %x", &type, &tick, &a, &b) != 4) + return 0; + switch (type) { + case AO_LOG_FLIGHT: + f->ground_accel = a; + f->ground_pres = 0; + f->flight = b; + *ground_pres = 0; + *ground_pres_count = 0; + break; + case AO_LOG_SENSOR: + timedata_add(&f->accel, tick, a); + timedata_add(&f->pres, tick, b); + if (*ground_pres_count < 20) { + *ground_pres += b; + (*ground_pres_count)++; + if (*ground_pres_count >= 20) + f->ground_pres = *ground_pres / *ground_pres_count; + } + break; + case AO_LOG_TEMP_VOLT: + timedata_add(&f->temp, tick, a); + timedata_add(&f->volt, tick, b); + break; + case AO_LOG_DEPLOY: + timedata_add(&f->drogue, tick, a); + timedata_add(&f->main, tick, b); + break; + case AO_LOG_STATE: + timedata_add(&f->state, tick, a); + break; + case AO_LOG_GPS_TIME: + gps.time = tick; + break; + case AO_LOG_GPS_LAT: + gps.lat = ((int32_t) (a + (b << 16))) / 10000000.0; + break; + case AO_LOG_GPS_LON: + gps.lon = ((int32_t) (a + (b << 16))) / 10000000.0; + break; + case AO_LOG_GPS_ALT: + gps.alt = ((int32_t) (a + (b << 16))); + gpsdata_add(&f->gps, &gps); + break; + case AO_LOG_GPS_SAT: + break; + default: + return 0; + } + return 1; +} + +static const char *state_names[] = { + "startup", + "idle", + "pad", + "boost", + "fast", + "coast", + "drogue", + "main", + "landed", + "invalid" +}; + +static enum ao_flight_state +state_name_to_state(char *state_name) +{ + enum ao_flight_state state; + for (state = ao_flight_startup; state < ao_flight_invalid; state++) + if (!strcmp(state_names[state], state_name)) + return state; + return ao_flight_invalid; +} + +static int +read_telem(const char *line, struct cc_flightraw *f) +{ + struct cc_telem telem; + struct cc_gpselt gps; + if (!cc_telem_parse(line, &telem)) + return 0; + f->ground_accel = telem.ground_accel; + f->ground_pres = telem.ground_pres; + f->flight = 0; + timedata_add(&f->accel, telem.tick, telem.flight_accel); + timedata_add(&f->pres, telem.tick, telem.flight_pres); + timedata_add(&f->temp, telem.tick, telem.temp); + timedata_add(&f->volt, telem.tick, telem.batt); + timedata_add(&f->drogue, telem.tick, telem.drogue); + timedata_add(&f->main, telem.tick, telem.main); + timedata_add(&f->state, telem.tick, state_name_to_state(telem.state)); + if (telem.gps.gps_locked) { + gps.time = telem.tick; + gps.lat = telem.gps.lat; + gps.lon = telem.gps.lon; + gps.alt = telem.gps.alt; + gpsdata_add(&f->gps, &gps); + } + return 1; +} + +struct cc_flightraw * +cc_log_read(FILE *file) +{ + struct cc_flightraw *f; + char line[8192]; + double ground_pres; + int ground_pres_count; + + f = calloc(1, sizeof (struct cc_flightraw)); + if (!f) + return NULL; + while (fgets(line, sizeof (line), file)) { + if (read_eeprom(line, f, &ground_pres, &ground_pres_count)) + continue; + if (read_telem(line, f)) + continue; + fprintf (stderr, "invalid line: %s", line); + } + return f; +} + +void +cc_flightraw_free(struct cc_flightraw *raw) +{ + timedata_free(&raw->accel); + timedata_free(&raw->pres); + timedata_free(&raw->temp); + timedata_free(&raw->volt); + timedata_free(&raw->main); + timedata_free(&raw->drogue); + timedata_free(&raw->state); + gpsdata_free(&raw->gps); + free(raw); +} diff --git a/ao-tools/lib/cc-telem.c b/ao-tools/lib/cc-telem.c new file mode 100644 index 00000000..a6ac0313 --- /dev/null +++ b/ao-tools/lib/cc-telem.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "cc.h" +#include +#include + +static void +cc_parse_string(char *target, int len, char *source) +{ + strncpy(target, source, len-1); + target[len-1] = '\0'; +} + +static void +cc_parse_int(int *target, char *source) +{ + *target = strtol(source, NULL, 0); +} + +static void +cc_parse_hex(int *target, char *source) +{ + *target = strtol(source, NULL, 16); +} + +static void +cc_parse_pos(double *target, char *source) +{ + int deg; + double min; + char dir; + double r; + + if (sscanf(source, "%d°%lf'%c", °, &min, &dir) != 3) { + *target = 0; + return; + } + r = deg + min / 60.0; + if (dir == 'S' || dir == 'W') + r = -r; + *target = r; +} + +#define PARSE_MAX_WORDS 512 + +int +cc_telem_parse(const char *input_line, struct cc_telem *telem) +{ + char *saveptr; + char *words[PARSE_MAX_WORDS]; + int nword; + char line_buf[8192], *line; + int tracking_pos; + + /* avoid smashing our input parameter */ + strncpy (line_buf, input_line, sizeof (line_buf)-1); + line_buf[sizeof(line_buf) - 1] = '\0'; + line = line_buf; + for (nword = 0; nword < PARSE_MAX_WORDS; nword++) { + words[nword] = strtok_r(line, " \t\n", &saveptr); + line = NULL; + if (words[nword] == NULL) + break; + } + if (nword < 36) + return FALSE; + if (strcmp(words[0], "CALL") != 0) + return FALSE; + cc_parse_string(telem->callsign, sizeof (telem->callsign), words[1]); + cc_parse_int(&telem->serial, words[3]); + + cc_parse_int(&telem->rssi, words[5]); + cc_parse_string(telem->state, sizeof (telem->state), words[9]); + cc_parse_int(&telem->tick, words[10]); + cc_parse_int(&telem->accel, words[12]); + cc_parse_int(&telem->pres, words[14]); + cc_parse_int(&telem->temp, words[16]); + cc_parse_int(&telem->batt, words[18]); + cc_parse_int(&telem->drogue, words[20]); + cc_parse_int(&telem->main, words[22]); + cc_parse_int(&telem->flight_accel, words[24]); + cc_parse_int(&telem->ground_accel, words[26]); + cc_parse_int(&telem->flight_vel, words[28]); + cc_parse_int(&telem->flight_pres, words[30]); + cc_parse_int(&telem->ground_pres, words[32]); + cc_parse_int(&telem->gps.nsat, words[34]); + if (strcmp (words[36], "unlocked") == 0) { + telem->gps.gps_connected = 1; + telem->gps.gps_locked = 0; + telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0; + telem->gps.lat = telem->gps.lon = 0; + telem->gps.alt = 0; + tracking_pos = 37; + } else if (nword >= 40) { + telem->gps.gps_locked = 1; + telem->gps.gps_connected = 1; + sscanf(words[36], "%d:%d:%d", &telem->gps.gps_time.hour, &telem->gps.gps_time.minute, &telem->gps.gps_time.second); + cc_parse_pos(&telem->gps.lat, words[37]); + cc_parse_pos(&telem->gps.lon, words[38]); + sscanf(words[39], "%dm", &telem->gps.alt); + tracking_pos = 46; + } else { + telem->gps.gps_connected = 0; + telem->gps.gps_locked = 0; + telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0; + telem->gps.lat = telem->gps.lon = 0; + telem->gps.alt = 0; + tracking_pos = -1; + } + if (nword >= 46) { + telem->gps.gps_extended = 1; + sscanf(words[40], "%lfm/s", &telem->gps.ground_speed); + sscanf(words[41], "%d", &telem->gps.course); + sscanf(words[42], "%lfm/s", &telem->gps.climb_rate); + sscanf(words[43], "%lf", &telem->gps.hdop); + sscanf(words[44], "%d", &telem->gps.h_error); + sscanf(words[45], "%d", &telem->gps.v_error); + } else { + telem->gps.gps_extended = 0; + telem->gps.ground_speed = 0; + telem->gps.course = 0; + telem->gps.climb_rate = 0; + telem->gps.hdop = 0; + telem->gps.h_error = 0; + telem->gps.v_error = 0; + } + if (tracking_pos >= 0 && nword >= tracking_pos + 2 && strcmp(words[tracking_pos], "SAT") == 0) { + int c, n, pos; + cc_parse_int(&n, words[tracking_pos + 1]); + pos = tracking_pos + 2; + if (nword >= pos + n * 3) { + telem->gps_tracking.channels = n; + for (c = 0; c < n; c++) { + cc_parse_int(&telem->gps_tracking.sats[c].svid, + words[pos + 0]); + cc_parse_hex(&telem->gps_tracking.sats[c].state, + words[pos + 1]); + cc_parse_int(&telem->gps_tracking.sats[c].c_n0, + words[pos + 2]); + pos += 3; + } + } else { + telem->gps_tracking.channels = 0; + } + } else { + telem->gps_tracking.channels = 0; + } + return TRUE; +} diff --git a/ao-tools/lib/cc-usb.c b/ao-tools/lib/cc-usb.c index 17f05911..80d9c04f 100644 --- a/ao-tools/lib/cc-usb.c +++ b/ao-tools/lib/cc-usb.c @@ -30,27 +30,29 @@ #include "cc-usb.h" -#define CC_NUM_READ 16 +#define CC_NUM_HEX_READ 64 /* * AltOS has different buffer sizes for in/out packets */ -#define CC_IN_BUF 256 +#define CC_IN_BUF 65536 #define CC_OUT_BUF 64 #define DEFAULT_TTY "/dev/ttyACM0" -struct cc_read { +struct cc_hex_read { uint8_t *buf; int len; }; struct cc_usb { - int fd; - uint8_t in_buf[CC_IN_BUF]; - int in_count; - uint8_t out_buf[CC_OUT_BUF]; - int out_count; - struct cc_read read_buf[CC_NUM_READ]; - int read_count; + int fd; + uint8_t in_buf[CC_IN_BUF]; + int in_pos; + int in_count; + uint8_t out_buf[CC_OUT_BUF]; + int out_count; + + struct cc_hex_read hex_buf[CC_NUM_HEX_READ]; + int hex_count; }; #define NOT_HEX 0xff @@ -72,61 +74,48 @@ cc_hex_nibble(uint8_t c) * and write them to the waiting buffer */ static void -cc_handle_in(struct cc_usb *cc) +cc_handle_hex_read(struct cc_usb *cc) { uint8_t h, l; - int in_pos; - int read_pos; + int hex_pos; - in_pos = 0; - read_pos = 0; - while (read_pos < cc->read_count && in_pos < cc->in_count) { + hex_pos = 0; + while (hex_pos < cc->hex_count && cc->in_pos < cc->in_count) { /* * Skip to next hex character */ - while (in_pos < cc->in_count && - cc_hex_nibble(cc->in_buf[in_pos]) == NOT_HEX) - in_pos++; + while (cc->in_pos < cc->in_count && + cc_hex_nibble(cc->in_buf[cc->in_pos]) == NOT_HEX) + cc->in_pos++; /* * Make sure we have two characters left */ - if (cc->in_count - in_pos < 2) + if (cc->in_count - cc->in_pos < 2) break; /* * Parse hex number */ - h = cc_hex_nibble(cc->in_buf[in_pos]); - l = cc_hex_nibble(cc->in_buf[in_pos+1]); + h = cc_hex_nibble(cc->in_buf[cc->in_pos]); + l = cc_hex_nibble(cc->in_buf[cc->in_pos+1]); if (h == NOT_HEX || l == NOT_HEX) { fprintf(stderr, "hex read error\n"); break; } - in_pos += 2; + cc->in_pos += 2; /* * Store hex number */ - *cc->read_buf[read_pos].buf++ = (h << 4) | l; - if (--cc->read_buf[read_pos].len <= 0) - read_pos++; - } - - /* Move remaining bytes to the start of the input buffer */ - if (in_pos) { - memmove(cc->in_buf, cc->in_buf + in_pos, - cc->in_count - in_pos); - cc->in_count -= in_pos; + *cc->hex_buf[hex_pos].buf++ = (h << 4) | l; + if (--cc->hex_buf[hex_pos].len <= 0) + hex_pos++; } - /* Move pending reads to the start of the array */ - if (read_pos) { - memmove(cc->read_buf, cc->read_buf + read_pos, - (cc->read_count - read_pos) * sizeof (cc->read_buf[0])); - cc->read_count -= read_pos; + /* Move pending hex reads to the start of the array */ + if (hex_pos) { + memmove(cc->hex_buf, cc->hex_buf + hex_pos, + (cc->hex_count - hex_pos) * sizeof (cc->hex_buf[0])); + cc->hex_count -= hex_pos; } - - /* Once we're done reading, flush any pending input */ - if (cc->read_count == 0) - cc->in_count = 0; } static void @@ -158,8 +147,8 @@ cc_usb_dbg(int indent, uint8_t *bytes, int len) * Flush pending writes, fill pending reads */ -int -cc_usb_sync(struct cc_usb *cc) +static int +_cc_usb_sync(struct cc_usb *cc, int wait_for_input) { int ret; struct pollfd fds; @@ -167,26 +156,33 @@ cc_usb_sync(struct cc_usb *cc) fds.fd = cc->fd; for (;;) { - if (cc->read_count || cc->out_count) + if (cc->hex_count || cc->out_count) timeout = 5000; + else if (wait_for_input && cc->in_pos == cc->in_count) + timeout = wait_for_input; else timeout = 0; fds.events = 0; + /* Move remaining bytes to the start of the input buffer */ + if (cc->in_pos) { + memmove(cc->in_buf, cc->in_buf + cc->in_pos, + cc->in_count - cc->in_pos); + cc->in_count -= cc->in_pos; + cc->in_pos = 0; + } if (cc->in_count < CC_IN_BUF) fds.events |= POLLIN; if (cc->out_count) fds.events |= POLLOUT; ret = poll(&fds, 1, timeout); if (ret == 0) { - if (timeout) { - fprintf(stderr, "USB link timeout\n"); - exit(1); - } + if (timeout) + return -1; break; } if (ret < 0) { perror("poll"); - break; + return -1; } if (fds.revents & POLLIN) { ret = read(cc->fd, cc->in_buf + cc->in_count, @@ -194,7 +190,8 @@ cc_usb_sync(struct cc_usb *cc) if (ret > 0) { cc_usb_dbg(24, cc->in_buf + cc->in_count, ret); cc->in_count += ret; - cc_handle_in(cc); + if (cc->hex_count) + cc_handle_hex_read(cc); } else if (ret < 0) perror("read"); } @@ -211,6 +208,16 @@ cc_usb_sync(struct cc_usb *cc) perror("write"); } } + return 0; +} + +void +cc_usb_sync(struct cc_usb *cc) +{ + if (_cc_usb_sync(cc, 0) < 0) { + fprintf(stderr, "USB link timeout\n"); + exit(1); + } } void @@ -244,6 +251,38 @@ cc_usb_printf(struct cc_usb *cc, char *format, ...) } } +int +cc_usb_getchar(struct cc_usb *cc) +{ + while (cc->in_pos == cc->in_count) { + if (_cc_usb_sync(cc, 5000) < 0) { + fprintf(stderr, "USB link timeout\n"); + exit(1); + } + } + return cc->in_buf[cc->in_pos++]; +} + +void +cc_usb_getline(struct cc_usb *cc, char *line, int max) +{ + int c; + + while ((c = cc_usb_getchar(cc)) != '\n') { + switch (c) { + case '\r': + break; + default: + if (max > 1) { + *line++ = c; + max--; + } + break; + } + } + *line++ = '\0'; +} + int cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len) { @@ -266,12 +305,18 @@ cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len) void cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len) { - struct cc_read *read_buf; - while (cc->read_count >= CC_NUM_READ) + struct cc_hex_read *hex_buf; + + /* At the start of a command sequence, flush any pending input */ + if (cc->hex_count == 0) { cc_usb_sync(cc); - read_buf = &cc->read_buf[cc->read_count++]; - read_buf->buf = buf; - read_buf->len = len; + cc->in_count = 0; + } + while (cc->hex_count >= CC_NUM_HEX_READ) + cc_usb_sync(cc); + hex_buf = &cc->hex_buf[cc->hex_count++]; + hex_buf->buf = buf; + hex_buf->len = len; } int @@ -351,9 +396,10 @@ cc_usb_open(char *tty) cfmakeraw(&termios); tcsetattr(cc->fd, TCSAFLUSH, &termios); cc_usb_printf(cc, "E 0\nm 0\n"); - cc_usb_sync(cc); - sleep(1); - cc_usb_sync(cc); + do { + cc->in_count = cc->in_pos = 0; + _cc_usb_sync(cc, 100); + } while (cc->in_count > 0); return cc; } diff --git a/ao-tools/lib/cc-usb.h b/ao-tools/lib/cc-usb.h index 9baabd95..7b6be350 100644 --- a/ao-tools/lib/cc-usb.h +++ b/ao-tools/lib/cc-usb.h @@ -47,12 +47,18 @@ cc_usb_debug_mode(struct cc_usb *cc); int cc_usb_reset(struct cc_usb *cc); -int +void cc_usb_sync(struct cc_usb *cc); void cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len); +int +cc_usb_getchar(struct cc_usb *cc); + +void +cc_usb_getline(struct cc_usb *cc, char *line, int max); + void cc_usb_printf(struct cc_usb *cc, char *format, ...); diff --git a/ao-tools/lib/cc-util.c b/ao-tools/lib/cc-util.c index 7104470c..65488ee9 100644 --- a/ao-tools/lib/cc-util.c +++ b/ao-tools/lib/cc-util.c @@ -15,8 +15,8 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include "cc.h" #define _GNU_SOURCE +#include "cc.h" #include #include #include diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index 0933f272..57f80b8d 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -18,6 +18,8 @@ #ifndef _CC_H_ #define _CC_H_ +#include + char * cc_fullname (char *dir, char *file); @@ -51,4 +53,219 @@ cc_usbdevs_scan(void); char * cc_usbdevs_find_by_arg(char *arg, char *default_product); +void +cc_set_log_dir(char *dir); + +char * +cc_get_log_dir(void); + +char * +cc_make_filename(int serial, char *ext); + +/* + * For sequential data which are not evenly spaced + */ + +struct cc_timedataelt { + double time; + double value; +}; + +struct cc_timedata { + int num; + int size; + struct cc_timedataelt *data; + double time_offset; +}; + + +/* + * For GPS data + */ + +struct cc_gpselt { + double time; + double lat; + double lon; + double alt; +}; + +struct cc_gpsdata { + int num; + int size; + struct cc_gpselt *data; + double time_offset; +}; + +/* + * For sequential data which are evenly spaced + */ +struct cc_perioddata { + int num; + double start; + double step; + double *data; +}; + +enum ao_flight_state { + ao_flight_startup = 0, + ao_flight_idle = 1, + ao_flight_pad = 2, + ao_flight_boost = 3, + ao_flight_fast = 4, + ao_flight_coast = 5, + ao_flight_drogue = 6, + ao_flight_main = 7, + ao_flight_landed = 8, + ao_flight_invalid = 9 +}; + +struct cc_flightraw { + int flight; + int serial; + double ground_accel; + double ground_pres; + struct cc_timedata accel; + struct cc_timedata pres; + struct cc_timedata temp; + struct cc_timedata volt; + struct cc_timedata main; + struct cc_timedata drogue; + struct cc_timedata state; + struct cc_gpsdata gps; +}; + +struct cc_flightraw * +cc_log_read(FILE *file); + +void +cc_flightraw_free(struct cc_flightraw *raw); + +struct cc_flightcooked { + struct cc_perioddata accel_accel; + struct cc_perioddata accel_speed; + struct cc_perioddata accel_pos; + struct cc_perioddata pres_pos; + struct cc_perioddata pres_speed; + struct cc_perioddata pres_accel; + struct cc_perioddata gps_lat; + struct cc_perioddata gps_lon; + struct cc_perioddata gps_alt; + struct cc_timedata state; +}; + +/* + * Telemetry data contents + */ + + +struct cc_gps_time { + int hour; + int minute; + int second; +}; + +struct cc_gps { + int nsat; + int gps_locked; + int gps_connected; + struct cc_gps_time gps_time; + double lat; /* degrees (+N -S) */ + double lon; /* degrees (+E -W) */ + int alt; /* m */ + + int gps_extended; /* has extra data */ + double ground_speed; /* m/s */ + int course; /* degrees */ + double climb_rate; /* m/s */ + double hdop; /* unitless? */ + int h_error; /* m */ + int v_error; /* m */ +}; + +#define SIRF_SAT_STATE_ACQUIRED (1 << 0) +#define SIRF_SAT_STATE_CARRIER_PHASE_VALID (1 << 1) +#define SIRF_SAT_BIT_SYNC_COMPLETE (1 << 2) +#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE (1 << 3) +#define SIRF_SAT_CARRIER_PULLIN_COMPLETE (1 << 4) +#define SIRF_SAT_CODE_LOCKED (1 << 5) +#define SIRF_SAT_ACQUISITION_FAILED (1 << 6) +#define SIRF_SAT_EPHEMERIS_AVAILABLE (1 << 7) + +struct cc_gps_sat { + int svid; + int state; + int c_n0; +}; + +struct cc_gps_tracking { + int channels; + struct cc_gps_sat sats[12]; +}; + +struct cc_telem { + char callsign[16]; + int serial; + int rssi; + char state[16]; + int tick; + int accel; + int pres; + int temp; + int batt; + int drogue; + int main; + int flight_accel; + int ground_accel; + int flight_vel; + int flight_pres; + int ground_pres; + struct cc_gps gps; + struct cc_gps_tracking gps_tracking; +}; + +int +cc_telem_parse(const char *input_line, struct cc_telem *telem); + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* Conversion functions */ +double +cc_pressure_to_altitude(double pressure); + +double +cc_altitude_to_pressure(double altitude); + +double +cc_barometer_to_pressure(double baro); + +double +cc_barometer_to_altitude(double baro); + +double +cc_accelerometer_to_acceleration(double accel, double ground_accel); + +double +cc_thermometer_to_temperature(double thermo); + +double +cc_battery_to_voltage(double battery); + +double +cc_ignitor_to_voltage(double ignite); + +void +cc_great_circle (double start_lat, double start_lon, + double end_lat, double end_lon, + double *dist, double *bearing); + +int +cc_timedata_min(struct cc_timedata *d, double min_time, double max_time); + +int +cc_timedata_max(struct cc_timedata *d, double min_time, double max_time); + #endif /* _CC_H_ */ diff --git a/configure.ac b/configure.ac index 56402857..c668df04 100644 --- a/configure.ac +++ b/configure.ac @@ -77,10 +77,12 @@ ao-tools/Makefile ao-tools/lib/Makefile ao-tools/ao-rawload/Makefile ao-tools/ao-dbg/Makefile +ao-tools/ao-dumplog/Makefile ao-tools/ao-bitbang/Makefile ao-tools/ao-eeprom/Makefile ao-tools/ao-list/Makefile ao-tools/ao-load/Makefile +ao-tools/ao-postflight/Makefile ao-tools/ao-view/Makefile ao-utils/Makefile ])