Better parsing for extras; Added deviceID resolution

This commit is contained in:
2026-03-18 17:01:46 +01:00
parent be8cd00c00
commit 17caa22331
8 changed files with 1517 additions and 240 deletions

View File

@@ -233,7 +233,7 @@ describe("Frame.decodeMicE", () => {
const frame = Frame.fromString(data);
const decoded = frame.decode() as MicEPayload;
expect(decoded).not.toBeNull();
expect(decoded?.type).toBe(DataType.MicECurrent);
expect(decoded?.type).toBe(DataType.MicE);
});
it("decodes a Mic-E packet with old format (single quote)", () => {
@@ -322,6 +322,16 @@ describe("Frame.decodePosition", () => {
const decoded = frame.decode() as PositionPayload;
expect(decoded).not.toBeNull();
});
it("should handle UTF-8 characters", () => {
const data =
"WB2OSZ-5>APDW17:!4237.14NS07120.83W#PHG7140 Did you know that APRS comments and messages can contain UTF-8 characters? アマチュア無線";
const frame = Frame.fromString(data);
const decoded = frame.decode() as PositionPayload;
expect(decoded).not.toBeNull();
expect(decoded?.type).toBe(DataType.PositionNoTimestampNoMessaging);
expect(decoded?.position.comment).toContain("UTF-8 characters? アマチュア無線");
});
});
describe("Frame.decodeStatus", () => {
@@ -468,9 +478,9 @@ describe("Frame.decodeMicE", () => {
const decoded = frame.decode() as MicEPayload;
expect(decoded).not.toBeNull();
expect(decoded?.type).toBe(DataType.MicECurrent);
expect(decoded?.type).toBe(DataType.MicE);
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position).toBeDefined();
expect(typeof decoded.position.latitude).toBe("number");
expect(typeof decoded.position.longitude).toBe("number");
@@ -497,7 +507,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.latitude).toBeCloseTo(12 + 34.56 / 60, 3);
}
});
@@ -509,7 +519,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.latitude).toBeCloseTo(1 + 20.45 / 60, 3);
}
});
@@ -521,7 +531,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.latitude).toBeCloseTo(40 + 12.34 / 60, 3);
}
});
@@ -533,7 +543,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.latitude).toBeLessThan(0);
}
});
@@ -547,7 +557,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(typeof decoded.position.longitude).toBe("number");
expect(decoded.position.longitude).toBeGreaterThanOrEqual(-180);
expect(decoded.position.longitude).toBeLessThanOrEqual(180);
@@ -561,7 +571,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(typeof decoded.position.longitude).toBe("number");
}
});
@@ -573,7 +583,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(typeof decoded.position.longitude).toBe("number");
expect(Math.abs(decoded.position.longitude)).toBeGreaterThan(90);
}
@@ -588,7 +598,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
if (decoded.position.speed !== undefined) {
expect(decoded.position.speed).toBeGreaterThanOrEqual(0);
expect(typeof decoded.position.speed).toBe("number");
@@ -603,7 +613,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
if (decoded.position.course !== undefined) {
expect(decoded.position.course).toBeGreaterThanOrEqual(0);
expect(decoded.position.course).toBeLessThan(360);
@@ -618,7 +628,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.speed).toBeUndefined();
}
});
@@ -630,7 +640,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
if (decoded.position.course !== undefined) {
expect(decoded.position.course).toBeGreaterThan(0);
expect(decoded.position.course).toBeLessThan(360);
@@ -647,7 +657,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.symbol).toBeDefined();
expect(decoded.position.symbol?.table).toBeDefined();
expect(decoded.position.symbol?.code).toBeDefined();
@@ -659,29 +669,30 @@ describe("Frame.decodeMicE", () => {
describe("Altitude decoding", () => {
it("should decode altitude from /A=NNNNNN format", () => {
const data = "CALL>4ABCDE:`c.l+@&'/'\"G:}/A=001234";
const data = "CALL>4ABCDE:`c.l+@&'//A=001234";
const frame = Frame.fromString(data);
const decoded = frame.decode() as Payload;
const decoded = frame.decode() as MicEPayload;
expect(decoded).not.toBeNull();
expect(decoded.type).toBe(DataType.MicE);
if (decoded && decoded.type === DataType.MicECurrent) {
expect(decoded.position.altitude).toBeCloseTo(1234 * 0.3048, 1);
}
expect(decoded.position).toBeDefined();
expect(decoded.position.altitude).toBeDefined();
expect(decoded.position.altitude).toBeCloseTo(1234 * 0.3048, 1);
});
it("should decode altitude from base-91 format }abc", () => {
const data = "CALL>4AB2DE:`c.l+@&'/'\"G:}}S^X";
it("should decode altitude from base-91 format abc}", () => {
const data = "N83MZ>T2TQ5U,WA1PLE-4*:`c.l+@&'/\"4T}KJ6TMS";
const frame = Frame.fromString(data);
const decoded = frame.decode() as Payload;
const decoded = frame.decode() as MicEPayload;
expect(decoded).not.toBeNull();
expect(decoded.type).toBe(DataType.MicE);
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded.position.comment?.startsWith("}")) {
expect(decoded.position.altitude).toBeDefined();
}
}
expect(decoded.position).toBeDefined();
expect(decoded.position.altitude).toBeDefined();
expect(decoded.position.comment).toBe("KJ6TMS");
expect(decoded.position.altitude).toBeCloseTo(61, 1);
});
it("should prefer /A= format over base-91 when both present", () => {
@@ -691,7 +702,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.altitude).toBeCloseTo(5000 * 0.3048, 1);
}
});
@@ -703,7 +714,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.altitude).toBeUndefined();
expect(decoded.position.comment).toContain("Just a comment");
}
@@ -718,7 +729,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.messageType).toBe("M0: Off Duty");
}
});
@@ -730,7 +741,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.messageType).toBeDefined();
expect(typeof decoded.messageType).toBe("string");
}
@@ -753,7 +764,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.comment).toContain("This is a test comment");
}
});
@@ -765,7 +776,7 @@ describe("Frame.decodeMicE", () => {
expect(decoded).not.toBeNull();
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.comment).toBeDefined();
}
});
@@ -802,7 +813,7 @@ describe("Frame.decodeMicE", () => {
expect(() => frame.decode()).not.toThrow();
const decoded = frame.decode() as MicEPayload;
expect(decoded === null || decoded?.type === DataType.MicECurrent).toBe(true);
expect(decoded === null || decoded?.type === DataType.MicE).toBe(true);
});
});
@@ -813,9 +824,9 @@ describe("Frame.decodeMicE", () => {
const decoded = frame.decode() as MicEPayload;
expect(decoded).not.toBeNull();
expect(decoded?.type).toBe(DataType.MicECurrent);
expect(decoded?.type).toBe(DataType.MicE);
if (decoded && decoded.type === DataType.MicECurrent) {
if (decoded && decoded.type === DataType.MicE) {
expect(decoded.position.latitude).toBeDefined();
expect(decoded.position.longitude).toBeDefined();
expect(decoded.position.symbol).toBeDefined();