From fa1a02371c34ffd21058bb44e5d236e91dbe1346 Mon Sep 17 00:00:00 2001 From: xamidev Date: Fri, 7 Nov 2025 21:07:16 +0100 Subject: [PATCH] Header parsing --- README.md | 5 ++++ a.out | Bin 16344 -> 16624 bytes igc.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++----- igc.h | 33 ++++++++++++++++---------- 4 files changed, 88 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f21fb5e..c3f105b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ Work in progress. Goals are: parsing an IGC file containing flight data, giving analytics about the flight, and if I really have time to kill, making some kind of (3D??) view of the flight.. +TODO: +- header parsing +- data point doubly linked list +- Raylib 3d 3rd person flight line visualization???? + # License GNU GPL v3 diff --git a/a.out b/a.out index 73c46c56734ba83159884fedf775bcc272813513..3a4bce600c45c8a796589f525f4ac5b0936e057a 100755 GIT binary patch literal 16624 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 delta 2565 zcmZWreNa?Y6u8x6OTtvP;_YHqhGeu2CXwP99Qg9T|k`rImpt3Zl4@9{j#d!RdPvW<a8M5t<9J|0!=IxD8IKhqVSPd|`-;iub5Ob`K4(*Dx=*BXwi=PV<*E7cQE|&5zR) zBFElx5OTN7VM_W)QJa!HL<+cS)g5ZY8|VSw)MFn_&wx8!T5 zrXNU|-+PIhI@P?uO;I&_xM@($vr@GhY*g)T#T}4FNC9tG8@n5l|D5DMJrKbPV$y(Q zLf?s}eSbgVK3{vZn&vo{#-OzAX=#Th4M;ZhwYG`XPsaHAi8B$MEp6fxUp?~$aB^77 zVMVu~&o3ZzfiZhEX1X4!9!6~sQQ=6PWz>ccWk#x-Q7a!+v)VD&bXIE@>y|jF}K?W{8z?{BL4=YFttm3F+uXnIvNGucr6Y$(5X=sIm+%OV9{ z9mFcorF6kDWh`+>t~${G>9%xJ&eN5QeGuGWTS1pK-NPJ#xkz3LuW9_J@PRw1$oQPrqwwfo_>f$;U|~Zn_A6XXWy0MZ!Td3u`k*I#p?;@5 zHf&|?c>ITCdpa~HPst;6j4m%5r^vRA{kLS9J>BC5Ww{wK9dSP5*xRz)iKxFT%Qp~L zAx2Vgq@)Oaw_t9HLyX7B8mLQ2eX|0uw9N~pWE}q37FdkAuCblJaHRQT;iags*`MeF z`W>P#O!u=8X$Vh17aQArN0yf(hIB$HdI@3v{rD@s6I_#87J>8Wqc&~V)OJb!q9JqP z&li$}t8ozFSV{?$JCbMz_#Fkb3A!CgmVWhZLHJ9H*x{a|K^knFNj336Oc+5qLQH-x5SIOCcjY_UW1)Dozi z7BjOHtF;pw$@XZ`D&`_@MqV1m7a|`mn90?+XZ5c@@l>m=ghvg}wi?z=v%1!!*CN=6 z_8QSL_*hPy#~wZpJd=wq)9z1TaQRrjkYyz5YwWXqm&2D%GC)`fAiZjuh0^bUw z=*nPnMh1lsGV|e=%qX~@xhuFo>kJK6Lvfxn zqMx(aINS3wjdtEucIeK_3iX)QYfWIuuckH7nC~?1C1C5?6;eIBZ@jUk*j*-^i diff --git a/igc.c b/igc.c index f4e7f77..56b9177 100644 --- a/igc.c +++ b/igc.c @@ -49,18 +49,62 @@ struct IGC_DataPoint* parse_datapoint(char* line) dp->lon.cardinal = line[23] == EAST ? EAST : WEST; dp->baro_alt = atoi(baro_alt); dp->gps_alt = atoi(gps_alt); - - printf("Data point: %d:%d:%d | %d°%d'%d\"%c %d°%d'%d\"%c | Baro: %dm GPS: %dm\n", dp->hour, dp->minute, dp->second, dp->lat.deg, dp->lat.min, dp->lat.sec, dp->lat.cardinal, dp->lon.deg, dp->lon.min, dp->lon.sec, dp->lon.cardinal, dp->baro_alt, dp->gps_alt); return dp; } -void parse_header_record(char* line) +void show_datapoint(struct IGC_DataPoint* dp) +{ + printf("Data point: %02d:%02d:%02d | %d°%d'%d\"%c %d°%d'%d\"%c | Baro: %dm GPS: %dm\n", dp->hour, dp->minute, dp->second, dp->lat.deg, dp->lat.min, dp->lat.sec, dp->lat.cardinal, dp->lon.deg, dp->lon.min, dp->lon.sec, dp->lon.cardinal, dp->baro_alt, dp->gps_alt); +} + +void show_header_info(struct IGC_Header* hdr) +{ + printf("*** Flight information:\nDate: %02d/%02d/%02d\nPilot: %sCopilot: %sAircraft: %sRegistration: %s", hdr->day, hdr->month, hdr->year, hdr->pilot_name, hdr->crew2_name, hdr->aircraft_name, hdr->aircraft_registration_no); +} + +struct IGC_Header* parse_header_record(struct IGC_Header* hdr, char* line) { if (strncmp(line, "HFDTEDATE", 9) == 0) { - + strtok(line, ":"); + char* date_str = strtok(NULL, ":"); + char day[3], month[3], year[3] = {0}; + + memcpy(day, date_str, 2); + memcpy(month, date_str+2, 2); + memcpy(year, date_str+4, 2); + + hdr->day = atoi(day); + hdr->month = atoi(month); + hdr->year = atoi(year); } + if (strncmp(line, "HFPLTPILOTINCHARGE", 18) == 0) + { + strtok(line, ":"); + strncpy(hdr->pilot_name, strtok(NULL, ":"), SMALL_NAME_MAX); + } + if (strncmp(line, "HFCM2CREW2", 10) == 0) + { + strtok(line, ":"); + strncpy(hdr->crew2_name, strtok(NULL, ":"), SMALL_NAME_MAX); + } + if (strncmp(line, "HFGTYGLIDERTYPE", 15) == 0) + { + strtok(line, ":"); + strncpy(hdr->aircraft_name, strtok(NULL, ":"), SMALL_NAME_MAX); + } + if (strncmp(line, "HFGIDGLIDERID", 13) == 0) + { + strtok(line, ":"); + strncpy(hdr->aircraft_registration_no, strtok(NULL, ":"), SMALL_NAME_MAX); + } + if (strncmp(line, "HFDTMGPSDATUM", 13) == 0) + { + strtok(line, ":"); + strncpy(hdr->gps_datum, strtok(NULL, ":"), SMALL_NAME_MAX); + } + return hdr; } void parse_igc_file(FILE* fp) @@ -68,23 +112,36 @@ void parse_igc_file(FILE* fp) size_t len = 0; char* line = NULL; + struct IGC_Header* hdr = (struct IGC_Header*)calloc(1, sizeof(struct IGC_Header)); + while (getline(&line, &len, fp) != -1) { switch(line[0]) { + case MANUFACTURER: + printf("Manufacturer info: %s", line); + break; + case COMMENT: + printf("Comment: %s", line); + break; case DATAPOINT: struct IGC_DataPoint* dp = parse_datapoint(line); + // show_datapoint(dp); // Doubly linked list of points (so theyre joined in chronological order; opens possibilities for analysis later) // append_datapoint(dp); break; case HEADER: - parse_header_record(line); + hdr = parse_header_record(hdr, line); break; + case SEC_KEY: + printf("Security key found: %s", line); + break; default: - printf("Unrecognized record type '%c'\n", line[0]); + printf("Unhandled record type '%c'\n", line[0]); break; } } + show_header_info(hdr); } int main(int argc, char* argv[]) diff --git a/igc.h b/igc.h index 87759b6..9122ac2 100644 --- a/igc.h +++ b/igc.h @@ -9,11 +9,10 @@ #ifndef IGC_H #define IGC_H +#define SMALL_NAME_MAX 32 #define NAME_MAX 256 #define TMP_SIZE 8 -void parse_igc_file(FILE* fp); - enum IGC_RecordType { MANUFACTURER = 'A', @@ -27,7 +26,7 @@ enum IGC_RecordType PILOT_EVENT = 'E', GEN_PURPOSE_PLACEHOLDER = 'K', COMMENT = 'L', - SEC_KEY = 'G' + SEC_KEY = 'G', }; enum Cardinals @@ -53,16 +52,18 @@ struct IGC_Header int year; // Not exhaustive - char pilot_name[NAME_MAX] = {0}; - char crew2_name[NAME_MAX] = {0}; - char aircraft_name[NAME_MAX] = {0}; - char aircraft_registration_no[NAME_MAX] = {0}; - char gps_datum[NAME_MAX] = {0}; - char firmware_ver[NAME_MAX] = {0}; - char hardware_ver[NAME_MAX] = {0}; - char frtype[NAME_MAX] = {0}; - char press_alt_sensor[NAME_MAX] = {0}; -} + char pilot_name[NAME_MAX]; + char crew2_name[NAME_MAX]; + char aircraft_name[NAME_MAX]; + char aircraft_registration_no[NAME_MAX]; + char gps_datum[NAME_MAX]; + + // (not too important; ignored for now, may be part of "verbose" setting later) + char firmware_ver[NAME_MAX]; + char hardware_ver[NAME_MAX]; + char frtype[NAME_MAX]; + char press_alt_sensor[NAME_MAX]; +}; struct IGC_DataPoint { @@ -80,4 +81,10 @@ struct IGC_DataPoint int gps_alt; }; +void parse_igc_file(FILE* fp); +struct IGC_DataPoint* parse_datapoint(char* line); +void show_datapoint(struct IGC_DataPoint* dp); +void show_header_info(struct IGC_Header* hdr); +struct IGC_Header* parse_header_record(struct IGC_Header* hdr, char* line); + #endif