Summary
Label_QQ.decode() decodes the groundspeed field at message.text.substring(45, 48), and clearly intends to skip decoding it (calling ResultFormatter.unknown instead) when it is the placeholder ---. But the guard tests decodeResult.remaining.text, not the groundspeed field itself — and by the time the check runs, remaining.text reflects the previous unknown field at substring(42, 45), not the groundspeed field at substring(45, 48).
Result: any QQ message where the 45–48 field is --- but the 42–45 field is anything else passes the guard and calls ResultFormatter.groundspeed(decodeResult, Number('---')), storing NaN and rendering "NaN knots".
Location
lib/plugins/Label_QQ.ts:43-53
ResultFormatter.unknown(decodeResult, message.text.substring(42, 45));
ResultFormatter.position(decodeResult, pos);
if (decodeResult.remaining.text !== '---') {
ResultFormatter.groundspeed(
decodeResult,
Number(message.text.substring(45, 48)),
);
} else {
ResultFormatter.unknown(decodeResult, message.text.substring(45, 48));
}
Why the guard is wrong
ResultFormatter.unknown appends to decodeResult.remaining.text (see lib/utils/result_formatter.ts:683-684):
if (!decodeResult.remaining.text) decodeResult.remaining.text = value;
else decodeResult.remaining.text += sep + value;
So after the substring(42, 45) call on the line above, remaining.text ends in that field's value. Checking remaining.text !== '---' is therefore checking the 42–45 field, not the groundspeed field the branch is about to decode.
The current tests don't catch this because in both fixture variants, either both fields are --- or neither is.
Reproduction
const msg = {
label: 'QQ',
text: 'KLGBKLAX0004\r\n001FE07000444N3349.8W11810.1028---0009',
};
const result = new Label_QQ().decode(msg);
// Actual: result.raw.groundspeed === NaN, formatted contains "NaN knots"
// Expected: no groundspeed decoded; 45..48 recorded as unknown
Here substring(42, 45) is '028' (not ---), so the guard passes, but substring(45, 48) is '---', which Number('---') yields NaN.
Suggested fix
Read the field directly instead of the accumulated remaining.text:
const gsField = message.text.substring(45, 48);
if (gsField !== '---') {
ResultFormatter.groundspeed(decodeResult, Number(gsField));
} else {
ResultFormatter.unknown(decodeResult, gsField);
}
Add a test case covering the 028--- split.
Summary
Label_QQ.decode()decodes the groundspeed field atmessage.text.substring(45, 48), and clearly intends to skip decoding it (callingResultFormatter.unknowninstead) when it is the placeholder---. But the guard testsdecodeResult.remaining.text, not the groundspeed field itself — and by the time the check runs,remaining.textreflects the previousunknownfield atsubstring(42, 45), not the groundspeed field atsubstring(45, 48).Result: any QQ message where the 45–48 field is
---but the 42–45 field is anything else passes the guard and callsResultFormatter.groundspeed(decodeResult, Number('---')), storingNaNand rendering"NaN knots".Location
lib/plugins/Label_QQ.ts:43-53Why the guard is wrong
ResultFormatter.unknownappends todecodeResult.remaining.text(seelib/utils/result_formatter.ts:683-684):So after the
substring(42, 45)call on the line above,remaining.textends in that field's value. Checkingremaining.text !== '---'is therefore checking the 42–45 field, not the groundspeed field the branch is about to decode.The current tests don't catch this because in both fixture variants, either both fields are
---or neither is.Reproduction
Here
substring(42, 45)is'028'(not---), so the guard passes, butsubstring(45, 48)is'---', whichNumber('---')yieldsNaN.Suggested fix
Read the field directly instead of the accumulated
remaining.text:Add a test case covering the
028---split.