1+ #include " igc.h"
2+
3+ #include < SD_MMC.h>
4+
5+ #include " Arduino.h"
6+ #include " ArduinoJson.h"
7+ #include " FS.h"
8+ #include " baro.h"
9+ #include " gps.h"
10+ #include " settings.h"
11+ #include " string_utils.h"
12+ #include " version.h"
13+ #include " time.h"
14+
15+ String latDegreeToStr (double degree) {
16+ char output[9 ]; // 8 bytes + null terminator
17+ char hemisphere = (degree >= 0 ) ? ' N' : ' S' ;
18+ degree = abs (degree);
19+
20+ int degrees = (int )degree;
21+ double minutes = (degree - degrees) * 60 ;
22+ int intMinutes = (int )minutes;
23+ int fractionalMinutes = (int )((minutes - intMinutes) * 1000 );
24+
25+ snprintf (output, 9 , " %02d%02d%03d%c" , degrees, intMinutes, fractionalMinutes, hemisphere);
26+ return String (output);
27+ }
28+
29+ String lngDegreeToStr (double degree) {
30+ char output[10 ]; // 9 bytes + null terminator
31+ char hemisphere = (degree >= 0 ) ? ' E' : ' W' ;
32+ degree = abs (degree);
33+
34+ int degrees = (int )degree;
35+ double minutes = (degree - degrees) * 60 ;
36+ int intMinutes = (int )minutes;
37+ int fractionalMinutes = (int )((minutes - intMinutes) * 1000 );
38+
39+ snprintf (output, 10 , " %03d%02d%03d%c" , degrees, intMinutes, fractionalMinutes, hemisphere);
40+ return String (output);
41+ }
42+
43+ const String Igc::desiredFileName () const {
44+ // Name of the file should be for example 2024-12-10-XFH-000-01.IGC
45+ // as per the IGC spec.
46+ char buf[11 ];
47+ tm cal;
48+ gps_getLocalDateTime (cal);
49+ strftime (buf, 11 , " %F" , &cal);
50+
51+ String ret = buf;
52+ ret += (String) " -" + IGC_MANUFACTURER_CODE + " -000" ;
53+ return ret;
54+ }
55+
56+ void Igc::log (unsigned long durationSec) {
57+ // Generate the time in HHMMSS
58+ char buf[8 ];
59+ tm cal;
60+ gps_getUtcDateTime (cal);
61+ strftime (buf, sizeof (buf), " %H%M%S" , &cal);
62+
63+ logger.writeBRecord (buf, // Time in HHMMSS
64+ latDegreeToStr (gps.location .lat ()),
65+ lngDegreeToStr (gps.location .lng ()),
66+ true ,
67+ baro.alt / 100 , // cm to meters
68+ gps.altitude .meters (),
69+ toDigits ((int )gpsAccuracy.error , 3 ));
70+ }
71+
72+ void Igc::startFlight () {
73+ Flight::startFlight ();
74+
75+ logger.setOutput (file);
76+
77+ // Log the Header
78+ // A record to look like "AXLFLeaf1"
79+ logger.setManufacturerId (IGC_MANUFACTURER_CODE );
80+ logger.setLoggerId (" Lea" );
81+ logger.setIdExtension (" f1" );
82+
83+ logger.pilot = " Unknown" ;
84+ logger.glider_type = " Unknown" ;
85+ // Overwrite from file if set in the Pilot descriptor
86+ setPilotFromFile ();
87+
88+ logger.firmware_version = VERSION ;
89+ logger.hardware_version = " Leaf1" ;
90+ logger.logger_type = (String) " Leaf1," + VERSION ;
91+ logger.gps_type = " GNSS LC86G" ;
92+ logger.pressure_type = " MS5611" ;
93+ logger.time_zone = (String)(TIME_ZONE / 60 );
94+
95+ tm cal;
96+ gps_getUtcDateTime (cal);
97+ strftime (logger.date , sizeof (logger.date ), " %d%m%y" , &cal);
98+
99+ logger.writeHeader ();
100+
101+ // Log the I record (saying we're going to log the, now manditory, FXA record)
102+ const IRecordExtension extensions[] = {IRecordExtension (3 , " FXA" )};
103+ logger.writeIRecord (sizeof (extensions) / sizeof (extensions[0 ]), extensions);
104+ }
105+
106+ void Igc::end (const FlightStats stats) {
107+ logger.writeGRecord ();
108+ Flight::end (stats);
109+ }
110+
111+ void Igc::setPilotFromFile () {
112+ auto pilotFile = SD_MMC .open (" /pilot.json" , " r" );
113+ if (!pilotFile) {
114+ return ; // No pilot JSON file
115+ }
116+ JsonDocument doc;
117+ deserializeJson (doc, pilotFile);
118+ logger.pilot = (String)doc[" pilot" ];
119+ logger.glider_type = (String)doc[" glider_type" ];
120+ }
0 commit comments