Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
78dbd3b0ef
|
|||
|
df266bab12
|
|||
|
0ab62dab02
|
|||
|
38b617728c
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hamradio/aprs",
|
"name": "@hamradio/aprs",
|
||||||
"version": "1.1.1",
|
"version": "1.1.3",
|
||||||
"description": "APRS (Automatic Packet Reporting System) protocol support for Typescript",
|
"description": "APRS (Automatic Packet Reporting System) protocol support for Typescript",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"APRS",
|
"APRS",
|
||||||
|
|||||||
27
src/frame.ts
27
src/frame.ts
@@ -378,6 +378,13 @@ export class Frame implements IFrame {
|
|||||||
if (routingSection) {
|
if (routingSection) {
|
||||||
structure.push(routingSection);
|
structure.push(routingSection);
|
||||||
}
|
}
|
||||||
|
// Add data type identifier section
|
||||||
|
structure.push({
|
||||||
|
name: "data type",
|
||||||
|
data: new TextEncoder().encode(this.payload.charAt(0)).buffer,
|
||||||
|
isString: true,
|
||||||
|
fields: [{ type: FieldType.CHAR, name: "identifier", length: 1 }],
|
||||||
|
});
|
||||||
if (payloadsegment) {
|
if (payloadsegment) {
|
||||||
structure.push(...payloadsegment);
|
structure.push(...payloadsegment);
|
||||||
}
|
}
|
||||||
@@ -416,7 +423,9 @@ export class Frame implements IFrame {
|
|||||||
offset += 7;
|
offset += 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.payload.length < offset + 19) return { payload: null };
|
// Need at least enough characters for compressed position (13) or
|
||||||
|
// uncompressed (19). Allow parsing to continue if compressed-length is present.
|
||||||
|
if (this.payload.length < offset + 13) return { payload: null };
|
||||||
|
|
||||||
// Check if compressed format
|
// Check if compressed format
|
||||||
const isCompressed = this.isCompressedPosition(
|
const isCompressed = this.isCompressedPosition(
|
||||||
@@ -2306,34 +2315,34 @@ const parseFrame = (data: string): Frame => {
|
|||||||
|
|
||||||
pathFields.push({
|
pathFields.push({
|
||||||
type: FieldType.CHAR,
|
type: FieldType.CHAR,
|
||||||
name: `Path separator ${i}`,
|
name: `path separator ${i}`,
|
||||||
length: 1,
|
length: 1,
|
||||||
});
|
});
|
||||||
pathFields.push({
|
pathFields.push({
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
name: `Repeater ${i}`,
|
name: `repeater ${i}`,
|
||||||
length: pathStr.length,
|
length: pathStr.length,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const routingSection: Segment = {
|
const routingSection: Segment = {
|
||||||
name: "Routing",
|
name: "routing",
|
||||||
data: encoder.encode(data.slice(0, routeSepIndex)).buffer,
|
data: encoder.encode(data.slice(0, routeSepIndex + 1)).buffer,
|
||||||
isString: true,
|
isString: true,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
name: "Source address",
|
name: "source address",
|
||||||
length: sourceStr.length,
|
length: sourceStr.length,
|
||||||
},
|
},
|
||||||
{ type: FieldType.CHAR, name: "Route separator", length: 1 },
|
{ type: FieldType.CHAR, name: "route separator", length: 1 },
|
||||||
{
|
{
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
name: "Destination address",
|
name: "destination address",
|
||||||
length: destinationStr.length,
|
length: destinationStr.length,
|
||||||
},
|
},
|
||||||
...pathFields,
|
...pathFields,
|
||||||
{ type: FieldType.CHAR, name: "Payload separator", length: 1 },
|
{ type: FieldType.CHAR, name: "payload separator", length: 1 },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { Frame } from "../src/frame";
|
import { Frame } from "../src/frame";
|
||||||
import type { Payload, StationCapabilitiesPayload } from "../src/frame.types";
|
import {
|
||||||
|
DataType,
|
||||||
|
type Payload,
|
||||||
|
type StationCapabilitiesPayload,
|
||||||
|
} from "../src/frame.types";
|
||||||
import { Dissected } from "@hamradio/packet";
|
import { Dissected } from "@hamradio/packet";
|
||||||
|
|
||||||
describe("Frame.decodeCapabilities", () => {
|
describe("Frame.decodeCapabilities", () => {
|
||||||
@@ -9,7 +13,7 @@ describe("Frame.decodeCapabilities", () => {
|
|||||||
const frame = Frame.fromString(data);
|
const frame = Frame.fromString(data);
|
||||||
const decoded = frame.decode() as StationCapabilitiesPayload;
|
const decoded = frame.decode() as StationCapabilitiesPayload;
|
||||||
expect(decoded).not.toBeNull();
|
expect(decoded).not.toBeNull();
|
||||||
expect(decoded.type).toBe("capabilities");
|
expect(decoded.type).toBe(DataType.StationCapabilities);
|
||||||
expect(Array.isArray(decoded.capabilities)).toBeTruthy();
|
expect(Array.isArray(decoded.capabilities)).toBeTruthy();
|
||||||
expect(decoded.capabilities).toContain("IGATE");
|
expect(decoded.capabilities).toContain("IGATE");
|
||||||
expect(decoded.capabilities).toContain("MSG_CNT");
|
expect(decoded.capabilities).toContain("MSG_CNT");
|
||||||
@@ -23,7 +27,7 @@ describe("Frame.decodeCapabilities", () => {
|
|||||||
structure: Dissected;
|
structure: Dissected;
|
||||||
};
|
};
|
||||||
expect(res.payload).not.toBeNull();
|
expect(res.payload).not.toBeNull();
|
||||||
if (res.payload && res.payload.type !== "capabilities")
|
if (res.payload && res.payload.type !== DataType.StationCapabilities)
|
||||||
throw new Error("expected capabilities payload");
|
throw new Error("expected capabilities payload");
|
||||||
expect(res.structure).toBeDefined();
|
expect(res.structure).toBeDefined();
|
||||||
const caps = res.structure.find((s) => s.name === "capabilities");
|
const caps = res.structure.find((s) => s.name === "capabilities");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { Frame } from "../src/frame";
|
import { Frame } from "../src/frame";
|
||||||
import type { RawGPSPayload } from "../src/frame.types";
|
import { DataType, type RawGPSPayload } from "../src/frame.types";
|
||||||
import { Dissected } from "@hamradio/packet";
|
import { Dissected } from "@hamradio/packet";
|
||||||
|
|
||||||
describe("Raw GPS decoding", () => {
|
describe("Raw GPS decoding", () => {
|
||||||
@@ -13,7 +13,7 @@ describe("Raw GPS decoding", () => {
|
|||||||
const payload = f.decode(false) as RawGPSPayload | null;
|
const payload = f.decode(false) as RawGPSPayload | null;
|
||||||
|
|
||||||
expect(payload).not.toBeNull();
|
expect(payload).not.toBeNull();
|
||||||
expect(payload?.type).toBe("raw-gps");
|
expect(payload?.type).toBe(DataType.RawGPS);
|
||||||
expect(payload?.sentence).toBe(sentence);
|
expect(payload?.sentence).toBe(sentence);
|
||||||
expect(payload?.position).toBeDefined();
|
expect(payload?.position).toBeDefined();
|
||||||
expect(typeof payload?.position?.latitude).toBe("number");
|
expect(typeof payload?.position?.latitude).toBe("number");
|
||||||
@@ -32,7 +32,7 @@ describe("Raw GPS decoding", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
expect(result.payload).not.toBeNull();
|
expect(result.payload).not.toBeNull();
|
||||||
expect(result.payload?.type).toBe("raw-gps");
|
expect(result.payload?.type).toBe(DataType.RawGPS);
|
||||||
expect(result.payload?.sentence).toBe(sentence);
|
expect(result.payload?.sentence).toBe(sentence);
|
||||||
expect(result.payload?.position).toBeDefined();
|
expect(result.payload?.position).toBeDefined();
|
||||||
expect(typeof result.payload?.position?.latitude).toBe("number");
|
expect(typeof result.payload?.position?.latitude).toBe("number");
|
||||||
|
|||||||
@@ -837,19 +837,19 @@ describe("Packet dissection with sections", () => {
|
|||||||
expect(result.structure).toBeDefined();
|
expect(result.structure).toBeDefined();
|
||||||
expect(result.structure.length).toBeGreaterThan(0);
|
expect(result.structure.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
const routingSection = result.structure.find((s) => s.name === "Routing");
|
const routingSection = result.structure.find((s) => s.name === "routing");
|
||||||
expect(routingSection).toBeDefined();
|
expect(routingSection).toBeDefined();
|
||||||
expect(routingSection?.fields).toBeDefined();
|
expect(routingSection?.fields).toBeDefined();
|
||||||
expect(routingSection?.fields?.length).toBeGreaterThan(0);
|
expect(routingSection?.fields?.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
const sourceField = routingSection?.fields?.find(
|
const sourceField = routingSection?.fields?.find(
|
||||||
(a) => a.name === "Source address",
|
(a) => a.name === "source address",
|
||||||
);
|
);
|
||||||
expect(sourceField).toBeDefined();
|
expect(sourceField).toBeDefined();
|
||||||
expect(sourceField?.length).toBeGreaterThan(0);
|
expect(sourceField?.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
const destField = routingSection?.fields?.find(
|
const destField = routingSection?.fields?.find(
|
||||||
(a) => a.name === "Destination address",
|
(a) => a.name === "destination address",
|
||||||
);
|
);
|
||||||
expect(destField).toBeDefined();
|
expect(destField).toBeDefined();
|
||||||
expect(destField?.length).toBeGreaterThan(0);
|
expect(destField?.length).toBeGreaterThan(0);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { Dissected } from "@hamradio/packet";
|
import { Dissected } from "@hamradio/packet";
|
||||||
import { Frame } from "../src/frame";
|
import { Frame } from "../src/frame";
|
||||||
import type { UserDefinedPayload } from "../src/frame.types";
|
import { DataType, type UserDefinedPayload } from "../src/frame.types";
|
||||||
|
|
||||||
describe("Frame.decodeUserDefined", () => {
|
describe("Frame.decodeUserDefined", () => {
|
||||||
it("parses packet type only", () => {
|
it("parses packet type only", () => {
|
||||||
@@ -9,7 +9,7 @@ describe("Frame.decodeUserDefined", () => {
|
|||||||
const frame = Frame.fromString(data);
|
const frame = Frame.fromString(data);
|
||||||
const decoded = frame.decode() as UserDefinedPayload;
|
const decoded = frame.decode() as UserDefinedPayload;
|
||||||
expect(decoded).not.toBeNull();
|
expect(decoded).not.toBeNull();
|
||||||
expect(decoded.type).toBe("user-defined");
|
expect(decoded.type).toBe(DataType.UserDefined);
|
||||||
expect(decoded.userPacketType).toBe("01");
|
expect(decoded.userPacketType).toBe("01");
|
||||||
expect(decoded.data).toBe("");
|
expect(decoded.data).toBe("");
|
||||||
});
|
});
|
||||||
@@ -22,7 +22,7 @@ describe("Frame.decodeUserDefined", () => {
|
|||||||
structure: Dissected;
|
structure: Dissected;
|
||||||
};
|
};
|
||||||
expect(res.payload).not.toBeNull();
|
expect(res.payload).not.toBeNull();
|
||||||
expect(res.payload.type).toBe("user-defined");
|
expect(res.payload.type).toBe(DataType.UserDefined);
|
||||||
expect(res.payload.userPacketType).toBe("TEX");
|
expect(res.payload.userPacketType).toBe("TEX");
|
||||||
expect(res.payload.data).toBe("Hello world");
|
expect(res.payload.data).toBe("Hello world");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user