Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract engine noise from IGC, add to KML multipath #1054

Merged
merged 22 commits into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
9ab41be
Extract engine noise from IGC, add to KML multipath
PacketFiend Mar 27, 2023
98f428d
Add IGC tests and test files
PacketFiend Mar 28, 2023
7d97cd7
Fix kml test suite for extension data.
robertlipe Mar 28, 2023
8eaa447
Better way to figure out which extensions are present?
PacketFiend Mar 29, 2023
c6b63c3
New IgcMetaData and ExtensionDefinition classes
PacketFiend Mar 29, 2023
3f1e059
Lots of new extensions
PacketFiend Mar 30, 2023
a32aed0
This looks like a better way...
PacketFiend Mar 31, 2023
c6731b2
Minor cleanup
PacketFiend Apr 1, 2023
222c5aa
Dead code removal
PacketFiend Mar 31, 2023
201907d
Update IGC->KML reference KML files
PacketFiend Apr 1, 2023
c6749c5
Replace QHash with QList, eliminate evil if ladders
PacketFiend Apr 1, 2023
c1a883e
Many small improvements and cleanups
PacketFiend Apr 4, 2023
77844dc
Many small improvements and cleanups
PacketFiend Apr 4, 2023
f1f0c53
Merge branch 'develop' of github.com:PacketFiend/gpsbabel
PacketFiend Apr 4, 2023
44043db
Precision loss fixes, igc_fsdata now templated
PacketFiend Apr 4, 2023
1eb45f8
Merge branch 'master' into develop
PacketFiend Apr 4, 2023
4f16fb4
Merge remote-tracking branch 'gpsbabel/master' into develop
PacketFiend Apr 5, 2023
1af2221
More precision fixes, less templating
PacketFiend Apr 5, 2023
d525791
format with astylerc options.
tsteven4 Apr 5, 2023
de92e9a
delete unused include
tsteven4 Apr 5, 2023
d3e67fc
convert KmlFormat::wp_field to scoped enum.
tsteven4 Apr 6, 2023
e740fee
add missed file with scoped enum refatoring changes.
tsteven4 Apr 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/autom4te.cache/
/babelweb/
/bld/
/build/
/compile_commands.json
/config.h
/config.log
Expand Down Expand Up @@ -43,3 +45,4 @@ CMakeFiles/
/empty
/gbversion.h
/qrc_gpsbabel.cpp
/.vscode/
3 changes: 2 additions & 1 deletion formspec.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ enum FsType {
kFsOzi = 0x6f7a6900L,
kFsGmsd = 0x474d5344L, /* GMSD = Garmin specific data */
kFsQstarzBl1000 = 0x5173747aL,
kFsLowranceusr4 = 0x615f234cL
kFsLowranceusr4 = 0x615f234cL,
kFsIGC = 69676308L, /* IGC format, amendment 8 (2023-02-08) */
};

