"invalid"
};
+static const char *kml_state_colours[] = {
+ "FF000000",
+ "FF000000",
+ "FF000000",
+ "FF0000FF",
+ "FF4080FF",
+ "FF00FFFF",
+ "FFFF0000",
+ "FF00FF00",
+ "FF000000",
+ "FFFFFFFF"
+};
+
static int plot_colors[3][3] = {
{ 0, 0x90, 0 }, /* height */
{ 0xa0, 0, 0 }, /* speed */
return pd;
}
-static const char kml_header[] =
+static const char kml_header_start[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
- "<kml xmlns=\"http://earth.google.com/kml/2.0\">\n"
+ "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
+ "<Document>\n"
+ " <name>%s</name>\n"
+ " <description>\n";
+static const char kml_header_end[] =
+ " </description>\n"
+ " <open>0</open>\n";
+
+static const char kml_style_start[] =
+ " <Style id=\"ao-flightstate-%s\">\n"
+ " <LineStyle><color>%s</color><width>4</width></LineStyle>\n"
+ " <BalloonStyle>\n"
+ " <text>\n";
+static const char kml_style_end[] =
+ " </text>\n"
+ " </BalloonStyle>\n"
+ " </Style>\n";
+
+static const char kml_placemark_start[] =
" <Placemark>\n"
- " <name>gps</name>\n"
- " <Style id=\"khStyle690\">\n"
- " <LineStyle id=\"khLineStyle694\">\n"
- " <color>ff00ffff</color>\n"
- " <width>4</width>\n"
- " </LineStyle>\n"
- " </Style>\n"
- " <MultiGeometry id=\"khMultiGeometry697\">\n"
- " <LineString id=\"khLineString698\">\n"
- " <tessellate>1</tessellate>\n"
- " <altitudeMode>absolute</altitudeMode>\n"
- " <coordinates>\n";
+ " <name>%s</name>\n"
+ " <styleUrl>#ao-flightstate-%s</styleUrl>\n"
+ " <LineString>\n"
+ " <tessellate>1</tessellate>\n"
+ " <altitudeMode>absolute</altitudeMode>\n"
+ " <coordinates>\n";
+
+static const char kml_coord_fmt[] =
+ " %12.7f, %12.7f, %12.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
+
+static const char kml_placemark_end[] =
+ " </coordinates>\n"
+ " </LineString>\n"
+ " </Placemark>\n";
static const char kml_footer[] =
- "</coordinates>\n"
+ " </coordinates>\n"
" </LineString>\n"
- " </MultiGeometry>\n"
- "</Placemark>\n"
+ " </Placemark>\n"
+ "</Document>\n"
"</kml>\n";
+static unsigned
+gps_daytime(struct cc_gpselt *gps)
+{
+ return ((gps->hour * 60 +
+ gps->minute) * 60 +
+ gps->second) * 1000;
+}
+
+int
+daytime_hour(unsigned daytime)
+{
+ return daytime / 1000 / 60 / 60;
+}
+
+int
+daytime_minute(unsigned daytime)
+{
+ return (daytime / 1000 / 60) % 60;
+}
+
+int
+daytime_second(unsigned daytime)
+{
+ return (daytime / 1000) % 60;
+}
+
+int
+daytime_millisecond(unsigned daytime)
+{
+ return daytime % 1000;
+}
+
+static unsigned
+compute_daytime_ms(double time, struct cc_gpsdata *gps)
+{
+ int i;
+ unsigned gps_start_daytime, gps_stop_daytime;
+
+ if (time <= gps->data[0].time) {
+ gps_stop_daytime = gps_daytime(&gps->data[0]);
+ return gps_stop_daytime - (gps->data[0].time - time) * 10;
+ }
+ for (i = 0; i < gps->num - 1; i++)
+ if (time > gps->data[i].time)
+ break;
+ gps_start_daytime = gps_daytime(&gps->data[i]);
+ if (i == gps->num - 1) {
+ return gps_start_daytime + (time - gps->data[i].time) * 10;
+ } else {
+ unsigned gps_period_daytime;
+ double gps_period_time;
+ double time_since_start;
+
+ gps_stop_daytime = gps_daytime(&gps->data[i + 1]);
+
+ /* range of gps daytime values */
+ gps_period_daytime = gps_stop_daytime - gps_start_daytime;
+
+ /* range of gps time values */
+ gps_period_time = gps->data[i+1].time - gps->data[i].time;
+
+ /* sample time after first gps time */
+ time_since_start = time - gps->data[i].time;
+
+ return gps_start_daytime +
+ gps_period_daytime * time_since_start / gps_period_time;
+ }
+}
+
static void
analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file,
FILE *raw_file, char *plot_name, FILE *gps_file, FILE *kml_file)
double state_start, state_stop;
struct cc_flightcooked *cooked;
double apogee;
+ char buf[128];
+
+ if (kml_file) {
+ snprintf(buf, sizeof (buf), "AO Flight#%d S/N: %03d", f->flight, f->serial);
+ fprintf(kml_file, kml_header_start, buf);
+ }
fprintf(summary_file,
"Serial: %9d\n"
"Flight: %9d\n",
f->serial, f->flight);
+
if (f->year) {
- fprintf(summary_file,
+ snprintf(buf, sizeof (buf),
"Date: %04d-%02d-%02d\n",
f->year, f->month, f->day);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
if (f->gps.num) {
- fprintf(summary_file,
+ snprintf(buf, sizeof (buf),
"Time: %2d:%02d:%02d\n",
f->gps.data[0].hour,
f->gps.data[0].minute,
f->gps.data[0].second);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
boost_start = f->accel.data[0].time;
boost_stop = f->accel.data[f->accel.num-1].time;
min_pres = f->pres.data[pres_i].value;
height = cc_barometer_to_altitude(min_pres) -
cc_barometer_to_altitude(f->ground_pres);
- fprintf(summary_file, "Max height: %9.2fm %9.2fft %9.2fs\n",
+ apogee = f->pres.data[pres_i].time;
+ snprintf(buf, sizeof (buf), "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);
- apogee = f->pres.data[pres_i].time;
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
cooked = cc_flight_cook(f);
speed_i = cc_perioddata_max(&cooked->accel_speed, boost_start, boost_stop);
if (speed_i >= 0) {
speed = cooked->accel_speed.data[speed_i];
- fprintf(summary_file, "Max speed: %9.2fm/s %9.2fft/s %9.2fs\n",
+ snprintf(buf, sizeof (buf), "Max 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, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
}
accel_i = cc_timedata_min(&f->accel, boost_start, boost_stop);
{
accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value,
f->ground_accel);
- fprintf(summary_file, "Max accel: %9.2fm/s² %9.2fg %9.2fs\n",
+ snprintf(buf, sizeof (buf), "Max accel: %9.2fm/s² %9.2fg %9.2fs\n",
accel, accel / 9.80665,
(f->accel.data[accel_i].time - boost_start) / 100.0);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
+ if (kml_file)
+ fprintf(kml_file, "%s", kml_header_end);
+
for (i = 0; i < f->state.num; i++) {
state = f->state.data[i].value;
state_start = f->state.data[i].time;
fprintf(summary_file, "State: %s\n", state_names[state]);
fprintf(summary_file, "\tStart: %9.2fs\n", (state_start - boost_start) / 100.0);
fprintf(summary_file, "\tDuration: %9.2fs\n", (state_stop - state_start) / 100.0);
+ if (kml_file) {
+ fprintf(kml_file, kml_style_start, state_names[state], kml_state_colours[state]);
+ fprintf(kml_file, "\tState: %s\n", state_names[state]);
+ fprintf(kml_file, "\tStart: %9.2fs\n", (state_start - boost_start) / 100.0);
+ fprintf(kml_file, "\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);
- fprintf(summary_file, "\tMax accel: %9.2fm/s² %9.2fg %9.2fs\n",
+ snprintf(buf, sizeof (buf), "\tMax accel: %9.2fm/s² %9.2fg %9.2fs\n",
accel, accel / 9.80665,
(f->accel.data[accel_i].time - boost_start) / 100.0);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
if (cooked) {
}
if (speed_i >= 0)
{
- fprintf(summary_file, "\tMax speed: %9.2fm/s %9.2fft/s %9.2fs\n",
+ snprintf(buf, sizeof (buf), "\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",
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
+
+ snprintf(buf, sizeof (buf), "\tAvg speed: %9.2fm/s %9.2fft/s\n",
avg_speed, avg_speed * 100 / 2.4 / 12.0);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
}
pres_i = cc_timedata_min(&f->pres, state_start, state_stop);
min_pres = f->pres.data[pres_i].value;
height = cc_barometer_to_altitude(min_pres) -
cc_barometer_to_altitude(f->ground_pres);
- fprintf(summary_file, "\tMax height: %9.2fm %9.2fft %9.2fs\n",
+ snprintf(buf, sizeof (buf), "\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);
+ fprintf(summary_file, "%s", buf);
+ if (kml_file) fprintf(kml_file, "%s", buf);
}
+ if (kml_file) fprintf(kml_file, "%s", kml_style_end);
}
if (cooked && detail_file) {
double max_height = 0;
int i;
double *times;
- fprintf(detail_file, "%9s %9s %9s %9s\n",
- "time", "height", "speed", "accel");
+ fprintf(detail_file, "%9s %9s %9s %9s %9s\n",
+ "time", "height", "speed", "accel", "daytime");
for (i = 0; i < cooked->pres_pos.num; i++) {
- double time = (cooked->accel_accel.start + i * cooked->accel_accel.step - boost_start) / 100.0;
+ double clock_time = cooked->accel_accel.start + i * cooked->accel_accel.step;
+ double time = (clock_time - boost_start) / 100.0;
double accel = cooked->accel_accel.data[i];
double pos = cooked->pres_pos.data[i];
double speed;
+ unsigned daytime;
if (cooked->pres_pos.start + cooked->pres_pos.step * i < apogee)
speed = cooked->accel_speed.data[i];
else
speed = cooked->pres_speed.data[i];
- fprintf(detail_file, "%9.2f %9.2f %9.2f %9.2f\n",
- time, pos, speed, accel);
+ if (f->gps.num)
+ daytime = compute_daytime_ms(clock_time, &f->gps);
+ else
+ daytime = 0;
+ fprintf(detail_file, "%9.2f %9.2f %9.2f %9.2f %02d:%02d:%02d.%03d\n",
+ time, pos, speed, accel,
+ daytime_hour(daytime),
+ daytime_minute(daytime),
+ daytime_second(daytime),
+ daytime_millisecond(daytime));
}
}
if (raw_file) {
- fprintf(raw_file, "%9s %9s %9s\n",
- "time", "height", "accel");
+ fprintf(raw_file, "%9s %9s %9s %9s\n",
+ "time", "height", "accel", "daytime");
for (i = 0; i < cooked->pres.num; i++) {
double time = cooked->pres.data[i].time;
double pres = cooked->pres.data[i].value;
double accel = cooked->accel.data[i].value;
- fprintf(raw_file, "%9.2f %9.2f %9.2f %9.2f\n",
- time, pres, accel);
+ unsigned daytime;
+ if (f->gps.num)
+ daytime = compute_daytime_ms(time, &f->gps);
+ else
+ daytime = 0;
+ fprintf(raw_file, "%9.2f %9.2f %9.2f %02d:%02d:%02d.%03d\n",
+ time, pres, accel,
+ daytime_hour(daytime),
+ daytime_minute(daytime),
+ daytime_second(daytime),
+ daytime_millisecond(daytime));
}
}
if (gps_file || kml_file) {
int j = 0, baro_pos;
double baro_offset;
double baro = 0.0;
+ int state_idx = 0;
if (gps_file)
- fprintf(gps_file, "%9s %12s %12s %9s %8s %5s\n",
+ fprintf(gps_file, "%2s %2s %2s %9s %12s %12s %9s %8s %5s\n",
+ "hr", "mn", "sc",
"time", "lat", "lon", "alt", "baro", "nsat");
if (kml_file)
- fprintf(kml_file, "%s", kml_header);
+ fprintf(kml_file, kml_placemark_start,
+ state_names[(int)f->state.data[state_idx].value],
+ state_names[(int)f->state.data[state_idx].value]);
+
if (f->gps.num)
baro_offset = f->gps.data[0].alt;
else
baro = cooked->pres_pos.data[baro_pos];
}
if (gps_file)
- fprintf(gps_file, "%12.7f %12.7f %12.7f %7.1f %7.1f",
+ fprintf(gps_file, "%2d %2d %2d %12.7f %12.7f %12.7f %7.1f %7.1f",
+ f->gps.data[i].hour,
+ f->gps.data[i].minute,
+ f->gps.data[i].second,
(f->gps.data[i].time - boost_start) / 100.0,
f->gps.data[i].lat,
f->gps.data[i].lon,
f->gps.data[i].alt,
baro + baro_offset);
+
+ nsat = 0;
+ if (f->gps.sats) {
+ for (k = 0; k < f->gps.sats[j].nsat; k++) {
+ if (f->gps.sats[j].sat[k].svid != 0)
+ nsat++;
+ }
+ if (gps_file) {
+ fprintf(gps_file, " %4d", nsat);
+ for (k = 0; k < f->gps.sats[j].nsat; k++) {
+ if (f->gps.sats[j].sat[k].svid != 0) {
+ fprintf (gps_file, " %3d(%4.1f)",
+ f->gps.sats[j].sat[k].svid,
+ (double) f->gps.sats[j].sat[k].c_n);
+ }
+ }
+ fprintf(gps_file, "\n");
+ }
+ }
+
if (kml_file) {
- fprintf(kml_file, "%12.7f, %12.7f, %12.7f <!-- alt %12.7f time %12.7f sats %d -->",
+ snprintf(buf, sizeof (buf), kml_coord_fmt,
f->gps.data[i].lon,
f->gps.data[i].lat,
baro + baro_offset,
f->gps.data[i].alt,
(f->gps.data[i].time - boost_start) / 100.0,
nsat);
- if (i < f->gps.num - 1)
- fprintf(kml_file, ",\n");
- else
- fprintf(kml_file, "\n");
- }
-
- nsat = 0;
- for (k = 0; k < f->gps.sats[j].nsat; k++) {
- if (f->gps.sats[j].sat[k].svid != 0)
- nsat++;
- }
- if (gps_file) {
- fprintf(gps_file, " %4d", nsat);
- for (k = 0; k < f->gps.sats[j].nsat; k++) {
- if (f->gps.sats[j].sat[k].svid != 0) {
- fprintf (gps_file, " %3d(%4.1f)",
- f->gps.sats[j].sat[k].svid,
- (double) f->gps.sats[j].sat[k].c_n);
+ fprintf(kml_file, "%s", buf);
+ if (state_idx + 1 < f->state.num && f->state.data[state_idx + 1].time <= f->gps.data[i].time) {
+ state_idx++;
+ if (f->state.data[state_idx - 1].value != f->state.data[state_idx].value) {
+ fprintf(kml_file, "%s", kml_placemark_end);
+ fprintf(kml_file, kml_placemark_start,
+ state_names[(int)f->state.data[state_idx].value],
+ state_names[(int)f->state.data[state_idx].value]);
+ fprintf(kml_file, "%s", buf);
}
}
- fprintf(gps_file, "\n");
}
+
}
if (kml_file)
fprintf(kml_file, "%s", kml_footer);
if (has_gps && !gps_file)
gps_file = open_output(gps_name, argv[i], ".gps");
if (has_kml && !kml_file)
- kml_file = open_output(gps_name, argv[i], ".kml");
+ kml_file = open_output(kml_name, argv[i], ".kml");
s = strstr(argv[i], "-serial-");
if (s)
serial = atoi(s + 8);