/* * GPS Test Tool - PC Application * * Tests GPS filtering and GPX generation using actual embedded code * Usage: ./gps-test-tool * * 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; unsigned char FLAGS = 0; volatile unsigned char reset_cause = 0; /* no real MCU reset happened; log_boot_record() reports "UNKNOWN" */ /* File handle for debug output redirection */ FILE *debug_output = NULL; /* soft/syslog.c's log_config()/log_boot_record() print these; not part of * this PC build otherwise (they're normally in soft/settings.c). */ __flash const char gnss_name_stub[] = "GPS+GLONASS+GALILEO/etc (PC test tool - not tracked per mode)"; __flash const char *gnss_names[] = { gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub, gnss_name_stub, }; __flash const char uart_log_mode_stub[] = "n/a (PC test tool)"; __flash const char *uart_log_mode_names[] = { uart_log_mode_stub, uart_log_mode_stub, uart_log_mode_stub, }; /* 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 */ /* Match soft/settings.c's defaults_u8, so this tool's auto-pause behavior * (enabled below) matches an out-of-the-box device. */ System.conf.auto_pause_time = 30; System.conf.auto_pause_dist = 10; System.conf.auto_pause_speed = 3; System.conf.min_sats = 4; /* Require at least 4 satellites */ System.tracking_paused = 0; System.tracking_auto_paused = 0; System.bat_volt = 4.0f; /* no real ADC here; keeps the status line's v=/bat%= fields sane */ System.bat_volt_min = 4.0f; 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) | CONFFLAG_AUTO_PAUSE; /* Reset system state */ reset_system(); gps_powered_on(); log_boot_record(); /* 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 */ /* Kept in sync with soft/main.c's FIX_HOLD_EPOCHS. */ #define FIX_HOLD_EPOCHS 5 unsigned char fix_ok_prev = 0; unsigned char fix_hold_counter = 0; unsigned char sbas_prev = 0xFF; /* 0xFF: not yet known, suppress the first (non-)transition */ time_t next_status_utc = 0; time_t next_summary_utc = 0; 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); auto_pause_process(); point_count++; } } /* Mirror soft/main.c's system-log event/status triggers, so this * tool exercises and lets you review the same log output the * firmware produces. There's no real clock here, so pace the * periodic status line/session summary off the NMEA timestamps * instead of uptime. */ if (utc) { if (nmea_epoch_complete()) { unsigned char fix_ok_now = (FLAGS & F_GPSOK) && !System.sat_count_low; if (fix_ok_now == fix_ok_prev) { fix_hold_counter = 0; } else if (++fix_hold_counter >= FIX_HOLD_EPOCHS) { fix_hold_counter = 0; fix_ok_prev = fix_ok_now; if (fix_ok_now) log_fix_event(); else log_fix_lost(); } } if (System.sbas != sbas_prev) { if (sbas_prev != 0xFF) log_sbas_transition(); sbas_prev = System.sbas; } if (!next_status_utc) next_status_utc = utc + 60; if (utc >= next_status_utc) { log_status_line(); next_status_utc = utc + 60; } if (!next_summary_utc) next_summary_utc = utc + 3600; if (utc >= next_summary_utc) { log_session_summary(0); next_summary_utc = utc + 3600; } } } /* Close GPX file */ gpx_close(&gpx_file); log_session_summary(1); /* 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 \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; }