struct FormatSpecificData {
Expand Down
120 changes: 108 additions & 12 deletions igc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <cstring> // for strcmp, strlen, strtok, strcat, strchr, strcpy, strncat
#include <iterator> // for reverse_iterator, operator==, prev, next
#include <optional> // for optional
#include <tuple> // for std::make_tuple

#include <QByteArray> // for QByteArray
#include <QChar> // for QChar
Expand All @@ -44,11 +45,14 @@
#include <QTime> // for operator<, operator==, QTime
#include <Qt> // for UTC, SkipEmptyParts
#include <QtGlobal> // for foreach, qPrintable
#include <QDebug> // DELETEME for debugging

#include "defs.h"
#include "gbfile.h" // for gbfprintf, gbfclose, gbfopen, gbfputs, gbfgetstr, gbfile
#include "grtcirc.h" // for RAD, gcdist, radtometers
#include "src/core/datetime.h" // for DateTime
#include "formspec.h" // for FormatSpecificData, kFsIGC



#define MYNAME "IGC"
Expand Down Expand Up @@ -79,7 +83,7 @@ unsigned char IgcFormat::coords_match(double lat1, double lon1, double lat2, dou
* @param rec Caller allocated storage for the record. At least kMaxRecLen chars must be allocated.
* @return the record type. rec_none on EOF, rec_bad on fgets() or parse error.
*/
IgcFormat::igc_rec_type_t IgcFormat::get_record(char** rec)
IgcFormat::igc_rec_type_t IgcFormat::get_record(char** rec) const
{
char* c;
retry:
Expand Down Expand Up @@ -146,7 +150,7 @@ void IgcFormat::TaskRecordReader::igc_task_rec(const char* rec)
&day, &month, &year,
&hour, &minute, &second,
flight_date, task_num, &num_tp, task_desc) < 9) {
fatal(MYNAME ": task id (C) record parse error\n'%s'", rec);
fatal(MYNAME ": task id (C) record parse error A. \n'%s'", rec);
}
task_num[4] = '\0';
if (year < 70) {
Expand Down Expand Up @@ -216,7 +220,7 @@ void IgcFormat::TaskRecordReader::igc_task_rec(const char* rec)
break;

default:
fatal(MYNAME ": task id (C) record internal error\n%s", rec);
fatal(MYNAME ": task id (C) record internal error B\n%s", rec);
break;
}

Expand Down Expand Up @@ -250,11 +254,15 @@ void IgcFormat::read()
char* hdr_data;
char trk_desc[kMaxDescLen + 1];
TaskRecordReader task_record_reader;
int current_line = 1; // For error reporting. Line numbering is off by one for some reason.
QList<std::tuple<QString, igc_ext_type_t, int, int, double>> ext_types_list;

strcpy(trk_desc, HDRMAGIC HDRDELIM);

while (true) {
igc_rec_type_t rec_type = get_record(&ibuf);
current_line++;
QString ibuf_q = QString::fromUtf8(ibuf);
switch (rec_type) {
case rec_manuf_id:
// Manufacturer/ID record already found in rd_init().
Expand Down Expand Up @@ -302,7 +310,7 @@ void IgcFormat::read()
}
break;

case rec_fix:
case rec_fix: {
// Date must appear in file before the first fix record
if (!date.isValid()) {
fatal(MYNAME ": bad date\n");
Expand Down Expand Up @@ -348,6 +356,33 @@ void IgcFormat::read()
}
prev_tod = tod;

/*
* Parse any extension data present. If no extensions are used (unlikely,
* but possible in the case of homebrew flight recorders), then skip the
* whole thing.
*/
if (!ext_types_list.isEmpty()) {
auto* fsdata = new igc_fsdata;
if (global_opts.debug_level >= 7) {
printf(MYNAME ": Record: %s\n",qPrintable(ibuf_q));
}
if (global_opts.debug_level >= 6) {
printf(MYNAME ": Adding extension data:");
}
for (const auto& [name, ext, start, len, factor] : ext_types_list) {
double ext_data = ibuf_q.mid(start,len).toInt() / factor;

fsdata->set_value(ext, ext_data);
if (global_opts.debug_level >= 6) {
printf(" %s:%f", qPrintable(name), ext_data);
}
}
if (global_opts.debug_level >= 6) {
printf("\n");
}
pres_wpt->fs.FsChainAdd(fsdata);
}

pres_wpt->SetCreationTime(QDateTime(date, tod, Qt::UTC));

// Add the waypoint to the pressure altitude track
Expand All @@ -358,7 +393,6 @@ void IgcFormat::read()
pres_wpt->altitude = unknown_alt;
}
track_add_wpt(pres_head, pres_wpt);

// Add the same waypoint with GNSS altitude to the second
// track
gnss_wpt = new Waypoint(*pres_wpt);
Expand All @@ -371,6 +405,7 @@ void IgcFormat::read()
}
track_add_wpt(gnss_head, gnss_wpt);
break;
}

