main.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * GPS Test Tool - PC Application
  3. *
  4. * Tests GPS filtering and GPX generation using actual embedded code
  5. * Usage: ./gps-test-tool <input.nmea>
  6. *
  7. * Generates:
  8. * - output_filtered.gpx
  9. * - output_unfiltered.gpx
  10. * - debug_filtered.txt
  11. * - debug_unfiltered.txt
  12. */
  13. #define PC_BUILD
  14. #include "main.h"
  15. /* Global variables expected by project code */
  16. volatile struct system_s System;
  17. struct location_s location;
  18. time_t utc;
  19. unsigned char config_flags = 0;
  20. unsigned char FLAGS = 0;
  21. volatile unsigned char reset_cause = 0; /* no real MCU reset happened; log_boot_record() reports "UNKNOWN" */
  22. /* File handle for debug output redirection */
  23. FILE *debug_output = NULL;
  24. /* soft/syslog.c's log_config()/log_boot_record() print these; not part of
  25. * this PC build otherwise (they're normally in soft/settings.c). */
  26. __flash const char gnss_name_stub[] = "GPS+GLONASS+GALILEO/etc (PC test tool - not tracked per mode)";
  27. __flash const char *gnss_names[] = {
  28. gnss_name_stub, gnss_name_stub, gnss_name_stub,
  29. gnss_name_stub, gnss_name_stub, gnss_name_stub,
  30. };
  31. __flash const char uart_log_mode_stub[] = "n/a (PC test tool)";
  32. __flash const char *uart_log_mode_names[] = {
  33. uart_log_mode_stub, uart_log_mode_stub, uart_log_mode_stub,
  34. };
  35. /* Function prototypes from project files */
  36. unsigned char gpx_init(FIL *file);
  37. unsigned char gpx_close(FIL *file);
  38. void gpx_process_point(struct location_s *loc, FIL *file);
  39. time_t gps_parse(const char *nmea);
  40. /* Time function implementation */
  41. char *get_iso_time(time_t time, unsigned char local) {
  42. static char output[32];
  43. struct tm *ct = gmtime(&time);
  44. sprintf(output, "%04d-%02d-%02dT%02d:%02d:%02d.000Z",
  45. ct->tm_year + 1900, ct->tm_mon + 1, ct->tm_mday,
  46. ct->tm_hour, ct->tm_min, ct->tm_sec);
  47. return output;
  48. }
  49. void iso_time_to_filename(char *time) {
  50. while (*time) {
  51. switch (*time) {
  52. case ':': *time = '-'; break;
  53. case '+': *time = 'p'; break;
  54. }
  55. time++;
  56. }
  57. }
  58. /* Reset system state */
  59. void reset_system(void) {
  60. memset((void*)&System, 0, sizeof(System));
  61. memset(&location, 0, sizeof(location));
  62. System.conf.skip_points = 0; /* Don't skip points for testing */
  63. /* Match soft/settings.c's defaults_u8, so this tool's auto-pause behavior
  64. * (enabled below) matches an out-of-the-box device. */
  65. System.conf.auto_pause_time = 30;
  66. System.conf.auto_pause_dist = 10;
  67. System.conf.auto_pause_speed = 3;
  68. System.conf.min_sats = 4; /* Require at least 4 satellites */
  69. System.tracking_paused = 0;
  70. System.tracking_auto_paused = 0;
  71. System.bat_volt = 4.0f; /* no real ADC here; keeps the status line's v=/bat%= fields sane */
  72. System.bat_volt_min = 4.0f;
  73. utc = 0;
  74. }
  75. /* Process NMEA file with specified filter setting */
  76. int process_nmea_file(const char *input_file, const char *output_gpx,
  77. const char *debug_file, int filters_enabled) {
  78. FILE *input;
  79. FIL gpx_file;
  80. char line[256];
  81. int point_count = 0;
  82. /* Open input file */
  83. input = fopen(input_file, "r");
  84. if (!input) {
  85. fprintf(stderr, "Error: Cannot open input file '%s'\n", input_file);
  86. return 1;
  87. }
  88. /* Open debug output file */
  89. debug_output = fopen(debug_file, "w");
  90. if (!debug_output) {
  91. fprintf(stderr, "Error: Cannot open debug file '%s'\n", debug_file);
  92. fclose(input);
  93. return 1;
  94. }
  95. /* Configure filters */
  96. config_flags = (filters_enabled ? 0 : CONFFLAG_DISABLE_FILTERS) | CONFFLAG_AUTO_PAUSE;
  97. /* Reset system state */
  98. reset_system();
  99. gps_powered_on();
  100. log_boot_record();
  101. /* Open GPX output file */
  102. if (f_open(&gpx_file, output_gpx, FA_WRITE | FA_OPEN_ALWAYS) != 0) {
  103. fprintf(stderr, "Error: Cannot open GPX file '%s'\n", output_gpx);
  104. fclose(input);
  105. fclose(debug_output);
  106. return 1;
  107. }
  108. /* Initialize GPX file */
  109. if (gpx_init(&gpx_file) != 0) {
  110. fprintf(stderr, "Error: GPX initialization failed\n");
  111. fclose(input);
  112. fclose(debug_output);
  113. f_close(&gpx_file);
  114. return 1;
  115. }
  116. fprintf(debug_output, "=== GPS Test Tool - %s ===\n",
  117. filters_enabled ? "FILTERS ENABLED" : "FILTERS DISABLED");
  118. fprintf(debug_output, "Input: %s\n", input_file);
  119. fprintf(debug_output, "Output: %s\n\n", output_gpx);
  120. /* Process NMEA file line by line */
  121. /* Kept in sync with soft/main.c's FIX_HOLD_EPOCHS. */
  122. #define FIX_HOLD_EPOCHS 5
  123. unsigned char fix_ok_prev = 0;
  124. unsigned char fix_hold_counter = 0;
  125. unsigned char sbas_prev = 0xFF; /* 0xFF: not yet known, suppress the first (non-)transition */
  126. time_t next_status_utc = 0;
  127. time_t next_summary_utc = 0;
  128. while (fgets(line, sizeof(line), input)) {
  129. size_t len = strlen(line);
  130. /* Skip empty lines */
  131. if (len == 0) continue;
  132. /* Ensure line ends with \r\n as expected by gps_parse */
  133. if (len > 0 && line[len-1] == '\n') {
  134. line[len-1] = '\0';
  135. len--;
  136. }
  137. if (len > 0 && line[len-1] == '\r') {
  138. line[len-1] = '\0';
  139. len--;
  140. }
  141. /* Add back \r\n for gps_parse */
  142. strcat(line, "\r\n");
  143. /* Parse NMEA sentence */
  144. if (gps_parse(line) != 0) {
  145. /* Valid location data received (RMC parsed) */
  146. /* Only process if we have valid coordinates from GGA */
  147. if (System.location_valid == LOC_VALID_NEW || System.location_valid == LOC_VALID) {
  148. gpx_process_point(&location, &gpx_file);
  149. auto_pause_process();
  150. point_count++;
  151. }
  152. }
  153. /* Mirror soft/main.c's system-log event/status triggers, so this
  154. * tool exercises and lets you review the same log output the
  155. * firmware produces. There's no real clock here, so pace the
  156. * periodic status line/session summary off the NMEA timestamps
  157. * instead of uptime. */
  158. if (utc) {
  159. if (nmea_epoch_complete()) {
  160. unsigned char fix_ok_now = (FLAGS & F_GPSOK) && !System.sat_count_low;
  161. if (fix_ok_now == fix_ok_prev) {
  162. fix_hold_counter = 0;
  163. } else if (++fix_hold_counter >= FIX_HOLD_EPOCHS) {
  164. fix_hold_counter = 0;
  165. fix_ok_prev = fix_ok_now;
  166. if (fix_ok_now)
  167. log_fix_event();
  168. else
  169. log_fix_lost();
  170. }
  171. }
  172. if (System.sbas != sbas_prev) {
  173. if (sbas_prev != 0xFF)
  174. log_sbas_transition();
  175. sbas_prev = System.sbas;
  176. }
  177. if (!next_status_utc)
  178. next_status_utc = utc + 60;
  179. if (utc >= next_status_utc) {
  180. log_status_line();
  181. next_status_utc = utc + 60;
  182. }
  183. if (!next_summary_utc)
  184. next_summary_utc = utc + 3600;
  185. if (utc >= next_summary_utc) {
  186. log_session_summary(0);
  187. next_summary_utc = utc + 3600;
  188. }
  189. }
  190. }
  191. /* Close GPX file */
  192. gpx_close(&gpx_file);
  193. log_session_summary(1);
  194. /* Print summary */
  195. {
  196. unsigned int moving = get_logging_time();
  197. unsigned long int stopped = get_pause_time();
  198. fprintf(debug_output, "\n=== Summary ===\n");
  199. fprintf(debug_output, "Points processed: %d\n", point_count);
  200. fprintf(debug_output, "Total distance: %.2f km\n", System.distance / 100000.0);
  201. fprintf(debug_output, "Elevation gain: %.1f m\n", System.elevation_gain / 10.0);
  202. fprintf(debug_output, "Elevation loss: %.1f m\n", System.elevation_loss / 10.0);
  203. fprintf(debug_output, "Moving time: %uh%02um\n", moving/3600, (moving/60)%60);
  204. fprintf(debug_output, "Stopped time: %luh%02lum\n", stopped/3600, (stopped/60)%60);
  205. fprintf(debug_output, "Average speed: %.2f km/h\n", get_avg_speed_x100()/100.0);
  206. fprintf(debug_output, "Average speed (dist/time): %.2f km/h\n", get_dist_avg_speed_x100()/100.0);
  207. }
  208. /* Close files */
  209. fclose(input);
  210. fclose(debug_output);
  211. debug_output = NULL;
  212. printf("Processed %d points -> %s (filters %s)\n",
  213. point_count, output_gpx, filters_enabled ? "ON" : "OFF");
  214. return 0;
  215. }
  216. int main(int argc, char *argv[]) {
  217. int ret;
  218. printf("GPS Test Tool v1.0\n");
  219. printf("==================\n\n");
  220. if (argc != 2) {
  221. fprintf(stderr, "Usage: %s <input.nmea>\n", argv[0]);
  222. fprintf(stderr, "\nGenerates:\n");
  223. fprintf(stderr, " - output_filtered.gpx\n");
  224. fprintf(stderr, " - output_unfiltered.gpx\n");
  225. fprintf(stderr, " - debug_filtered.txt\n");
  226. fprintf(stderr, " - debug_unfiltered.txt\n");
  227. return 1;
  228. }
  229. /* Process with filters enabled */
  230. printf("Processing with filters ENABLED...\n");
  231. ret = process_nmea_file(argv[1], "output_filtered.gpx",
  232. "debug_filtered.txt", 1);
  233. if (ret != 0) {
  234. fprintf(stderr, "Failed to process with filters enabled\n");
  235. return ret;
  236. }
  237. /* Process with filters disabled */
  238. printf("Processing with filters DISABLED...\n");
  239. ret = process_nmea_file(argv[1], "output_unfiltered.gpx",
  240. "debug_unfiltered.txt", 0);
  241. if (ret != 0) {
  242. fprintf(stderr, "Failed to process with filters disabled\n");
  243. return ret;
  244. }
  245. printf("\nProcessing complete!\n");
  246. printf("Generated files:\n");
  247. printf(" - output_filtered.gpx (filters ON)\n");
  248. printf(" - output_unfiltered.gpx (filters OFF)\n");
  249. printf(" - debug_filtered.txt (debug output with filters)\n");
  250. printf(" - debug_unfiltered.txt (debug output without filters)\n");
  251. return 0;
  252. }