Refactored extras field parsing

This commit is contained in:
2026-03-18 18:22:53 +01:00
parent 6adf1281ef
commit e9e329ccc1
3 changed files with 166 additions and 95 deletions

View File

@@ -764,32 +764,60 @@ export class Frame implements IFrame {
const extras: Partial<Extras> = {};
const fields: Field[] = [];
const beforeFields: Field[] = [];
let altitudeOffset: number | undefined = undefined;
let altitudeFields: Field[] = [];
let commentOffset: number = 0;
let commentBefore: string | undefined = undefined;
// Process successive 7-byte data extensions at the start of the comment.
let ext = comment.trimStart();
while (ext.length >= 7) {
// We first process the altitude marker, because it may appear anywhere
// in the comment and we want to extract it and its value before
// processing other tokens that may be present.
//
// /A=NNNNNN -> altitude in feet (6 digits)
// /A=-NNNNN -> altitude in feet with leading minus for negative altitudes (5 digits)
const altMatch = ext.match(/\/A=(-\d{5}|\d{6})/);
if (altMatch) {
if (altitudeOffset === undefined && altMatch) {
const altitude = feetToMeters(parseInt(altMatch[1], 10)); // feet to meters
if (isNaN(altitude)) {
break; // Invalid altitude format, stop parsing extras
}
extras.altitude = altitude;
// Keep track of where the altitude token appears in the comment for structure purposes.
altitudeOffset = comment.indexOf(altMatch[0]);
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "altitude marker", length: 2 });
fields.push({
type: FieldType.STRING,
name: "altitude value",
length: altMatch[1].length,
value: altitude.toFixed(3) + "m"
});
altitudeFields = [
{
type: FieldType.STRING,
name: "altitude marker",
data: new TextEncoder().encode("/A=").buffer,
length: 3
},
{
type: FieldType.STRING,
name: "altitude",
data: new TextEncoder().encode(altMatch[1]).buffer,
length: altMatch[1].length,
value: altitude.toFixed(3) + "m"
}
];
}
// remove altitude token from comment and advance ext for further parsing
comment = comment.replace(altMatch[0], "").trimStart();
if (altitudeOffset > 0) {
// Splice the comment into "before" and "after" around the altitude token.
commentBefore = comment.substring(0, altitudeOffset).trimEnd();
comment = comment.substring(altitudeOffset + altMatch[0].length).trimStart();
ext = commentBefore + comment; // Update ext to reflect the new comment with altitude token removed
continue;
}
// remove altitude token from ext and advance ext for further parsing
commentOffset += 7;
ext = ext.replace(altMatch[0], "").trimStart();
continue;
@@ -801,17 +829,24 @@ export class Frame implements IFrame {
if (/^\d{4}$/.test(r)) {
extras.range = milesToMeters(parseInt(r, 10)) / 1000.0; // Convert to kilometers
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "RNG marker", length: 3, value: "RNG" });
fields.push({
type: FieldType.STRING,
name: "range (rrrr)",
length: 4,
value: extras.range.toString() + "km"
});
(altitudeOffset !== undefined && commentOffset < altitudeOffset ? beforeFields : fields).push(
{
type: FieldType.STRING,
name: "range marker",
value: "RNG",
length: 3
},
{
type: FieldType.STRING,
name: "range (rrrr)",
length: 4,
value: extras.range.toString() + "km"
}
);
}
// remove range token from comment and advance ext for further parsing
comment = comment.substring(7).trimStart();
// remove range token from ext and advance ext for further parsing
commentOffset += 7;
ext = ext.substring(7).trimStart();
continue;
@@ -852,40 +887,42 @@ export class Frame implements IFrame {
};
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "PHG marker", length: 3, value: "PHG" });
fields.push({
type: FieldType.STRING,
name: "power (p)",
length: 1,
value: powerWatts !== undefined ? powerWatts.toString() + "W" : undefined
});
fields.push({
type: FieldType.STRING,
name: "height (h)",
length: 1,
value: heightMeters !== undefined ? heightMeters.toString() + "m" : undefined
});
fields.push({
type: FieldType.STRING,
name: "gain (g)",
length: 1,
value: gainDbi !== undefined ? gainDbi.toString() + "dBi" : undefined
});
fields.push({
type: FieldType.STRING,
name: "directivity (d)",
length: 1,
value:
directivity !== undefined
? typeof directivity === "number"
? directivity.toString() + "
: directivity
: undefined
});
(altitudeOffset !== undefined && commentOffset < altitudeOffset ? beforeFields : fields).push(
{ type: FieldType.STRING, name: "PHG marker", length: 3, value: "PHG" },
{
type: FieldType.STRING,
name: "power (p)",
length: 1,
value: powerWatts !== undefined ? powerWatts.toString() + "W" : undefined
},
{
type: FieldType.STRING,
name: "height (h)",
length: 1,
value: heightMeters !== undefined ? heightMeters.toString() + "m" : undefined
},
{
type: FieldType.STRING,
name: "gain (g)",
length: 1,
value: gainDbi !== undefined ? gainDbi.toString() + "dBi" : undefined
},
{
type: FieldType.STRING,
name: "directivity (d)",
length: 1,
value:
directivity !== undefined
? typeof directivity === "number"
? directivity.toString() + "°"
: directivity
: undefined
}
);
}
// remove PHG token from comment and advance ext for further parsing
comment = comment.substring(7).trimStart();
// remove PHG token from ext and advance ext for further parsing
commentOffset += 7;
ext = ext.substring(7).trimStart();
continue;
@@ -934,40 +971,42 @@ export class Frame implements IFrame {
};
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "DFS marker", length: 3, value: "DFS" });
fields.push({
type: FieldType.STRING,
name: "strength (s)",
length: 1,
value: strength !== undefined ? strength.toString() : undefined
});
fields.push({
type: FieldType.STRING,
name: "height (h)",
length: 1,
value: heightMeters !== undefined ? heightMeters.toString() + "m" : undefined
});
fields.push({
type: FieldType.STRING,
name: "gain (g)",
length: 1,
value: gainDbi !== undefined ? gainDbi.toString() + "dBi" : undefined
});
fields.push({
type: FieldType.STRING,
name: "directivity (d)",
length: 1,
value:
directivity !== undefined
? typeof directivity === "number"
? directivity.toString() + "
: directivity
: undefined
});
(altitudeOffset !== undefined && commentOffset < altitudeOffset ? beforeFields : fields).push(
{ type: FieldType.STRING, name: "DFS marker", length: 3, value: "DFS" },
{
type: FieldType.STRING,
name: "strength (s)",
length: 1,
value: strength !== undefined ? strength.toString() : undefined
},
{
type: FieldType.STRING,
name: "height (h)",
length: 1,
value: heightMeters !== undefined ? heightMeters.toString() + "m" : undefined
},
{
type: FieldType.STRING,
name: "gain (g)",
length: 1,
value: gainDbi !== undefined ? gainDbi.toString() + "dBi" : undefined
},
{
type: FieldType.STRING,
name: "directivity (d)",
length: 1,
value:
directivity !== undefined
? typeof directivity === "number"
? directivity.toString() + "°"
: directivity
: undefined
}
);
}
// remove DFS token from comment and advance ext for further parsing
comment = comment.substring(7).trimStart();
// remove DFS token from ext and advance ext for further parsing
commentOffset += 7;
ext = ext.substring(7).trimStart();
continue;
@@ -981,9 +1020,11 @@ export class Frame implements IFrame {
extras.spd = knotsToKmh(parseInt(speedStr, 10));
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "course", length: 3, value: extras.cse.toString() + "°" });
fields.push({ type: FieldType.CHAR, name: "marker", length: 1, value: "/" });
fields.push({ type: FieldType.STRING, name: "speed", length: 3, value: extras.spd.toString() + " km/h" });
(altitudeOffset !== undefined && commentOffset < altitudeOffset ? beforeFields : fields).push(
{ type: FieldType.STRING, name: "course", length: 3, value: extras.cse.toString() + "°" },
{ type: FieldType.CHAR, name: "marker", length: 1, value: "/" },
{ type: FieldType.STRING, name: "speed", length: 3, value: extras.spd.toString() + " km/h" }
);
}
// remove course/speed token from comment and advance ext for further parsing
@@ -1004,14 +1045,16 @@ export class Frame implements IFrame {
extras.dfs.strength = dfStrength;
if (withStructure) {
fields.push({ type: FieldType.STRING, name: "DF marker", length: 1, value: "/" });
fields.push({ type: FieldType.STRING, name: "bearing", length: 3, value: dfBearing.toString() + "°" });
fields.push({ type: FieldType.CHAR, name: "separator", length: 1, value: "/" });
fields.push({ type: FieldType.STRING, name: "strength", length: 3, value: dfStrength.toString() });
(altitudeOffset !== undefined && commentOffset < altitudeOffset ? beforeFields : fields).push(
{ type: FieldType.STRING, name: "DF marker", length: 1, value: "/" },
{ type: FieldType.STRING, name: "bearing", length: 3, value: dfBearing.toString() + "°" },
{ type: FieldType.CHAR, name: "separator", length: 1, value: "/" },
{ type: FieldType.STRING, name: "strength", length: 3, value: dfStrength.toString() }
);
}
// remove DF token from comment and advance ext for further parsing
comment = comment.substring(8).trimStart();
// remove DF token from ext and advance ext for further parsing
commentOffset += 8;
ext = ext.substring(8).trimStart();
continue;
@@ -1024,8 +1067,36 @@ export class Frame implements IFrame {
break;
}
// Export comment with extras fields removed, if any were parsed.
comment = comment.substring(commentOffset).trimStart();
extras.comment = comment;
extras.fields = fields.length > 0 ? fields : undefined;
if (withStructure) {
const commentBeforeFields: Field[] = commentBefore
? [
{
type: FieldType.STRING,
name: "comment",
value: commentBefore,
length: commentBefore.length
}
]
: [];
const commentFields: Field[] = comment
? [
{
type: FieldType.STRING,
name: "comment",
value: comment,
length: comment.length
}
]
: [];
// Insert the altitude fields at the correct position in the comment section based on where the altitude token was located in the original comment. If there was no altitude token, put all fields at the start of the comment section.
extras.fields = [...commentBeforeFields, ...beforeFields, ...altitudeFields, ...fields, ...commentFields];
}
return extras as Extras;
}