case rec_task:
// Create a route for each pre-flight declaration
Expand All @@ -387,7 +422,7 @@ void IgcFormat::read()
// Create a route for each post-flight declaration
task_record_reader.igc_task_rec(ibuf + 4);
break;
} else if (global_opts.debug_level) {
} else if (global_opts.debug_level >= 4) {
if (strcmp(tmp_str, "OOI") == 0) {
printf(MYNAME ": Observer Input> %s\n", ibuf + 4);
} else if (strcmp(tmp_str, "PLT") == 0) {
Expand All @@ -400,17 +435,76 @@ void IgcFormat::read()
}
break;

// These record types are discarded
case rec_fix_defn: {
// We need to scope this, or the compiler complains "transfer of control bypasses initialization of:"
// Not sure exactly what that means... something something scoping and initialization.
/*
* The first three characters define the number of extensions present.
* We don't particularly care about that. After that, every group of seven
* bytes is 4 digits followed by three letters, specifying start end end
* bytes of each extension, and the kind of extension (always three chars)
* Building the list of (un)supported extensions isn't necessary if we aren't
* producing debug output, but this case: is only done once per file.
*/

QList<QString> unsupported_extensions; // For determining how often unspported extensions exist
QList<QString> supported_extensions; // For debug output, determining how often supported extensions exist
if (global_opts.debug_level >= 1) {
printf(MYNAME ": I record: %s\n" MYNAME ": ", qPrintable(ibuf_q));
}
for (int i=3; i < ibuf_q.length(); i+=7) {
QString ext_type = ibuf_q.mid(i+4, 3);
QString extension_definition = ibuf_q.mid(i,7);
if (global_opts.debug_level >= 1) {
printf(" %s;",qPrintable(ext_type));
}
// -1 because IGC records are one-initialized and QStrings are zero-initialized
int begin = extension_definition.mid(0,2).toInt() - 1;
int end = extension_definition.mid(2,2).toInt() - 1;
int len = end - begin + 1;
QString name = extension_definition.mid(4,3);
igc_ext_type_t ext = get_ext_type(ext_type);
if (ext != IgcFormat::igc_ext_type_t::ext_rec_unknown) {
int factor = get_ext_factor(ext);
ext_types_list.append(std::make_tuple(name, ext, begin, len, factor));
supported_extensions.append(name);
} else {
unsupported_extensions.append(name);
}
}
if (global_opts.debug_level >= 1) {
printf("\n");
}
if (global_opts.debug_level >= 2) {
printf(MYNAME ": Extensions defined in I record:\n");
printf(MYNAME ": (Note: IGC records are one-initialized. QStrings are zero-initialized.)\n");
for (const auto& [name, ext, begin, len, factor] : ext_types_list) {
printf(MYNAME ": Extension %s (%i): Begin: %i; Length: %i\n", qPrintable(name), int(ext), begin, len);
}
if (global_opts.debug_level >= 3) {
printf("\n" MYNAME "Supported extensions:");
foreach (QString ext, supported_extensions) {
printf(" %s", qPrintable(ext));
}
printf("\nUnsupported extensions:");
foreach (QString ext, unsupported_extensions) {
printf(" %s", qPrintable(ext));
}
printf("\n");
}
}
}

// These record types are discarded
case rec_diff_gps:
case rec_event:
case rec_constel:
case rec_security:
case rec_fix_defn:
case rec_extn_defn:
case rec_extn_data:
break;

// No more records
// No more records
case rec_none:

// Include pressure altitude track only if it has useful
Expand All @@ -424,6 +518,8 @@ void IgcFormat::read()
if (gnss_head && !gnss_valid && pres_head) {
track_del_head(gnss_head);
}
// Dammit I hate early returns. I also hate while(true)
// TODO: Fix this. (KV)
return; // All done so bail

default:
Expand Down Expand Up @@ -465,7 +561,7 @@ void IgcFormat::detect_other_track(const route_head* rh, int& max_waypt_ct)
if (rh->rte_waypt_ct() > max_waypt_ct &&
(rh->rte_name.isEmpty() ||
(!rh->rte_name.startsWith(kPresTrkName) &&
!rh->rte_name.startsWith(kGNSSTrkName)))) {
!rh->rte_name.startsWith(kGNSSTrkName)))) {
head = rh;
max_waypt_ct = rh->rte_waypt_ct();
}
Expand Down Expand Up @@ -583,7 +679,7 @@ void IgcFormat::wr_header()
date = current_time();
assert(date.isValid() || gpsbabel_testmode());
}

gbfprintf(file_out, "HFDTE%s\r\n", date2str(date).constData());

// Other header data may have been stored in track description
Expand Down Expand Up @@ -768,7 +864,7 @@ int IgcFormat::correlate_tracks(const route_head* pres_track, const route_head*
speed = (deltat_msec == 0) ? 0:
radtometers(gcdist(RAD(wpt->latitude), RAD(wpt->longitude),
RAD((*wpt_rit)->latitude), RAD((*wpt_rit)->longitude))) /
(0.001 * deltat_msec);
(0.001 * deltat_msec);
if (global_opts.debug_level >= 2) {
printf(MYNAME ": speed=%.2fm/s\n", speed);
}
Expand Down
Loading