diff options
| author | Bdale Garbee <bdale@gag.com> | 2009-09-06 10:39:23 -0600 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2009-09-06 10:39:23 -0600 | 
| commit | 35c54b3a278fa9bc2bc7f4b5ee04866697c93ba0 (patch) | |
| tree | 158e5e258d25e1d1f2ffa55492a3db43c16214ba /ao-tools | |
| parent | 4f8eff7401ee2d8092ab36fa33411f9b23dda880 (diff) | |
| parent | 6d018ab933832e2d80bb1564c339d9fb18b57be2 (diff) | |
Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
Diffstat (limited to 'ao-tools')
26 files changed, 1729 insertions, 71 deletions
| 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 <ccdbg.h> +#include <cc.h>  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 <keithp@keithp.com> +.\" +.\" 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 <keithp@keithp.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#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 <tty-name>] [--device <device-name>\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 <unistd.h>  #include <getopt.h>  #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 <tty-name>] file.ihx serial-number\n", program); +	fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] 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 <keithp@keithp.com> +.\" +.\" 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 <keithp@keithp.com> + * + * 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 <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#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 <tty-name>] file.ihx\n", program); +	fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] 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 <keithp@keithp.com> + * + * 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 <keithp@keithp.com> + * + * 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 <math.h> + +/* + * 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 <http://en.wikipedia.org/wiki/International_Standard_Atmosphere + * + * Height measurements use the local tangent plane.  The postive z-direction is up. + * + * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2). + *   The lapse rate is given in Kelvin/meter, the gas constant for air is given + *   in Joules/(kilogram-Kelvin). + */ + +#define GRAVITATIONAL_ACCELERATION -9.80665 +#define AIR_GAS_CONSTANT	287.053 +#define NUMBER_OF_LAYERS	7 +#define MAXIMUM_ALTITUDE	84852.0 +#define MINIMUM_PRESSURE	0.3734 +#define LAYER0_BASE_TEMPERATURE	288.15 +#define LAYER0_BASE_PRESSURE	101325 + +/* lapse rate and base altitude for each layer in the atmosphere */ +static const double lapse_rate[NUMBER_OF_LAYERS] = { +	-0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002 +}; + +static const int base_altitude[NUMBER_OF_LAYERS] = { +	0, 11000, 20000, 32000, 47000, 51000, 71000 +}; + +/* outputs atmospheric pressure associated with the given altitude. altitudes +   are measured with respect to the mean sea level */ +double +cc_altitude_to_pressure(double altitude) +{ + +   double base_temperature = LAYER0_BASE_TEMPERATURE; +   double base_pressure = LAYER0_BASE_PRESSURE; + +   double pressure; +   double base; /* base for function to determine pressure */ +   double exponent; /* exponent for function to determine pressure */ +   int layer_number; /* identifies layer in the atmosphere */ +   int delta_z; /* difference between two altitudes */ + +   if (altitude > 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 <keithp@keithp.com> + * + * 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 <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <gconf/gconf-client.h> +#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 <keithp@keithp.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +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 <keithp@keithp.com> + * + * 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 <string.h> +#include <stdlib.h> + +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 @@ -245,6 +252,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)  {  	int	this_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 <string.h>  #include <stdlib.h>  #include <unistd.h> 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 <stdio.h> +  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_ */ | 
