Cleaned up the frame.ts by splitting payload parsing to subpackages
This commit is contained in:
197
src/payload.telemetry.ts
Normal file
197
src/payload.telemetry.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import { FieldType, type Segment } from "@hamradio/packet";
|
||||
|
||||
import {
|
||||
DataType,
|
||||
type Payload,
|
||||
type TelemetryBitSensePayload,
|
||||
type TelemetryCoefficientsPayload,
|
||||
type TelemetryDataPayload,
|
||||
type TelemetryParameterPayload,
|
||||
type TelemetryUnitPayload
|
||||
} from "./frame.types";
|
||||
|
||||
export const decodeTelemetryPayload = (
|
||||
raw: string,
|
||||
withStructure: boolean = false
|
||||
): {
|
||||
payload: Payload | null;
|
||||
segment?: Segment[];
|
||||
} => {
|
||||
try {
|
||||
if (raw.length < 2) return { payload: null };
|
||||
|
||||
const rest = raw.substring(1).trim();
|
||||
if (!rest) return { payload: null };
|
||||
|
||||
const segments: Segment[] = withStructure ? [] : [];
|
||||
|
||||
// Telemetry data: convention used here: starts with '#' then sequence then analogs and digital
|
||||
if (rest.startsWith("#")) {
|
||||
const parts = rest.substring(1).trim().split(/\s+/);
|
||||
const seq = parseInt(parts[0], 10);
|
||||
let analog: number[] = [];
|
||||
let digital = 0;
|
||||
|
||||
if (parts.length >= 2) {
|
||||
// analogs as comma separated
|
||||
analog = parts[1].split(",").map((v) => parseFloat(v));
|
||||
}
|
||||
|
||||
if (parts.length >= 3) {
|
||||
digital = parseInt(parts[2], 10);
|
||||
}
|
||||
|
||||
if (withStructure) {
|
||||
segments.push({
|
||||
name: "telemetry sequence",
|
||||
data: new TextEncoder().encode(String(seq)).buffer,
|
||||
isString: true,
|
||||
fields: [
|
||||
{
|
||||
type: FieldType.STRING,
|
||||
name: "sequence",
|
||||
length: String(seq).length
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
segments.push({
|
||||
name: "telemetry analog",
|
||||
data: new TextEncoder().encode(parts[1] || "").buffer,
|
||||
isString: true,
|
||||
fields: [
|
||||
{
|
||||
type: FieldType.STRING,
|
||||
name: "analogs",
|
||||
length: (parts[1] || "").length
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
segments.push({
|
||||
name: "telemetry digital",
|
||||
data: new TextEncoder().encode(String(digital)).buffer,
|
||||
isString: true,
|
||||
fields: [
|
||||
{
|
||||
type: FieldType.STRING,
|
||||
name: "digital",
|
||||
length: String(digital).length
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const payload: TelemetryDataPayload = {
|
||||
type: DataType.TelemetryData,
|
||||
variant: "data",
|
||||
sequence: isNaN(seq) ? 0 : seq,
|
||||
analog,
|
||||
digital: isNaN(digital) ? 0 : digital
|
||||
};
|
||||
|
||||
if (withStructure) return { payload, segment: segments };
|
||||
return { payload };
|
||||
}
|
||||
|
||||
// Telemetry parameters: 'PARAM' keyword
|
||||
if (/^PARAM/i.test(rest)) {
|
||||
const after = rest.replace(/^PARAM\s*/i, "");
|
||||
const names = after.split(/[,\s]+/).filter(Boolean);
|
||||
if (withStructure) {
|
||||
segments.push({
|
||||
name: "telemetry parameters",
|
||||
data: new TextEncoder().encode(after).buffer,
|
||||
isString: true,
|
||||
fields: [{ type: FieldType.STRING, name: "names", length: after.length }]
|
||||
});
|
||||
}
|
||||
const payload: TelemetryParameterPayload = {
|
||||
type: DataType.TelemetryData,
|
||||
variant: "parameters",
|
||||
names
|
||||
};
|
||||
if (withStructure) return { payload, segment: segments };
|
||||
return { payload };
|
||||
}
|
||||
|
||||
// Telemetry units: 'UNIT'
|
||||
if (/^UNIT/i.test(rest)) {
|
||||
const after = rest.replace(/^UNIT\s*/i, "");
|
||||
const units = after.split(/[,\s]+/).filter(Boolean);
|
||||
if (withStructure) {
|
||||
segments.push({
|
||||
name: "telemetry units",
|
||||
data: new TextEncoder().encode(after).buffer,
|
||||
isString: true,
|
||||
fields: [{ type: FieldType.STRING, name: "units", length: after.length }]
|
||||
});
|
||||
}
|
||||
const payload: TelemetryUnitPayload = {
|
||||
type: DataType.TelemetryData,
|
||||
variant: "unit",
|
||||
units
|
||||
};
|
||||
if (withStructure) return { payload, segment: segments };
|
||||
return { payload };
|
||||
}
|
||||
|
||||
// Telemetry coefficients: 'COEFF' a:,b:,c:
|
||||
if (/^COEFF/i.test(rest)) {
|
||||
const after = rest.replace(/^COEFF\s*/i, "");
|
||||
const aMatch = after.match(/A:([^\s;]+)/i);
|
||||
const bMatch = after.match(/B:([^\s;]+)/i);
|
||||
const cMatch = after.match(/C:([^\s;]+)/i);
|
||||
const parseList = (s?: string) => (s ? s.split(",").map((v) => parseFloat(v)) : []);
|
||||
const coefficients = {
|
||||
a: parseList(aMatch?.[1]),
|
||||
b: parseList(bMatch?.[1]),
|
||||
c: parseList(cMatch?.[1])
|
||||
};
|
||||
if (withStructure) {
|
||||
segments.push({
|
||||
name: "telemetry coefficients",
|
||||
data: new TextEncoder().encode(after).buffer,
|
||||
isString: true,
|
||||
fields: [{ type: FieldType.STRING, name: "coeffs", length: after.length }]
|
||||
});
|
||||
}
|
||||
const payload: TelemetryCoefficientsPayload = {
|
||||
type: DataType.TelemetryData,
|
||||
variant: "coefficients",
|
||||
coefficients
|
||||
};
|
||||
if (withStructure) return { payload, segment: segments };
|
||||
return { payload };
|
||||
}
|
||||
|
||||
// Telemetry bitsense/project: 'BITS' <number> [project]
|
||||
if (/^BITS?/i.test(rest)) {
|
||||
const parts = rest.split(/\s+/).slice(1);
|
||||
const sense = parts.length > 0 ? parseInt(parts[0], 10) : 0;
|
||||
const projectName = parts.length > 1 ? parts.slice(1).join(" ") : undefined;
|
||||
if (withStructure) {
|
||||
segments.push({
|
||||
name: "telemetry bitsense",
|
||||
data: new TextEncoder().encode(rest).buffer,
|
||||
isString: true,
|
||||
fields: [{ type: FieldType.STRING, name: "bitsense", length: rest.length }]
|
||||
});
|
||||
}
|
||||
const payload: TelemetryBitSensePayload = {
|
||||
type: DataType.TelemetryData,
|
||||
variant: "bitsense",
|
||||
sense: isNaN(sense) ? 0 : sense,
|
||||
...(projectName ? { projectName } : {})
|
||||
};
|
||||
if (withStructure) return { payload, segment: segments };
|
||||
return { payload };
|
||||
}
|
||||
|
||||
return { payload: null };
|
||||
} catch {
|
||||
return { payload: null };
|
||||
}
|
||||
};
|
||||
|
||||
export default decodeTelemetryPayload;
|
||||
Reference in New Issue
Block a user