align structure more with underlying chunk layout

This commit is contained in:
2025-01-06 21:49:08 -06:00
parent acac519e9a
commit 6580f66e91
35 changed files with 594 additions and 372 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ decoder.decode(infoPacketBuffer);
decoder.decode(dataPacketBuffer);
// the system_name from info packets if available
console.log(decoder.system_name);
console.log(decoder.systemName);
// map of trackers populated with any data that has been received
// this merges both info and data packet properties into one
+7 -9
View File
@@ -23,18 +23,18 @@ setInterval(() => {
}
Object.entries(decoder.trackers).forEach(([trackerId, tracker]) => {
const trackerName = decoder.trackers[trackerId]?.tracker_name?.tracker_name;
const trackerName = decoder.trackers[trackerId]?.name;
console.log(`Tracker - id: ${trackerId} | name: ${trackerName || ''}`);
if (tracker.pos) {
console.log(`\tpos: ${tracker.pos.pos_x}, ${tracker.pos.pos_y}, ${tracker.pos.pos_z}`);
console.log(`\tpos: ${tracker.pos.x}, ${tracker.pos.x}, ${tracker.pos.x}`);
}
if (tracker.speed) {
console.log(`\tspeed: ${tracker.speed.speed_x}, ${tracker.speed.speed_y}, ${tracker.speed.speed_z}`);
console.log(`\tspeed: ${tracker.speed.x}, ${tracker.speed.y}, ${tracker.speed.z}`);
}
if (tracker.ori) {
console.log(`\tori: ${tracker.ori.ori_x}, ${tracker.ori.ori_y}, ${tracker.ori.ori_z}`);
console.log(`\tori: ${tracker.ori.x}, ${tracker.ori.y}, ${tracker.ori.z}`);
}
if (tracker.status) {
@@ -42,17 +42,15 @@ setInterval(() => {
}
if (tracker.accel) {
console.log(`\taccel: ${tracker.accel.accel_x}, ${tracker.accel.accel_y}, ${tracker.accel.accel_z}`);
console.log(`\taccel: ${tracker.accel.x}, ${tracker.accel.y}, ${tracker.accel.z}`);
}
if (tracker.trgtpos) {
console.log(
`\ttrgtpos: ${tracker.trgtpos.trgtpos_x}, ${tracker.trgtpos.trgtpos_y}, ${tracker.trgtpos.trgtpos_z}`
);
console.log(`\ttrgtpos: ${tracker.trgtpos.x}, ${tracker.trgtpos.y}, ${tracker.trgtpos.z}`);
}
if (tracker.timestamp) {
console.log(`\ttimestamp: ${tracker.timestamp.tracker_timestamp}`);
console.log(`\ttimestamp: ${tracker.timestamp}`);
}
});
}, 1000);
+5 -13
View File
@@ -1,10 +1,9 @@
// recreation of psn_server.cpp example from https://github.com/vyv/psn-cpp
const dgram = require('dgram');
const { Encoder, Tracker, Decoder } = require('../dist/cjs');
const { Encoder, Tracker } = require('../dist/cjs');
const client = dgram.createSocket('udp4');
const encoder = new Encoder('Test PSN Server', 2, 0);
const decoder = new Decoder();
const trackers = [];
@@ -40,25 +39,18 @@ setInterval(() => {
trackers[index].setTimestamp(timestamp);
});
console.log(`Sending Data Packets`);
const dataPackets = encoder.getDataPackets(timestamp, trackers);
dataPackets.forEach((packet, index) => {
const decodedPacket = decoder.decode(packet);
console.log(
`Sending PSN_DATA_PACKET : Frame Id = ${decodedPacket.packet_header.frame_id} , Packet Count = ${index + 1}`
);
dataPackets.forEach((packet) => {
client.send(packet, 56565, '236.10.10.10');
});
timestamp += 1;
}, 5);
setInterval(() => {
console.log(`Sending Info Packets`);
const infoPackets = encoder.getInfoPackets(timestamp, trackers);
infoPackets.forEach((packet, index) => {
const decodedPacket = decoder.decode(packet);
console.log(
`Sending PSN_INFO_PACKET : Frame Id = ${decodedPacket.packet_header.frame_id} , Packet Count = ${index + 1}`
);
infoPackets.forEach((packet) => {
client.send(packet, 56565, '236.10.10.10');
});
}, 500);
+51 -61
View File
@@ -1,28 +1,32 @@
import { Decoders } from '.';
import { DataPacketChunk, InfoPacketChunk, InfoTrackerChunk } from './models';
import { DataPacketChunk, InfoPacketChunk, Tracker, TrackerFromData, TrackerFromInfo } from './models';
export class Decoder {
infoPacketFrames: { [key: number]: InfoPacketChunk[] } = {};
dataPacketFrames: { [key: number]: DataPacketChunk[] } = {};
trackers: { [key: string]: InfoTrackerChunk } = {};
trackers: { [key: string]: Tracker } = {};
system_name: string = '';
systemName: string = '';
constructor() {}
updateInfo(framePackets: InfoPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.system_name) {
this.system_name = packet.system_name.system_name;
if (packet.data.systemName?.data) {
this.systemName = packet.data.systemName?.data.systemName;
}
if (packet.tracker_list?.trackers) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
if (!this.trackers[trackerId]) {
this.trackers[trackerId] = {};
if (packet.data.trackerList?.data) {
packet.data.trackerList.data.trackers.forEach((infoTrackerChunk) => {
if (!this.trackers[infoTrackerChunk.chunk.header.id]) {
const tracker = TrackerFromInfo(infoTrackerChunk);
if (tracker) {
this.trackers[infoTrackerChunk.chunk.header.id] = tracker;
}
}
this.trackers[trackerId].tracker_name = tracker.tracker_name;
const tracker = this.trackers[infoTrackerChunk.chunk.header.id];
tracker.updateInfo(infoTrackerChunk);
});
}
});
@@ -30,94 +34,80 @@ export class Decoder {
updateData(framePackets: DataPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.tracker_list) {
Object.entries(packet.tracker_list.trackers).forEach(([trackerId, tracker]) => {
this.trackers[trackerId] = {
...this.trackers[trackerId],
...tracker,
};
if (packet.data.trackerList) {
packet.data.trackerList?.data.trackers.forEach((dataTrackerChunk) => {
if (!this.trackers[dataTrackerChunk.chunk.header.id]) {
const tracker = TrackerFromData(dataTrackerChunk);
if (tracker) {
this.trackers[dataTrackerChunk.chunk.header.id] = tracker;
}
}
const tracker = this.trackers[dataTrackerChunk.chunk.header.id];
tracker.updateData(dataTrackerChunk);
});
}
});
}
// TODO(jwetzell): add invalid frame id decoding. Scenario where a frame id is reused before one is complete
decode(packetBuf: Uint8Array): DataPacketChunk | InfoPacketChunk | undefined {
const packet = Decoders.Packet(packetBuf);
decode(packetBuf: Uint8Array) {
const packet = Decoders.Chunk(packetBuf);
if (packet === undefined) {
return packet;
}
if (packet.id === 0x6756) {
const infoPacket = packet as InfoPacketChunk;
if (infoPacket && infoPacket.has_subchunks) {
const currentInfoPacketHeader = infoPacket.packet_header;
if (packet.header.id === 0x6756) {
const infoPacket = Decoders.InfoPacketChunk(packetBuf);
if (infoPacket && infoPacket.chunk.header.hasSubchunks) {
const currentInfoPacketHeader = infoPacket.data.packetHeader;
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
const systemSubChunk = infoPacket.system_name;
const systemSubChunk = infoPacket.data.systemName;
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
throw new Error('malformed packet');
}
if (currentInfoPacketHeader.frame_id) {
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
if (currentInfoPacketHeader.data.frameId) {
if (this.infoPacketFrames[currentInfoPacketHeader.data.frameId] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.data.frameId] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
this.infoPacketFrames[currentInfoPacketHeader.data.frameId].push(infoPacket);
if (
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
currentInfoPacketHeader.frame_packet_count
this.infoPacketFrames[currentInfoPacketHeader.data.frameId].length ===
currentInfoPacketHeader.data.framePacketCount
) {
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.data.frameId]);
delete this.infoPacketFrames[currentInfoPacketHeader.data.frameId];
}
}
}
} else if (packet.id === 0x6755) {
const dataPacket = packet as DataPacketChunk;
if (dataPacket.has_subchunks) {
const currentDataPacketHeader = dataPacket.packet_header;
} else if (packet.header.id === 0x6755) {
const dataPacket = Decoders.DataPacketChunk(packetBuf);
if (dataPacket.chunk.header.hasSubchunks) {
const currentDataPacketHeader = dataPacket.data.packetHeader;
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
if (currentDataPacketHeader.frame_id) {
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
if (currentDataPacketHeader.data.frameId) {
if (this.dataPacketFrames[currentDataPacketHeader.data.frameId] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.data.frameId] = [];
}
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
this.dataPacketFrames[currentDataPacketHeader.data.frameId].push(dataPacket);
if (
this.dataPacketFrames[currentDataPacketHeader.frame_id].length ===
currentDataPacketHeader.frame_packet_count
this.dataPacketFrames[currentDataPacketHeader.data.frameId].length ===
currentDataPacketHeader.data.framePacketCount
) {
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
this.updateData(this.dataPacketFrames[currentDataPacketHeader.data.frameId]);
delete this.dataPacketFrames[currentDataPacketHeader.data.frameId];
}
}
}
}
return packet;
}
// NOTE(jwetzell): gets merged set of tracker field keys minus the guaranteed properties
getTrackerFields(): Set<string> {
const keys = new Set(
Object.values(this.trackers)
.map((tracker) => Object.keys(tracker))
.flat(1)
);
// NOTE(jwetzell): remove fields that definitely exist
keys.delete('id');
keys.delete('has_subchunks');
keys.delete('data_len');
keys.delete('chunk_data');
return keys;
}
}
+17 -7
View File
@@ -1,19 +1,29 @@
import { Chunk } from '../models/chunk';
export default (buffer: Uint8Array, dataDecoder: Function): Chunk => {
export default (buffer: Uint8Array): Chunk => {
let offset = 0;
const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
const chunk: Chunk = {};
chunk.id = view.getUint16(offset, true);
const id = view.getUint16(offset, true);
offset += 2;
// NOTE(jwetzell): this data is split up as 1 bit for has_subchunks and 15 bits for the data_len
const combinedLengthAndFlag = view.getUint16(offset, true);
chunk.has_subchunks = combinedLengthAndFlag > 32768;
chunk.data_len = chunk.has_subchunks ? combinedLengthAndFlag - 32768 : combinedLengthAndFlag;
const hasSubchunks = combinedLengthAndFlag > 32768;
const dataLen = hasSubchunks ? combinedLengthAndFlag - 32768 : combinedLengthAndFlag;
offset += 2;
chunk.chunk_data = buffer.slice(offset, offset + chunk.data_len);
dataDecoder(chunk);
const header = {
id,
dataLen,
hasSubchunks,
};
const chunkData = buffer.subarray(offset, offset + header.dataLen);
const chunk = {
chunkData,
header,
};
return chunk;
};
+20 -18
View File
@@ -1,30 +1,30 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { DataPacketChunk } from '../../models/data/data-packet-chunk';
import { DataPacketChunk, DataPacketChunkData } from '../../models/data/data-packet-chunk';
function decodeDataPacketChunk(dataPacketChunk: DataPacketChunk) {
if (dataPacketChunk.has_subchunks && dataPacketChunk.chunk_data && dataPacketChunk.data_len) {
export default (buffer: Uint8Array): DataPacketChunk => {
const chunk = Decoders.Chunk(buffer);
const data: DataPacketChunkData = {};
if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) {
let offset = 0;
while (offset < dataPacketChunk.data_len) {
const view = new DataView(
dataPacketChunk.chunk_data.buffer,
dataPacketChunk.chunk_data.byteOffset,
dataPacketChunk.chunk_data.byteLength
);
while (offset < chunk.header.dataLen) {
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const chunkId = view.getUint16(offset, true);
switch (chunkId) {
case 0x0000:
dataPacketChunk.packet_header = Decoders.PacketHeaderChunk(dataPacketChunk.chunk_data.slice(offset));
data.packetHeader = Decoders.PacketHeaderChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (dataPacketChunk.packet_header?.data_len) {
offset += dataPacketChunk.packet_header.data_len;
if (data.packetHeader.chunk.header.dataLen) {
offset += data.packetHeader.chunk.header.dataLen;
}
break;
case 0x0001:
dataPacketChunk.tracker_list = Decoders.DataTrackerListChunk(dataPacketChunk.chunk_data.slice(offset));
data.trackerList = Decoders.DataTrackerListChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (dataPacketChunk.tracker_list.data_len) {
offset += dataPacketChunk.tracker_list.data_len;
if (data.trackerList.chunk.header.dataLen) {
offset += data.trackerList.chunk.header.dataLen;
}
break;
default:
@@ -32,7 +32,9 @@ function decodeDataPacketChunk(dataPacketChunk: DataPacketChunk) {
}
}
}
}
export default (buffer: Uint8Array): DataPacketChunk =>
Decoders.Chunk(buffer, decodeDataPacketChunk) as DataPacketChunk;
return {
chunk,
data,
};
};
+38 -35
View File
@@ -1,43 +1,46 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { DataTrackerChunk } from '../../models/data/data-tracker-chunk';
import { DataTrackerChunk, DataTrackerChunkData } from '../../models/data/data-tracker-chunk';
function decodeTrackerChunk(trackerChunk: DataTrackerChunk) {
if (trackerChunk.chunk_data && trackerChunk.data_len) {
export default (buffer: Uint8Array): DataTrackerChunk => {
const chunk = Decoders.Chunk(buffer);
const data: DataTrackerChunkData = {};
if (chunk.chunkData && chunk.header.dataLen) {
let offset = 0;
while (offset < trackerChunk.data_len) {
const trackerFieldChunk = Decoders.DataTrackerFieldChunk(trackerChunk.chunk_data.slice(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerFieldChunk.data_len) {
offset += trackerFieldChunk.data_len;
switch (trackerFieldChunk.id) {
case 0x0000:
trackerChunk.pos = trackerFieldChunk;
break;
case 0x0001:
trackerChunk.speed = trackerFieldChunk;
break;
case 0x0002:
trackerChunk.ori = trackerFieldChunk;
break;
case 0x0003:
trackerChunk.status = trackerFieldChunk;
break;
case 0x0004:
trackerChunk.accel = trackerFieldChunk;
break;
case 0x0005:
trackerChunk.trgtpos = trackerFieldChunk;
break;
case 0x0006:
trackerChunk.timestamp = trackerFieldChunk;
break;
default:
break;
}
while (offset < chunk.header.dataLen) {
const trackerFieldChunk = Decoders.Chunk(chunk.chunkData.subarray(offset));
switch (trackerFieldChunk.header.id) {
case 0x0000:
data.pos = Decoders.DataTrackerXYZChunk(chunk.chunkData.subarray(offset));
break;
case 0x0001:
data.speed = Decoders.DataTrackerXYZChunk(chunk.chunkData.subarray(offset));
break;
case 0x0002:
data.ori = Decoders.DataTrackerXYZChunk(chunk.chunkData.subarray(offset));
break;
case 0x0003:
data.status = Decoders.DataTrackerStatusChunk(chunk.chunkData.subarray(offset));
break;
case 0x0004:
data.accel = Decoders.DataTrackerXYZChunk(chunk.chunkData.subarray(offset));
break;
case 0x0005:
data.trgtpos = Decoders.DataTrackerXYZChunk(chunk.chunkData.subarray(offset));
break;
case 0x0006:
data.timestamp = Decoders.DataTrackerTimestampChunk(chunk.chunkData.subarray(offset));
break;
default:
break;
}
offset += Constants.CHUNK_HEADER_SIZE;
offset += trackerFieldChunk.header.dataLen;
}
}
}
export default (buffer: Uint8Array): DataTrackerChunk => Decoders.Chunk(buffer, decodeTrackerChunk) as DataTrackerChunk;
return {
chunk,
data,
};
};
@@ -1,63 +0,0 @@
/* eslint-disable no-case-declarations */
import { Decoders } from '..';
import { DataTrackerFieldChunk } from '../../models/data/data-tracker-field-chunk';
function readXYZ(trackerFieldChunk: DataTrackerFieldChunk, prefix: string = '') {
if (trackerFieldChunk.chunk_data) {
const view = new DataView(
trackerFieldChunk.chunk_data.buffer,
trackerFieldChunk.chunk_data.byteOffset,
trackerFieldChunk.chunk_data.byteLength
);
trackerFieldChunk[`${prefix}x`] = view.getFloat32(0, true);
trackerFieldChunk[`${prefix}y`] = view.getFloat32(4, true);
trackerFieldChunk[`${prefix}z`] = view.getFloat32(8, true);
}
}
function decodeTrackerFieldChunk(trackerFieldChunk: DataTrackerFieldChunk) {
if (trackerFieldChunk.id !== undefined) {
switch (trackerFieldChunk.id) {
case 0x0000:
readXYZ(trackerFieldChunk, 'pos_');
break;
case 0x0001:
readXYZ(trackerFieldChunk, 'speed_');
break;
case 0x0002:
readXYZ(trackerFieldChunk, 'ori_');
break;
case 0x0003:
if (trackerFieldChunk.chunk_data) {
const view = new DataView(
trackerFieldChunk.chunk_data.buffer,
trackerFieldChunk.chunk_data.byteOffset,
trackerFieldChunk.chunk_data.byteLength
);
trackerFieldChunk.validity = view.getFloat32(0, true);
}
break;
case 0x0004:
readXYZ(trackerFieldChunk, 'accel_');
break;
case 0x0005:
readXYZ(trackerFieldChunk, 'trgtpos_');
break;
case 0x0006:
if (trackerFieldChunk.chunk_data) {
const view = new DataView(
trackerFieldChunk.chunk_data.buffer,
trackerFieldChunk.chunk_data.byteOffset,
trackerFieldChunk.chunk_data.byteLength
);
trackerFieldChunk.tracker_timestamp = view.getBigUint64(0, true);
}
break;
default:
break;
}
}
}
export default (buffer: Uint8Array): DataTrackerFieldChunk =>
Decoders.Chunk(buffer, decodeTrackerFieldChunk) as DataTrackerFieldChunk;
+23 -14
View File
@@ -1,24 +1,33 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { DataTrackerListChunk } from '../../models/data/data-tracker-list-chunk';
import { DataTrackerChunk } from '../../models';
import { DataTrackerListChunk, DataTrackerListChunkData } from '../../models/data/data-tracker-list-chunk';
function decodeDataTrackerListChunk(dataTrackerListChunk: DataTrackerListChunk) {
dataTrackerListChunk.trackers = {};
export default (buffer: Uint8Array): DataTrackerListChunk => {
const chunk = Decoders.Chunk(buffer);
const trackers: DataTrackerChunk[] = [];
// TODO(jwetzell): add error handling
let offset = 0;
if (dataTrackerListChunk.chunk_data) {
while (offset < dataTrackerListChunk.chunk_data.length) {
const trackerChunk = Decoders.DataTrackerChunk(dataTrackerListChunk.chunk_data.slice(offset));
if (chunk.chunkData) {
let offset = 0;
while (offset < chunk.chunkData.length) {
const trackerChunk = Decoders.DataTrackerChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerChunk.data_len) {
offset += trackerChunk.data_len;
if (trackerChunk.chunk.header.dataLen) {
offset += trackerChunk.chunk.header.dataLen;
}
if (trackerChunk.id !== undefined) {
dataTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
if (trackerChunk.chunk.header.id !== undefined) {
trackers.push(trackerChunk);
}
}
}
}
export default (buffer: Uint8Array): DataTrackerListChunk =>
Decoders.Chunk(buffer, decodeDataTrackerListChunk) as DataTrackerListChunk;
const data: DataTrackerListChunkData = {
trackers,
};
return {
chunk,
data,
};
};
@@ -0,0 +1,20 @@
/* eslint-disable no-case-declarations */
import { Decoders } from '..';
import { DataTrackerStatusChunk, DataTrackerStatusChunkData } from '../../models/data/data-tracker-status-chunk';
export default (buffer: Uint8Array): DataTrackerStatusChunk => {
const chunk = Decoders.Chunk(buffer);
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const validity = view.getFloat32(0, true);
const data: DataTrackerStatusChunkData = {
validity,
};
return {
chunk,
data,
};
};
@@ -0,0 +1,23 @@
/* eslint-disable no-case-declarations */
import { Decoders } from '..';
import {
DataTrackerTimestampChunk,
DataTrackerTimestampChunkData,
} from '../../models/data/data-tracker-timestamp-chunk';
export default (buffer: Uint8Array): DataTrackerTimestampChunk => {
const chunk = Decoders.Chunk(buffer);
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const timestamp = view.getBigUint64(0, true);
const data: DataTrackerTimestampChunkData = {
timestamp,
};
return {
chunk,
data,
};
};
@@ -0,0 +1,20 @@
import { Decoders } from '..';
import { DataTrackerXYZChunk, DataTrackerXYZChunkData } from '../../models/data/data-tracker-xyz-chunk';
export default (buffer: Uint8Array): DataTrackerXYZChunk => {
const chunk = Decoders.Chunk(buffer);
// TODO(jwetzell): add error handling
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const data: DataTrackerXYZChunkData = {
x: view.getFloat32(0, true),
y: view.getFloat32(4, true),
z: view.getFloat32(8, true),
};
return {
chunk,
data,
};
};
+6 -2
View File
@@ -4,8 +4,10 @@ import PacketHeaderChunk from './packet-header-chunk';
import DataPacketChunk from './data/data-packet-chunk';
import DataTrackerChunk from './data/data-tracker-chunk';
import DataTrackerFieldChunk from './data/data-tracker-field-chunk';
import DataTrackerListChunk from './data/data-tracker-list-chunk';
import DataTrackerStatusChunk from './data/data-tracker-status-chunk';
import DataTrackerTimestampChunk from './data/data-tracker-timestamp-chunk';
import DataTrackerXYZChunk from './data/data-tracker-xyz-chunk';
import InfoPacketChunk from './info/info-packet-chunk';
import InfoSystemNameChunk from './info/info-system-name-chunk';
@@ -19,7 +21,9 @@ export const Decoders = {
Packet,
DataPacketChunk,
DataTrackerChunk,
DataTrackerFieldChunk,
DataTrackerXYZChunk,
DataTrackerStatusChunk,
DataTrackerTimestampChunk,
DataTrackerListChunk,
InfoPacketChunk,
InfoSystemNameChunk,
+23 -21
View File
@@ -1,37 +1,37 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { InfoPacketChunk } from '../../models/info/info-packet-chunk';
import { InfoPacketChunk, InfoPacketChunkData } from '../../models/info/info-packet-chunk';
function decodeInfoPacketChunk(infoPacketChunk: InfoPacketChunk) {
if (infoPacketChunk.has_subchunks && infoPacketChunk.chunk_data && infoPacketChunk.data_len) {
export default (buffer: Uint8Array): InfoPacketChunk => {
const chunk = Decoders.Chunk(buffer);
const data: InfoPacketChunkData = {};
if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) {
let offset = 0;
while (offset < infoPacketChunk.data_len) {
const view = new DataView(
infoPacketChunk.chunk_data.buffer,
infoPacketChunk.chunk_data.byteOffset,
infoPacketChunk.chunk_data.byteLength
);
while (offset < chunk.header.dataLen) {
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const chunkId = view.getUint16(offset, true);
switch (chunkId) {
case 0x0000:
infoPacketChunk.packet_header = Decoders.PacketHeaderChunk(infoPacketChunk.chunk_data.slice(offset));
data.packetHeader = Decoders.PacketHeaderChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.packet_header?.data_len) {
offset += infoPacketChunk.packet_header.data_len;
if (data.packetHeader?.chunk.header.dataLen) {
offset += data.packetHeader.chunk.header.dataLen;
}
break;
case 0x0001:
infoPacketChunk.system_name = Decoders.InfoSystemNameChunk(infoPacketChunk.chunk_data.slice(offset));
data.systemName = Decoders.InfoSystemNameChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.system_name?.data_len) {
offset += infoPacketChunk.system_name.data_len;
if (data.systemName?.chunk.header.dataLen) {
offset += data.systemName?.chunk.header.dataLen;
}
break;
case 0x0002:
infoPacketChunk.tracker_list = Decoders.InfoTrackerListChunk(infoPacketChunk.chunk_data.slice(offset));
data.trackerList = Decoders.InfoTrackerListChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (infoPacketChunk.tracker_list?.data_len) {
offset += infoPacketChunk.tracker_list.data_len;
if (data.trackerList?.chunk.header.dataLen) {
offset += data.trackerList.chunk.header.dataLen;
}
break;
default:
@@ -39,7 +39,9 @@ function decodeInfoPacketChunk(infoPacketChunk: InfoPacketChunk) {
}
}
}
}
export default (buffer: Uint8Array): InfoPacketChunk =>
Decoders.Chunk(buffer, decodeInfoPacketChunk) as InfoPacketChunk;
return {
chunk,
data,
};
};
+12 -10
View File
@@ -1,13 +1,15 @@
import { Decoders } from '..';
import { InfoSystemNameChunk } from '../../models/info/info-system-name-chunk';
import { InfoSystemNameChunk, InfoSystemNameChunkData } from '../../models/info/info-system-name-chunk';
function decodeSystemNameChunk(systemNameChunk: InfoSystemNameChunk) {
if (systemNameChunk.chunk_data !== undefined && systemNameChunk.data_len !== undefined) {
systemNameChunk.system_name = new TextDecoder().decode(
systemNameChunk.chunk_data.slice(0, systemNameChunk.data_len)
);
}
}
export default (buffer: Uint8Array): InfoSystemNameChunk => {
const chunk = Decoders.Chunk(buffer);
export default (buffer: Uint8Array): InfoSystemNameChunk =>
Decoders.Chunk(buffer, decodeSystemNameChunk) as InfoSystemNameChunk;
const data: InfoSystemNameChunkData = {
systemName: new TextDecoder().decode(chunk.chunkData.subarray(0, chunk.header.dataLen)),
};
return {
chunk,
data,
};
};
+20 -15
View File
@@ -1,31 +1,36 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { InfoTrackerChunk } from '../../models/info/info-tracker-chunk';
import { InfoTrackerChunk, InfoTrackerChunkData } from '../../models/info/info-tracker-chunk';
function decodeInfoTrackerChunk(infoTrackerChunk: InfoTrackerChunk) {
if (infoTrackerChunk.has_subchunks && infoTrackerChunk.chunk_data && infoTrackerChunk.data_len) {
export default (buffer: Uint8Array): InfoTrackerChunk => {
const chunk = Decoders.Chunk(buffer);
if (chunk.header.hasSubchunks && chunk.chunkData && chunk.header.dataLen) {
let offset = 0;
while (offset < infoTrackerChunk.data_len) {
const view = new DataView(
infoTrackerChunk.chunk_data.buffer,
infoTrackerChunk.chunk_data.byteOffset,
infoTrackerChunk.chunk_data.byteLength
);
while (offset < chunk.header.dataLen) {
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const chunkId = view.getUint16(offset, true);
switch (chunkId) {
case 0x0000:
infoTrackerChunk.tracker_name = Decoders.InfoTrackerNameChunk(infoTrackerChunk.chunk_data.slice(offset));
const data: InfoTrackerChunkData = {
trackerName: Decoders.InfoTrackerNameChunk(chunk.chunkData.subarray(offset)),
};
offset += Constants.CHUNK_HEADER_SIZE;
if (infoTrackerChunk.tracker_name?.data_len) {
offset += infoTrackerChunk?.tracker_name?.data_len;
if (data.trackerName.chunk.header.dataLen) {
offset += data.trackerName.chunk.header.dataLen;
}
break;
return {
chunk,
data,
};
default:
break;
}
}
}
}
export default (buffer: Uint8Array): InfoTrackerChunk => Decoders.Chunk(buffer, decodeInfoTrackerChunk);
return {
chunk,
};
};
+23 -14
View File
@@ -1,24 +1,33 @@
import { Decoders } from '..';
import { Constants } from '../../constants';
import { InfoTrackerListChunk } from '../../models/info/info-tracker-list-chunk';
import { InfoTrackerChunk } from '../../models/info/info-tracker-chunk';
import { InfoTrackerListChunk, InfoTrackerListChunkData } from '../../models/info/info-tracker-list-chunk';
function decodeInfoTrackerListChunk(infoTrackerListChunk: InfoTrackerListChunk) {
infoTrackerListChunk.trackers = {};
if (infoTrackerListChunk.has_subchunks && infoTrackerListChunk.chunk_data) {
export default (buffer: Uint8Array): InfoTrackerListChunk => {
const chunk = Decoders.Chunk(buffer);
const trackers: InfoTrackerChunk[] = [];
if (chunk.header.hasSubchunks && chunk.chunkData) {
let offset = 0;
if (infoTrackerListChunk.data_len) {
while (offset < infoTrackerListChunk.data_len) {
const trackerChunk = Decoders.InfoTrackerChunk(infoTrackerListChunk.chunk_data.slice(offset));
if (chunk.header.dataLen) {
while (offset < chunk.header.dataLen) {
const trackerChunk = Decoders.InfoTrackerChunk(chunk.chunkData.subarray(offset));
offset += Constants.CHUNK_HEADER_SIZE;
if (trackerChunk.data_len) {
offset += trackerChunk.data_len;
if (trackerChunk.chunk.header.dataLen) {
offset += trackerChunk.chunk.header.dataLen;
}
if (trackerChunk.id !== undefined) {
infoTrackerListChunk.trackers[trackerChunk.id] = trackerChunk;
if (trackerChunk.chunk.header.id !== undefined) {
trackers.push(trackerChunk);
}
}
}
}
}
export default (buffer: Uint8Array): InfoTrackerListChunk =>
Decoders.Chunk(buffer, decodeInfoTrackerListChunk) as InfoTrackerListChunk;
const data: InfoTrackerListChunkData = {
trackers,
};
return {
chunk,
data,
};
};
+13 -10
View File
@@ -1,12 +1,15 @@
import { Decoders } from '..';
import { InfoTrackerNameChunk } from '../../models/info/info-tracker-name-chunk';
import { InfoTrackerNameChunk, InfoTrackerNameChunkData } from '../../models/info/info-tracker-name-chunk';
function decodeTrackerNameChunk(trackerNameChunk: InfoTrackerNameChunk) {
if (trackerNameChunk.chunk_data !== undefined && trackerNameChunk.data_len !== undefined) {
trackerNameChunk.tracker_name = new TextDecoder().decode(
trackerNameChunk.chunk_data.slice(0, trackerNameChunk.data_len)
);
}
}
export default (buffer: Uint8Array): InfoTrackerNameChunk =>
Decoders.Chunk(buffer, decodeTrackerNameChunk) as InfoTrackerNameChunk;
export default (buffer: Uint8Array): InfoTrackerNameChunk => {
const chunk = Decoders.Chunk(buffer);
const data: InfoTrackerNameChunkData = {
trackerName: new TextDecoder().decode(chunk.chunkData.subarray(0, chunk.header.dataLen)),
};
return {
chunk,
data,
};
};
+16 -17
View File
@@ -1,21 +1,20 @@
import { Decoders } from '.';
import { PacketHeaderChunk } from '../models/packet-header-chunk';
function decodePacketHeaderChunk(packetHeaderChunk: PacketHeaderChunk) {
if (packetHeaderChunk.chunk_data) {
const view = new DataView(
packetHeaderChunk.chunk_data.buffer,
packetHeaderChunk.chunk_data.byteOffset,
packetHeaderChunk.chunk_data.byteLength
);
packetHeaderChunk.packet_timestamp = view.getBigUint64(0, true);
packetHeaderChunk.version_high = view.getUint8(8);
packetHeaderChunk.version_low = view.getUint8(9);
packetHeaderChunk.frame_id = view.getUint8(10);
packetHeaderChunk.frame_packet_count = view.getUint8(11);
}
}
import { PacketHeaderChunk, PacketHeaderChunkData } from '../models/packet-header-chunk';
export default (buffer: Uint8Array): PacketHeaderChunk => {
return Decoders.Chunk(buffer, decodePacketHeaderChunk);
const chunk = Decoders.Chunk(buffer);
const view = new DataView(chunk.chunkData.buffer, chunk.chunkData.byteOffset, chunk.chunkData.byteLength);
const data: PacketHeaderChunkData = {
packetTimestamp: view.getBigUint64(0, true),
versionHigh: chunk.chunkData[8],
versionLow: chunk.chunkData[9],
frameId: chunk.chunkData[10],
framePacketCount: chunk.chunkData[11],
};
return {
chunk,
data,
};
};
+9 -5
View File
@@ -1,6 +1,10 @@
export interface Chunk {
id?: number;
has_subchunks?: boolean;
data_len?: number;
chunk_data?: Uint8Array;
export interface ChunkHeader {
id: number;
dataLen: number;
hasSubchunks: boolean;
}
export interface Chunk {
chunkData: Uint8Array;
header: ChunkHeader;
}
+8 -3
View File
@@ -2,7 +2,12 @@ import { Chunk } from '../chunk';
import { PacketHeaderChunk } from '../packet-header-chunk';
import { DataTrackerListChunk } from './data-tracker-list-chunk';
export interface DataPacketChunk extends Chunk {
packet_header: PacketHeaderChunk;
tracker_list: DataTrackerListChunk;
export interface DataPacketChunkData {
packetHeader?: PacketHeaderChunk;
trackerList?: DataTrackerListChunk;
}
export interface DataPacketChunk {
chunk: Chunk;
data: DataPacketChunkData;
}
+16 -9
View File
@@ -1,12 +1,19 @@
import { Chunk } from '../chunk';
import { DataTrackerFieldChunk } from './data-tracker-field-chunk';
import { DataTrackerStatusChunk } from './data-tracker-status-chunk';
import { DataTrackerTimestampChunk } from './data-tracker-timestamp-chunk';
import { DataTrackerXYZChunk } from './data-tracker-xyz-chunk';
export interface DataTrackerChunk extends Chunk {
pos?: DataTrackerFieldChunk;
speed?: DataTrackerFieldChunk;
ori?: DataTrackerFieldChunk;
status?: DataTrackerFieldChunk;
accel?: DataTrackerFieldChunk;
trgtpos?: DataTrackerFieldChunk;
timestamp?: DataTrackerFieldChunk;
export interface DataTrackerChunkData {
pos?: DataTrackerXYZChunk;
speed?: DataTrackerXYZChunk;
ori?: DataTrackerXYZChunk;
status?: DataTrackerStatusChunk;
accel?: DataTrackerXYZChunk;
trgtpos?: DataTrackerXYZChunk;
timestamp?: DataTrackerTimestampChunk;
}
export interface DataTrackerChunk {
chunk: Chunk;
data: DataTrackerChunkData;
}
@@ -1,5 +0,0 @@
import { Chunk } from '../chunk';
export interface DataTrackerFieldChunk extends Chunk {
[key: string]: number | bigint | number[] | Uint8Array | undefined | boolean;
}
+7 -4
View File
@@ -1,8 +1,11 @@
import { Chunk } from '../chunk';
import { DataTrackerChunk } from './data-tracker-chunk';
export interface DataTrackerListChunk extends Chunk {
trackers: {
[key: number]: DataTrackerChunk;
};
export interface DataTrackerListChunkData {
trackers: DataTrackerChunk[];
}
export interface DataTrackerListChunk {
chunk: Chunk;
data: DataTrackerListChunkData;
}
@@ -0,0 +1,10 @@
import { Chunk } from '../chunk';
export interface DataTrackerStatusChunkData {
validity: number;
}
export interface DataTrackerStatusChunk {
chunk: Chunk;
data: DataTrackerStatusChunkData;
}
@@ -0,0 +1,10 @@
import { Chunk } from '../chunk';
export interface DataTrackerTimestampChunkData {
timestamp: bigint;
}
export interface DataTrackerTimestampChunk {
chunk: Chunk;
data: DataTrackerTimestampChunkData;
}
+12
View File
@@ -0,0 +1,12 @@
import { Chunk } from '../chunk';
export interface DataTrackerXYZChunkData {
x: number;
y: number;
z: number;
}
export interface DataTrackerXYZChunk {
chunk: Chunk;
data: DataTrackerXYZChunkData;
}
-1
View File
@@ -1,7 +1,6 @@
export * from './chunk';
export * from './data/data-packet-chunk';
export * from './data/data-tracker-chunk';
export * from './data/data-tracker-field-chunk';
export * from './data/data-tracker-list-chunk';
export * from './info/info-packet-chunk';
export * from './info/info-system-name-chunk';
+8 -4
View File
@@ -3,8 +3,12 @@ import { PacketHeaderChunk } from '../packet-header-chunk';
import { InfoSystemNameChunk } from './info-system-name-chunk';
import { InfoTrackerListChunk } from './info-tracker-list-chunk';
export interface InfoPacketChunk extends Chunk {
packet_header: PacketHeaderChunk;
system_name: InfoSystemNameChunk;
tracker_list: InfoTrackerListChunk;
export interface InfoPacketChunkData {
packetHeader?: PacketHeaderChunk;
systemName?: InfoSystemNameChunk;
trackerList?: InfoTrackerListChunk;
}
export interface InfoPacketChunk {
chunk: Chunk;
data: InfoPacketChunkData;
}
+7 -2
View File
@@ -1,5 +1,10 @@
import { Chunk } from '../chunk';
export interface InfoSystemNameChunk extends Chunk {
system_name: string;
export interface InfoSystemNameChunkData {
systemName: string;
}
export interface InfoSystemNameChunk {
chunk: Chunk;
data: InfoSystemNameChunkData;
}
+7 -2
View File
@@ -1,6 +1,11 @@
import { Chunk } from '../chunk';
import { InfoTrackerNameChunk } from './info-tracker-name-chunk';
export interface InfoTrackerChunk extends Chunk {
tracker_name?: InfoTrackerNameChunk;
export interface InfoTrackerChunkData {
trackerName: InfoTrackerNameChunk;
}
export interface InfoTrackerChunk {
chunk: Chunk;
data?: InfoTrackerChunkData;
}
+7 -4
View File
@@ -1,8 +1,11 @@
import { Chunk } from '../chunk';
import { InfoTrackerChunk } from './info-tracker-chunk';
export interface InfoTrackerListChunk extends Chunk {
trackers: {
[key: number]: InfoTrackerChunk;
};
export interface InfoTrackerListChunkData {
trackers: InfoTrackerChunk[];
}
export interface InfoTrackerListChunk {
chunk: Chunk;
data: InfoTrackerListChunkData;
}
+7 -2
View File
@@ -1,5 +1,10 @@
import { Chunk } from '../chunk';
export interface InfoTrackerNameChunk extends Chunk {
tracker_name: string;
export interface InfoTrackerNameChunkData {
trackerName: string;
}
export interface InfoTrackerNameChunk {
chunk: Chunk;
data: InfoTrackerNameChunkData;
}
+11 -6
View File
@@ -1,9 +1,14 @@
import { Chunk } from './chunk';
export interface PacketHeaderChunk extends Chunk {
packet_timestamp?: bigint;
version_high?: number;
version_low?: number;
frame_id?: number;
frame_packet_count?: number;
export interface PacketHeaderChunkData {
packetTimestamp: bigint;
versionHigh: number;
versionLow: number;
frameId: number;
framePacketCount: number;
}
export interface PacketHeaderChunk {
chunk: Chunk;
data: PacketHeaderChunkData;
}
+137 -15
View File
@@ -8,16 +8,24 @@ import dataTrackerTimestampChunk from '../encoders/data/data-tracker-timestamp-c
import dataTrackerTrgtposChunk from '../encoders/data/data-tracker-trgtpos-chunk';
import infoTrackerChunk from '../encoders/info/info-tracker-chunk';
import infoTrackerNameChunk from '../encoders/info/info-tracker-name-chunk';
import { DataTrackerChunk } from './data/data-tracker-chunk';
import { InfoTrackerChunk } from './info/info-tracker-chunk';
export type XYZData = {
x: number;
y: number;
z: number;
};
export class Tracker {
id: number;
name: string;
pos?: [number, number, number];
speed?: [number, number, number];
ori?: [number, number, number];
pos?: XYZData;
speed?: XYZData;
ori?: XYZData;
validity?: number;
accel?: [number, number, number];
trgtpos?: [number, number, number];
accel?: XYZData;
trgtpos?: XYZData;
timestamp?: bigint;
constructor(id: number, name: string) {
@@ -33,15 +41,45 @@ export class Tracker {
}
setPos(x: number, y: number, z: number) {
this.pos = [x, y, z];
if (this.pos === undefined) {
this.pos = {
x,
y,
z,
};
} else {
this.pos.x = x;
this.pos.y = y;
this.pos.z = z;
}
}
setSpeed(x: number, y: number, z: number) {
this.speed = [x, y, z];
if (this.speed === undefined) {
this.speed = {
x,
y,
z,
};
} else {
this.speed.x = x;
this.speed.y = y;
this.speed.z = z;
}
}
setOri(x: number, y: number, z: number) {
this.ori = [x, y, z];
if (this.ori === undefined) {
this.ori = {
x,
y,
z,
};
} else {
this.ori.x = x;
this.ori.y = y;
this.ori.z = z;
}
}
setStatus(validity: number) {
@@ -49,11 +87,31 @@ export class Tracker {
}
setAccel(x: number, y: number, z: number) {
this.accel = [x, y, z];
if (this.accel === undefined) {
this.accel = {
x,
y,
z,
};
} else {
this.accel.x = x;
this.accel.y = y;
this.accel.z = z;
}
}
setTrgtPos(x: number, y: number, z: number) {
this.trgtpos = [x, y, z];
if (this.trgtpos === undefined) {
this.trgtpos = {
x,
y,
z,
};
} else {
this.trgtpos.x = x;
this.trgtpos.y = y;
this.trgtpos.z = z;
}
}
setTimestamp(timestamp: bigint) {
@@ -64,15 +122,15 @@ export class Tracker {
const fieldChunks: Uint8Array[] = [];
if (this.pos) {
fieldChunks.push(dataTrackerPosChunk(...this.pos));
fieldChunks.push(dataTrackerPosChunk(this.pos.x, this.pos.y, this.pos.z));
}
if (this.speed) {
fieldChunks.push(dataTrackerSpeedChunk(...this.speed));
fieldChunks.push(dataTrackerSpeedChunk(this.speed.x, this.speed.y, this.speed.z));
}
if (this.ori) {
fieldChunks.push(dataTrackerOriChunk(...this.ori));
fieldChunks.push(dataTrackerOriChunk(this.ori.x, this.ori.y, this.ori.z));
}
if (this.validity) {
@@ -80,11 +138,11 @@ export class Tracker {
}
if (this.accel) {
fieldChunks.push(dataTrackerAccelChunk(...this.accel));
fieldChunks.push(dataTrackerAccelChunk(this.accel.x, this.accel.y, this.accel.z));
}
if (this.trgtpos) {
fieldChunks.push(dataTrackerTrgtposChunk(...this.trgtpos));
fieldChunks.push(dataTrackerTrgtposChunk(this.trgtpos.x, this.trgtpos.y, this.trgtpos.z));
}
if (this.timestamp) {
@@ -97,4 +155,68 @@ export class Tracker {
getInfoChunk() {
return infoTrackerChunk(this.id, infoTrackerNameChunk(this.name));
}
updateInfo(infoTrackerChunk: InfoTrackerChunk) {
if (infoTrackerChunk.data) {
this.name = infoTrackerChunk.data.trackerName.data.trackerName;
}
}
updateData(dataTrackerChunk: DataTrackerChunk) {
if (dataTrackerChunk.data.pos) {
this.setPos(dataTrackerChunk.data.pos.data.x, dataTrackerChunk.data.pos.data.y, dataTrackerChunk.data.pos.data.z);
}
if (dataTrackerChunk.data.speed) {
this.setSpeed(
dataTrackerChunk.data.speed.data.x,
dataTrackerChunk.data.speed.data.y,
dataTrackerChunk.data.speed.data.z
);
}
if (dataTrackerChunk.data.ori) {
this.setOri(dataTrackerChunk.data.ori.data.x, dataTrackerChunk.data.ori.data.y, dataTrackerChunk.data.ori.data.z);
}
if (dataTrackerChunk.data.status) {
this.setStatus(dataTrackerChunk.data.status.data.validity);
}
if (dataTrackerChunk.data.accel) {
this.setAccel(
dataTrackerChunk.data.accel.data.x,
dataTrackerChunk.data.accel.data.y,
dataTrackerChunk.data.accel.data.z
);
}
if (dataTrackerChunk.data.trgtpos) {
this.setTrgtPos(
dataTrackerChunk.data.trgtpos.data.x,
dataTrackerChunk.data.trgtpos.data.y,
dataTrackerChunk.data.trgtpos.data.z
);
}
if (dataTrackerChunk.data.timestamp) {
this.setTimestamp(dataTrackerChunk.data.timestamp.data.timestamp);
}
}
}
export function TrackerFromInfo(infoTrackerChunk: InfoTrackerChunk) {
if (infoTrackerChunk.data) {
return new Tracker(infoTrackerChunk.chunk.header.id, infoTrackerChunk.data.trackerName.data.trackerName);
}
return undefined;
}
export function TrackerFromData(dataTrackerChunk: DataTrackerChunk) {
if (dataTrackerChunk.data) {
const tracker = new Tracker(dataTrackerChunk.chunk.header.id, '');
tracker.updateData(dataTrackerChunk);
return tracker;
}
return undefined;
}