From: Keith Packard Date: Sun, 6 Sep 2009 23:24:35 +0000 (-0700) Subject: Add plots to ao-postflight using the plplot library X-Git-Tag: debian/0.5+77+gc57bd7f~8^2~6 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=32d3536706324808df6cd02248a236302b831571 Add plots to ao-postflight using the plplot library It's not perfect, but it generates .svg plot output. Signed-off-by: Keith Packard --- diff --git a/ao-tools/ao-postflight/Makefile.am b/ao-tools/ao-postflight/Makefile.am index 301ac454..589d164a 100644 --- a/ao-tools/ao-postflight/Makefile.am +++ b/ao-tools/ao-postflight/Makefile.am @@ -1,11 +1,11 @@ bin_PROGRAMS=ao-postflight -AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) +AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) $(PLPLOT_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_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS) $(PLPLOT_LIBS) ao_postflight_SOURCES = ao-postflight.c diff --git a/ao-tools/ao-postflight/ao-postflight.c b/ao-tools/ao-postflight/ao-postflight.c index c1e4d800..bc6638e9 100644 --- a/ao-tools/ao-postflight/ao-postflight.c +++ b/ao-tools/ao-postflight/ao-postflight.c @@ -24,8 +24,7 @@ #include #include "cc-usb.h" #include "cc.h" - -#define NUM_BLOCK 512 +#include static const char *state_names[] = { "startup", @@ -40,12 +39,75 @@ static const char *state_names[] = { "invalid" }; -void -analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file) +static void +plot_perioddata(struct cc_perioddata *d, char *axis_label, char *plot_label, + double min_time, double max_time) +{ + double *times; + double ymin, ymax; + int ymin_i, ymax_i; + int i; + int start, stop; + + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) + return; + + times = calloc(stop - start + 1, sizeof (double)); + for (i = start; i <= stop; i++) + times[i-start] = i * d->step / 100.0; + + ymin_i = cc_perioddata_min(d, min_time, max_time); + ymax_i = cc_perioddata_max(d, min_time, max_time); + ymin = d->data[ymin_i]; + ymax = d->data[ymax_i]; + plenv(times[0], times[stop-start], + ymin, ymax, 0, 2); + plcol0(1); + pllab("Time", axis_label, plot_label); + plline(stop - start + 1, times, d->data + start); +} + +static struct cc_perioddata * +merge_data(struct cc_perioddata *first, struct cc_perioddata *last, double split_time) +{ + int i; + struct cc_perioddata *pd; + int num; + double start_time, stop_time; + double t; + + pd = calloc(1, sizeof (struct cc_perioddata)); + start_time = first->start; + stop_time = last->start + last->step * last->num; + num = (stop_time - start_time) / first->step; + pd->num = num; + pd->data = calloc(num, sizeof (double)); + pd->start = first->start; + pd->step = first->step; + for (i = 0; i < num; i++) { + t = pd->start + i * pd->step; + if (t <= split_time) { + pd->data[i] = first->data[i]; + } else { + int j; + + j = (t - last->start) / last->step; + if (j < 0 || j >= last->num) + pd->data[i] = 0; + else + pd->data[i] = last->data[j]; + } + } + return pd; +} + +static void +analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file, char *plot_name) { double height; double accel; double speed; + double avg_speed; double boost_start, boost_stop; double min_pres; int i; @@ -129,18 +191,24 @@ analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file) if (cooked) { if (state < ao_flight_drogue) { - speed_i = cc_perioddata_max(&cooked->accel_speed, state_start, state_stop); + speed_i = cc_perioddata_max_mag(&cooked->accel_speed, state_start, state_stop); if (speed_i >= 0) speed = cooked->accel_speed.data[speed_i]; + avg_speed = cc_perioddata_average(&cooked->accel_speed, state_start, state_stop); } else { - speed_i = cc_perioddata_max(&cooked->pres_speed, state_start, state_stop); + speed_i = cc_perioddata_max_mag(&cooked->pres_speed, state_start, state_stop); if (speed_i >= 0) speed = cooked->pres_speed.data[speed_i]; + avg_speed = cc_perioddata_average(&cooked->pres_speed, state_start, state_stop); } if (speed_i >= 0) + { fprintf(summary_file, "\tMax speed: %9.2fm/s %9.2fft/s %9.2fs\n", speed, speed * 100 / 2.4 / 12.0, (cooked->accel_speed.start + speed_i * cooked->accel_speed.step - boost_start) / 100.0); + fprintf(summary_file, "\tAvg speed: %9.2fm/s %9.2fft/s\n", + avg_speed, avg_speed * 100 / 2.4 / 12.0); + } } pres_i = cc_timedata_min(&f->pres, state_start, state_stop); if (pres_i >= 0) @@ -154,16 +222,10 @@ analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file) } } if (cooked && detail_file) { - double apogee_time; double max_height = 0; int i; + double *times; - for (i = 0; i < cooked->pres_pos.num; i++) { - if (cooked->pres_pos.data[i] > max_height) { - max_height = cooked->pres_pos.data[i]; - apogee_time = cooked->pres_pos.start + cooked->pres_pos.step * i; - } - } fprintf(detail_file, "%9s %9s %9s %9s\n", "time", "height", "speed", "accel"); for (i = 0; i < cooked->pres_pos.num; i++) { @@ -171,7 +233,7 @@ analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file) double accel = cooked->accel_accel.data[i]; double pos = cooked->pres_pos.data[i]; double speed; - if (cooked->pres_pos.start + cooked->pres_pos.step * i < apogee_time) + if (cooked->pres_pos.start + cooked->pres_pos.step * i < apogee) speed = cooked->accel_speed.data[i]; else speed = cooked->pres_speed.data[i]; @@ -179,17 +241,41 @@ analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file) time, pos, speed, accel); } } + if (cooked && plot_name) { + struct cc_perioddata *speed; + plsdev("svgcairo"); + plsfnam(plot_name); +#define PLOT_DPI 96 + plspage(PLOT_DPI, PLOT_DPI, 8 * PLOT_DPI, 8 * PLOT_DPI, 0, 0); + plscolbg(0xff, 0xff, 0xff); + plscol0(1,0,0,0); + plstar(2, 3); + speed = merge_data(&cooked->accel_speed, &cooked->pres_speed, apogee); + + plot_perioddata(&cooked->pres_pos, "meters", "Height", -1e10, 1e10); + plot_perioddata(&cooked->pres_pos, "meters", "Height", boost_start, apogee); + plot_perioddata(speed, "meters/second", "Speed", -1e10, 1e10); + plot_perioddata(speed, "meters/second", "Speed", boost_start, apogee); + plot_perioddata(&cooked->accel_accel, "meters/second²", "Acceleration", -1e10, 1e10); + plot_perioddata(&cooked->accel_accel, "meters/second²", "Acceleration", boost_start, apogee); + free(speed->data); + free(speed); + plend(); + } + if (cooked) + cc_flightcooked_free(cooked); } static const struct option options[] = { - { .name = "summary", .has_arg = 1, .val = 'S' }, - { .name = "detail", .has_arg = 1, .val = 'D' }, + { .name = "summary", .has_arg = 1, .val = 's' }, + { .name = "detail", .has_arg = 1, .val = 'd' }, + { .name = "plot", .has_arg = 1, .val = 'p' }, { 0, 0, 0, 0}, }; static void usage(char *program) { - fprintf(stderr, "usage: %s [--summary=] [--detail=] [--detail=] {flight-log} ...\n", program); exit(1); } @@ -206,15 +292,19 @@ main (int argc, char **argv) int serial; char *s; char *summary_name = NULL, *detail_name = NULL; + char *plot_name = NULL; - while ((c = getopt_long(argc, argv, "S:D:", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "s:d:p:", options, NULL)) != -1) { switch (c) { - case 'S': + case 's': summary_name = optarg; break; - case 'D': + case 'd': detail_name = optarg; break; + case 'p': + plot_name = optarg; + break; default: usage(argv[0]); break; @@ -260,7 +350,7 @@ main (int argc, char **argv) } if (!raw->serial) raw->serial = serial; - analyse_flight(raw, summary_file, detail_file); + analyse_flight(raw, summary_file, detail_file, plot_name); cc_flightraw_free(raw); } return ret; diff --git a/ao-tools/lib/cc-analyse.c b/ao-tools/lib/cc-analyse.c index 0e020115..cdb16f02 100644 --- a/ao-tools/lib/cc-analyse.c +++ b/ao-tools/lib/cc-analyse.c @@ -38,6 +38,26 @@ cc_timedata_min(struct cc_timedata *d, double min_time, double max_time) return min_i; } +int +cc_timedata_min_mag(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 || fabs(d->data[i].value) < min) { + min_i = i; + min = fabs(d->data[i].value); + set = 1; + } + return min_i; +} + int cc_timedata_max(struct cc_timedata *d, double min_time, double max_time) { @@ -58,23 +78,81 @@ cc_timedata_max(struct cc_timedata *d, double min_time, double max_time) return max_i; } +int +cc_timedata_max_mag(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 || fabs(d->data[i].value) > max) { + max_i = i; + max = fabs(d->data[i].value); + set = 1; + } + return max_i; +} + +double +cc_timedata_average(struct cc_timedata *td, double start_time, double stop_time) +{ + int i; + double prev_time; + double next_time; + double interval; + double sum = 0.0; + double period = 0.0; + + prev_time = start_time; + for (i = 0; i < td->num; i++) { + if (start_time <= td->data[i].time && td->data[i].time <= stop_time) { + if (i < td->num - 1 && td->data[i+1].time < stop_time) + next_time = (td->data[i].time + td->data[i+1].time) / 2.0; + else + next_time = stop_time; + interval = next_time - prev_time; + sum += td->data[i].value * interval; + period += interval; + prev_time = next_time; + } + } + return sum / period; +} + +int +cc_perioddata_limits(struct cc_perioddata *d, double min_time, double max_time, int *start, int *stop) +{ + double start_d, stop_d; + + if (d->num == 0) + return 0; + start_d = ceil((min_time - d->start) / d->step); + if (start_d < 0) + start_d = 0; + stop_d = floor((max_time - d->start) / d->step); + if (stop_d >= d->num) + stop_d = d->num - 1; + if (stop_d < start_d) + return 0; + *start = (int) start_d; + *stop = (int) stop_d; + return 1; +} + int cc_perioddata_min(struct cc_perioddata *d, double min_time, double max_time) { - int start, stop; int i; double min; int min_i; + int start, stop; - if (d->num == 0) - return -1; - start = (int) ceil((min_time - d->start) / d->step); - if (start < 0) - start = 0; - stop = (int) floor((max_time - d->start) / d->step); - if (stop >= d->num) - stop = d->num - 1; - if (stop < start) + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) return -1; min = d->data[start]; min_i = start; @@ -86,6 +164,26 @@ cc_perioddata_min(struct cc_perioddata *d, double min_time, double max_time) return min_i; } +int +cc_perioddata_min_mag(struct cc_perioddata *d, double min_time, double max_time) +{ + int start, stop; + int i; + double min; + int min_i; + + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) + return -1; + min = d->data[start]; + min_i = start; + for (i = start + 1; i <= stop; i++) + if (fabs(d->data[i]) < min) { + min = fabs(d->data[i]); + min_i = i; + } + return min_i; +} + int cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time) { @@ -94,15 +192,27 @@ cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time) double max; int max_i; - if (d->num == 0) + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) return -1; - start = (int) ceil((min_time - d->start) / d->step); - if (start < 0) - start = 0; - stop = (int) floor((max_time - d->start) / d->step); - if (stop >= d->num) - stop = d->num - 1; - if (stop < start) + max = d->data[start]; + max_i = start; + for (i = start + 1; i <= stop; i++) + if (d->data[i] > max) { + max = d->data[i]; + max_i = i; + } + return max_i; +} + +int +cc_perioddata_max_mag(struct cc_perioddata *d, double min_time, double max_time) +{ + int start, stop; + int i; + double max; + int max_i; + + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) return -1; max = d->data[start]; max_i = start; @@ -113,3 +223,31 @@ cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time) } return max_i; } + +double +cc_perioddata_average(struct cc_perioddata *d, double min_time, double max_time) +{ + int start, stop; + int i; + double sum = 0.0; + + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) + return 0.0; + for (i = start; i <= stop; i++) + sum += d->data[i]; + return sum / (stop - start + 1); +} + +double +cc_perioddata_average_mag(struct cc_perioddata *d, double min_time, double max_time) +{ + int start, stop; + int i; + double sum = 0.0; + + if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop)) + return 0.0; + for (i = start; i <= stop; i++) + sum += fabs(d->data[i]); + return sum / (stop - start + 1); +} diff --git a/ao-tools/lib/cc-process.c b/ao-tools/lib/cc-process.c index e906b635..469ad2f2 100644 --- a/ao-tools/lib/cc-process.c +++ b/ao-tools/lib/cc-process.c @@ -138,3 +138,21 @@ cc_flight_cook(struct cc_flightraw *raw) cooked->state.time_offset = raw->state.time_offset; return cooked; } + +#define if_free(x) ((x) ? free(x) : (void) 0) + +void +cc_flightcooked_free(struct cc_flightcooked *cooked) +{ + if_free(cooked->accel_accel.data); + if_free(cooked->accel_speed.data); + if_free(cooked->accel_pos.data); + if_free(cooked->pres_pos.data); + if_free(cooked->pres_speed.data); + if_free(cooked->pres_accel.data); + if_free(cooked->gps_lat.data); + if_free(cooked->gps_lon.data); + if_free(cooked->gps_alt.data); + if_free(cooked->state.data); + free(cooked); +} diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h index 356794e0..4e9aadc4 100644 --- a/ao-tools/lib/cc.h +++ b/ao-tools/lib/cc.h @@ -265,15 +265,42 @@ cc_great_circle (double start_lat, double start_lon, int cc_timedata_min(struct cc_timedata *d, double min_time, double max_time); +int +cc_timedata_min_mag(struct cc_timedata *d, double min_time, double max_time); + int cc_timedata_max(struct cc_timedata *d, double min_time, double max_time); +int +cc_timedata_max_mag(struct cc_timedata *d, double min_time, double max_time); + +double +cc_timedata_average(struct cc_timedata *d, double min_time, double max_time); + +double +cc_timedata_average_mag(struct cc_timedata *d, double min_time, double max_time); + +int +cc_perioddata_limits(struct cc_perioddata *d, double min_time, double max_time, int *start, int *stop); + int cc_perioddata_min(struct cc_perioddata *d, double min_time, double max_time); +int +cc_perioddata_min_mag(struct cc_perioddata *d, double min_time, double max_time); + int cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time); +int +cc_perioddata_max_mag(struct cc_perioddata *d, double min_time, double max_time); + +double +cc_perioddata_average(struct cc_perioddata *d, double min_time, double max_time); + +double +cc_perioddata_average_mag(struct cc_perioddata *d, double min_time, double max_time); + double * cc_low_pass(double *data, int data_len, double omega_pass, double omega_stop, double error); @@ -295,5 +322,7 @@ cc_perioddata_differentiate(struct cc_perioddata *i); struct cc_flightcooked * cc_flight_cook(struct cc_flightraw *raw); +void +cc_flightcooked_free(struct cc_flightcooked *cooked); #endif /* _CC_H_ */ diff --git a/configure.ac b/configure.ac index c668df04..6265c34e 100644 --- a/configure.ac +++ b/configure.ac @@ -71,6 +71,8 @@ PKG_CHECK_MODULES([LIBUSB], [libusb-1.0]) PKG_CHECK_MODULES([ALSA], [alsa]) +PKG_CHECK_MODULES([PLPLOT], [plplotd-gnome2]) + AC_OUTPUT([ Makefile ao-tools/Makefile