|
@@ -0,0 +1,208 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * GPS Test Tool - PC Application
|
|
|
|
|
+ *
|
|
|
|
|
+ * Tests GPS filtering and GPX generation using actual embedded code
|
|
|
|
|
+ * Usage: ./gps-test-tool <input.nmea>
|
|
|
|
|
+ *
|
|
|
|
|
+ * Generates:
|
|
|
|
|
+ * - output_filtered.gpx
|
|
|
|
|
+ * - output_unfiltered.gpx
|
|
|
|
|
+ * - debug_filtered.txt
|
|
|
|
|
+ * - debug_unfiltered.txt
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+#define PC_BUILD
|
|
|
|
|
+#include "main.h"
|
|
|
|
|
+
|
|
|
|
|
+/* Global variables expected by project code */
|
|
|
|
|
+volatile struct system_s System;
|
|
|
|
|
+struct location_s location;
|
|
|
|
|
+time_t utc;
|
|
|
|
|
+unsigned char config_flags = 0;
|
|
|
|
|
+
|
|
|
|
|
+/* File handle for debug output redirection */
|
|
|
|
|
+FILE *debug_output = NULL;
|
|
|
|
|
+
|
|
|
|
|
+/* Function prototypes from project files */
|
|
|
|
|
+unsigned char gpx_init(FIL *file);
|
|
|
|
|
+unsigned char gpx_close(FIL *file);
|
|
|
|
|
+void gpx_process_point(struct location_s *loc, FIL *file);
|
|
|
|
|
+time_t gps_parse(const char *nmea);
|
|
|
|
|
+
|
|
|
|
|
+/* Time function implementation */
|
|
|
|
|
+char *get_iso_time(time_t time, unsigned char local) {
|
|
|
|
|
+ static char output[32];
|
|
|
|
|
+ struct tm *ct = gmtime(&time);
|
|
|
|
|
+ sprintf(output, "%04d-%02d-%02dT%02d:%02d:%02d.000Z",
|
|
|
|
|
+ ct->tm_year + 1900, ct->tm_mon + 1, ct->tm_mday,
|
|
|
|
|
+ ct->tm_hour, ct->tm_min, ct->tm_sec);
|
|
|
|
|
+ return output;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void iso_time_to_filename(char *time) {
|
|
|
|
|
+ while (*time) {
|
|
|
|
|
+ switch (*time) {
|
|
|
|
|
+ case ':': *time = '-'; break;
|
|
|
|
|
+ case '+': *time = 'p'; break;
|
|
|
|
|
+ }
|
|
|
|
|
+ time++;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* Reset system state */
|
|
|
|
|
+void reset_system(void) {
|
|
|
|
|
+ memset((void*)&System, 0, sizeof(System));
|
|
|
|
|
+ memset(&location, 0, sizeof(location));
|
|
|
|
|
+ System.conf.skip_points = 0; /* Don't skip points for testing */
|
|
|
|
|
+ System.conf.auto_pause_dist = 100;
|
|
|
|
|
+ System.conf.min_sats = 4; /* Require at least 4 satellites */
|
|
|
|
|
+ System.tracking_paused = 0;
|
|
|
|
|
+ System.tracking_auto_paused = 0;
|
|
|
|
|
+ utc = 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* Process NMEA file with specified filter setting */
|
|
|
|
|
+int process_nmea_file(const char *input_file, const char *output_gpx,
|
|
|
|
|
+ const char *debug_file, int filters_enabled) {
|
|
|
|
|
+ FILE *input;
|
|
|
|
|
+ FIL gpx_file;
|
|
|
|
|
+ char line[256];
|
|
|
|
|
+ int point_count = 0;
|
|
|
|
|
+
|
|
|
|
|
+ /* Open input file */
|
|
|
|
|
+ input = fopen(input_file, "r");
|
|
|
|
|
+ if (!input) {
|
|
|
|
|
+ fprintf(stderr, "Error: Cannot open input file '%s'\n", input_file);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Open debug output file */
|
|
|
|
|
+ debug_output = fopen(debug_file, "w");
|
|
|
|
|
+ if (!debug_output) {
|
|
|
|
|
+ fprintf(stderr, "Error: Cannot open debug file '%s'\n", debug_file);
|
|
|
|
|
+ fclose(input);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Configure filters */
|
|
|
|
|
+ config_flags = filters_enabled ? 0 : CONFFLAG_DISABLE_FILTERS;
|
|
|
|
|
+
|
|
|
|
|
+ /* Reset system state */
|
|
|
|
|
+ reset_system();
|
|
|
|
|
+
|
|
|
|
|
+ /* Open GPX output file */
|
|
|
|
|
+ if (f_open(&gpx_file, output_gpx, FA_WRITE | FA_OPEN_ALWAYS) != 0) {
|
|
|
|
|
+ fprintf(stderr, "Error: Cannot open GPX file '%s'\n", output_gpx);
|
|
|
|
|
+ fclose(input);
|
|
|
|
|
+ fclose(debug_output);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Initialize GPX file */
|
|
|
|
|
+ if (gpx_init(&gpx_file) != 0) {
|
|
|
|
|
+ fprintf(stderr, "Error: GPX initialization failed\n");
|
|
|
|
|
+ fclose(input);
|
|
|
|
|
+ fclose(debug_output);
|
|
|
|
|
+ f_close(&gpx_file);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fprintf(debug_output, "=== GPS Test Tool - %s ===\n",
|
|
|
|
|
+ filters_enabled ? "FILTERS ENABLED" : "FILTERS DISABLED");
|
|
|
|
|
+ fprintf(debug_output, "Input: %s\n", input_file);
|
|
|
|
|
+ fprintf(debug_output, "Output: %s\n\n", output_gpx);
|
|
|
|
|
+
|
|
|
|
|
+ /* Process NMEA file line by line */
|
|
|
|
|
+ while (fgets(line, sizeof(line), input)) {
|
|
|
|
|
+ size_t len = strlen(line);
|
|
|
|
|
+
|
|
|
|
|
+ /* Skip empty lines */
|
|
|
|
|
+ if (len == 0) continue;
|
|
|
|
|
+
|
|
|
|
|
+ /* Ensure line ends with \r\n as expected by gps_parse */
|
|
|
|
|
+ if (len > 0 && line[len-1] == '\n') {
|
|
|
|
|
+ line[len-1] = '\0';
|
|
|
|
|
+ len--;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (len > 0 && line[len-1] == '\r') {
|
|
|
|
|
+ line[len-1] = '\0';
|
|
|
|
|
+ len--;
|
|
|
|
|
+ }
|
|
|
|
|
+ /* Add back \r\n for gps_parse */
|
|
|
|
|
+ strcat(line, "\r\n");
|
|
|
|
|
+
|
|
|
|
|
+ /* Parse NMEA sentence */
|
|
|
|
|
+ if (gps_parse(line) != 0) {
|
|
|
|
|
+ /* Valid location data received (RMC parsed) */
|
|
|
|
|
+ /* Only process if we have valid coordinates from GGA */
|
|
|
|
|
+ if (System.location_valid == LOC_VALID_NEW || System.location_valid == LOC_VALID) {
|
|
|
|
|
+ gpx_process_point(&location, &gpx_file);
|
|
|
|
|
+ point_count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Close GPX file */
|
|
|
|
|
+ gpx_close(&gpx_file);
|
|
|
|
|
+
|
|
|
|
|
+ /* Print summary */
|
|
|
|
|
+ fprintf(debug_output, "\n=== Summary ===\n");
|
|
|
|
|
+ fprintf(debug_output, "Points processed: %d\n", point_count);
|
|
|
|
|
+ fprintf(debug_output, "Total distance: %.2f km\n", System.distance / 100000.0);
|
|
|
|
|
+ fprintf(debug_output, "Elevation gain: %.1f m\n", System.elevation_gain / 10.0);
|
|
|
|
|
+ fprintf(debug_output, "Elevation loss: %.1f m\n", System.elevation_loss / 10.0);
|
|
|
|
|
+
|
|
|
|
|
+ /* Close files */
|
|
|
|
|
+ fclose(input);
|
|
|
|
|
+ fclose(debug_output);
|
|
|
|
|
+ debug_output = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ printf("Processed %d points -> %s (filters %s)\n",
|
|
|
|
|
+ point_count, output_gpx, filters_enabled ? "ON" : "OFF");
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int main(int argc, char *argv[]) {
|
|
|
|
|
+ int ret;
|
|
|
|
|
+
|
|
|
|
|
+ printf("GPS Test Tool v1.0\n");
|
|
|
|
|
+ printf("==================\n\n");
|
|
|
|
|
+
|
|
|
|
|
+ if (argc != 2) {
|
|
|
|
|
+ fprintf(stderr, "Usage: %s <input.nmea>\n", argv[0]);
|
|
|
|
|
+ fprintf(stderr, "\nGenerates:\n");
|
|
|
|
|
+ fprintf(stderr, " - output_filtered.gpx\n");
|
|
|
|
|
+ fprintf(stderr, " - output_unfiltered.gpx\n");
|
|
|
|
|
+ fprintf(stderr, " - debug_filtered.txt\n");
|
|
|
|
|
+ fprintf(stderr, " - debug_unfiltered.txt\n");
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Process with filters enabled */
|
|
|
|
|
+ printf("Processing with filters ENABLED...\n");
|
|
|
|
|
+ ret = process_nmea_file(argv[1], "output_filtered.gpx",
|
|
|
|
|
+ "debug_filtered.txt", 1);
|
|
|
|
|
+ if (ret != 0) {
|
|
|
|
|
+ fprintf(stderr, "Failed to process with filters enabled\n");
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* Process with filters disabled */
|
|
|
|
|
+ printf("Processing with filters DISABLED...\n");
|
|
|
|
|
+ ret = process_nmea_file(argv[1], "output_unfiltered.gpx",
|
|
|
|
|
+ "debug_unfiltered.txt", 0);
|
|
|
|
|
+ if (ret != 0) {
|
|
|
|
|
+ fprintf(stderr, "Failed to process with filters disabled\n");
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ printf("\nProcessing complete!\n");
|
|
|
|
|
+ printf("Generated files:\n");
|
|
|
|
|
+ printf(" - output_filtered.gpx (filters ON)\n");
|
|
|
|
|
+ printf(" - output_unfiltered.gpx (filters OFF)\n");
|
|
|
|
|
+ printf(" - debug_filtered.txt (debug output with filters)\n");
|
|
|
|
|
+ printf(" - debug_unfiltered.txt (debug output without filters)\n");
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|