Add plots to ao-postflight using the plplot library
authorKeith Packard <keithp@keithp.com>
Sun, 6 Sep 2009 23:24:35 +0000 (16:24 -0700)
committerKeith Packard <keithp@keithp.com>
Sun, 6 Sep 2009 23:24:35 +0000 (16:24 -0700)
It's not perfect, but it generates .svg plot output.

Signed-off-by: Keith Packard <keithp@keithp.com>
ao-tools/ao-postflight/Makefile.am
ao-tools/ao-postflight/ao-postflight.c
ao-tools/lib/cc-analyse.c
ao-tools/lib/cc-process.c
ao-tools/lib/cc.h
configure.ac

index 301ac45..589d164 100644 (file)
@@ -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
 
index c1e4d80..bc6638e 100644 (file)
@@ -24,8 +24,7 @@
 #include <getopt.h>
 #include "cc-usb.h"
 #include "cc.h"
-
-#define NUM_BLOCK      512
+#include <plplot/plplot.h>
 
 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=<summary-file>] [--detail=<detail-file] {flight-log} ...\n", program);
+       fprintf(stderr, "usage: %s [--summary=<summary-file>] [--detail=<detail-file] [--plot=<plot-file>] {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;
index 0e02011..cdb16f0 100644 (file)
@@ -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);
+}
index e906b63..469ad2f 100644 (file)
@@ -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);
+}
index 356794e..4e9aadc 100644 (file)
@@ -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_ */
index c668df0..6265c34 100644 (file)
@@ -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