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' [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;