ENCODING!! and lots of organization

This commit is contained in:
2023-09-13 14:50:27 -05:00
parent 2ab90d0146
commit fa0160fc54
37 changed files with 651 additions and 222 deletions
+142
View File
@@ -0,0 +1,142 @@
const PacketParser = require('./decoders/packet');
const InfoPacket = require('./models/packets/info-packet');
const DataPacket = require('./models/packets/data-packet');
class Decoder {
lastInfoPacketHeader;
lastDataPacketHeader;
infoPacketFrames = {};
dataPacketFrames = {};
constructor() {
this.info = {
trackers: {},
};
this.data = {
trackers: {},
};
}
updateInfo(framePackets) {
// console.log('info update');
framePackets.forEach((packet) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
// NOTE(jwetzell): system name subChunk
this.info.system_name = subChunk.system_name;
} else if (subChunk.id === 0x0002) {
// console.log(subChunk);
subChunk.trackers?.forEach((tracker) => {
if (this.info.trackers[tracker.id] === undefined) {
this.info.trackers[tracker.id] = {};
}
this.info.trackers[tracker.id].name = tracker.tracker_name.tracker_name;
});
}
});
});
}
updateData(framePackets) {
// console.log('data update');
framePackets.forEach((packet) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
subChunk.trackers?.forEach((tracker) => {
if (this.data.trackers[tracker.id] === undefined) {
this.data.trackers[tracker.id] = {};
}
tracker.fields?.forEach((field) => {
Object.entries(field).forEach(([key, value]) => {
this.data.trackers[tracker.id][key] = value;
});
});
});
}
});
});
}
// TODO(jwetzell): support multiple frame packets this might require some rework of how chunks are assumed
decode(packetBuf) {
const packet = PacketParser.parse(packetBuf);
// console.log(packet);
if (packet.id === 0x6756) {
const infoPacket = new InfoPacket(packet);
if (infoPacket) {
if (infoPacket.subChunks?.length > 0) {
const currentInfoPacketHeader = infoPacket.getHeaderPacket();
// console.log(currentInfoPacketHeader);
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
return;
}
const systemSubChunk = infoPacket.getSystemPacket();
// console.log(systemSubChunk);
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
return;
}
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
if (
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
currentInfoPacketHeader.frame_packet_count
) {
// console.log('found complete info frame');
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
} else {
// TODO(jwetzell): need to compute whether a frame is complete
// console.log('compute frame completion');
}
this.lastInfoPacketHeader = currentInfoPacketHeader;
}
}
} else if (packet.id === 0x6755) {
const dataPacket = new DataPacket(packet);
// console.log(dataPacket);
if (dataPacket) {
if (dataPacket.subChunks?.length > 0) {
const currentDataPacketHeader = dataPacket.getHeaderPacket();
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
console.error('data packet with no header');
return;
}
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
}
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
if (
this.dataPacketFrames[currentDataPacketHeader.frame_id].length ===
currentDataPacketHeader.frame_packet_count
) {
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
} else {
// TODO(jwetzell): need to compute whether a frame is complete
// console.log('compute data frame completion');
}
this.lastDataPacketHeader = currentDataPacketHeader;
}
}
}
}
}
module.exports = Decoder;
+20
View File
@@ -0,0 +1,20 @@
const { Parser } = require('binary-parser');
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.buffer('chunk_data', {
length: 'data_len',
});
@@ -1,6 +1,6 @@
const Parser = require('binary-parser').Parser;
const DataTrackerFieldParser = new Parser()
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
@@ -27,29 +27,3 @@ const DataTrackerFieldParser = new Parser()
0x0006: new Parser().uint64le('tracker_timestamp'),
},
});
const PSN_DATA_TRACKER_PARSER = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return Number.parseInt(binary.substring(1), 2);
},
})
.seek(-2)
.uint16le('has_subchunks', {
formatter: (item) => {
const binary = item.toString(2).padStart(16, '0');
return binary.charAt(0) === '1';
},
})
.buffer('data', {
length: 'data_len',
});
const DataTrackerListParser = new Parser().array('trackers', { type: PSN_DATA_TRACKER_PARSER, readUntil: 'eof' });
module.exports = {
DataTrackerFieldParser,
DataTrackerListParser,
};
@@ -1,6 +1,6 @@
const Parser = require('binary-parser').Parser;
const ChunkParser = new Parser()
const dataTrackerParser = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
@@ -15,19 +15,8 @@ const ChunkParser = new Parser()
return binary.charAt(0) === '1';
},
})
.buffer('chunk_data', {
.buffer('data', {
length: 'data_len',
});
const HeaderParser = new Parser()
.uint64le('packet_timestamp')
.uint8('version_high')
.uint8('version_low')
.uint8('frame_id')
.uint8('frame_packet_count')
.seek(4);
module.exports = {
ChunkParser,
HeaderParser,
};
module.exports = new Parser().array('trackers', { type: dataTrackerParser, readUntil: 'eof' });
+7
View File
@@ -0,0 +1,7 @@
const TrackerListChunk = require('./data-tracker-list-chunk');
const TrackerFieldChunk = require('./data-tracker-field-chunk');
module.exports = {
TrackerFieldChunk,
TrackerListChunk,
};
+17
View File
@@ -0,0 +1,17 @@
const Chunk = require('./chunk');
const PacketHeaderChunk = require('./packet-header-chunk');
const Packet = require('./packet');
const Info = require('./info');
const Data = require('./data');
module.exports = {
Chunk,
PacketHeaderChunk,
Packet,
Info: {
...Info,
},
Data: {
...Data,
},
};
+7
View File
@@ -0,0 +1,7 @@
const SystemNameChunk = require('./info-system-name-chunk');
const TrackerListChunk = require('./info-tracker-list-chunk');
module.exports = {
SystemNameChunk,
TrackerListChunk,
};
+3
View File
@@ -0,0 +1,3 @@
const Parser = require('binary-parser').Parser;
module.exports = new Parser().string('system_name', { greedy: true });
@@ -1,6 +1,6 @@
const Parser = require('binary-parser').Parser;
const PSN_INFO_TRACKER_NAME_PARSER = new Parser()
const infoTrackerNameParser = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
@@ -17,7 +17,7 @@ const PSN_INFO_TRACKER_NAME_PARSER = new Parser()
})
.string('tracker_name', { length: 'data_len' });
const PSN_INFO_TRACKER_PARSER = new Parser()
const infoTrackerParser = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
@@ -33,14 +33,7 @@ const PSN_INFO_TRACKER_PARSER = new Parser()
},
})
.nest('tracker_name', {
type: PSN_INFO_TRACKER_NAME_PARSER,
type: infoTrackerNameParser,
});
const InfoSystemNameParser = new Parser().string('system_name', { greedy: true });
const InfoTrackerListParser = new Parser().array('trackers', { type: PSN_INFO_TRACKER_PARSER, readUntil: 'eof' });
module.exports = {
InfoSystemNameParser,
InfoTrackerListParser,
};
module.exports = new Parser().array('trackers', { type: infoTrackerParser, readUntil: 'eof' });
+9
View File
@@ -0,0 +1,9 @@
const Parser = require('binary-parser').Parser;
module.exports = new Parser()
.uint64le('packet_timestamp')
.uint8('version_high')
.uint8('version_low')
.uint8('frame_id')
.uint8('frame_packet_count')
.seek(4);
+1 -3
View File
@@ -1,6 +1,6 @@
const Parser = require('binary-parser').Parser;
const PSN_PACKET_PARSER = new Parser()
module.exports = new Parser()
.uint16le('id')
.uint16le('data_len', {
formatter: (item) => {
@@ -16,5 +16,3 @@ const PSN_PACKET_PARSER = new Parser()
},
})
.buffer('chunk_data', { readUntil: 'eof' });
module.exports = (buffer) => PSN_PACKET_PARSER.parse(buffer);
+95
View File
@@ -0,0 +1,95 @@
const dataChunk = require('./encoders/data/data-chunk');
const dataTrackerListChunk = require('./encoders/data/data-tracker-list-chunk');
const infoChunk = require('./encoders/info/info-chunk');
const infoSystemNameChunk = require('./encoders/info/info-system-name-chunk');
const infoTrackerListChunk = require('./encoders/info/info-tracker-list-chunk');
const packetHeaderChunk = require('./encoders/packet-header-chunk');
const MAX_TRACKER_LIST_SIZE = 1000;
class Encoder {
constructor(systemName, versionHigh, versionLow) {
this.systemName = systemName;
this.versionHigh = versionHigh;
this.versionLow = versionLow;
this.dataFrameId = 1;
this.infoFrameId = 1;
}
getInfoPackets(timestamp, trackers) {
const trackerChunks = trackers.map((tracker) => tracker.getInfoChunk());
const infoPackets = [];
const trackerChunksLists = [];
let currentTrackerList = [];
let currentTrackerListSize = 0;
trackerChunks.forEach((trackerChunk) => {
if (currentTrackerListSize + trackerChunk.length > MAX_TRACKER_LIST_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentTrackerListSize = 0;
}
currentTrackerList.push(trackerChunk);
currentTrackerListSize += trackerChunk.length;
});
trackerChunksLists.push(currentTrackerList);
const header = packetHeaderChunk(
timestamp,
this.versionHigh,
this.versionLow,
this.infoFrameId,
trackerChunksLists.length
);
trackerChunksLists.forEach((trackerChunkList) => {
infoPackets.push(infoChunk(header, infoSystemNameChunk(this.systemName), infoTrackerListChunk(trackerChunkList)));
});
this.dataFrameId += 1;
if (this.dataFrameId > 255) {
this.dataFrameId = 0;
}
return infoPackets;
}
getDataPackets(timestamp, trackers) {
const allTrackerChunks = trackers.map((tracker) => tracker.getDataChunk());
const dataPackets = [];
const trackerChunksLists = [];
let currentTrackerList = [];
let currentTrackerListSize = 0;
allTrackerChunks.forEach((trackerChunk) => {
if (currentTrackerListSize + trackerChunk.length > MAX_TRACKER_LIST_SIZE) {
trackerChunksLists.push(currentTrackerList);
currentTrackerList = [];
currentTrackerListSize = 0;
}
currentTrackerList.push(trackerChunk);
currentTrackerListSize += trackerChunk.length;
});
trackerChunksLists.push(currentTrackerList);
const header = packetHeaderChunk(
timestamp,
this.versionHigh,
this.versionLow,
this.dataFrameId,
trackerChunksLists.length
);
trackerChunksLists.forEach((trackerChunks) => {
dataPackets.push(dataChunk(header, dataTrackerListChunk(trackerChunks)));
});
this.dataFrameId += 1;
if (this.dataFrameId > 255) {
this.dataFrameId = 0;
}
return dataPackets;
}
}
module.exports = Encoder;
+13
View File
@@ -0,0 +1,13 @@
module.exports = (id, chunkData, hasSubchunks) => {
if (chunkData.length > 0x7fff) {
throw new Error('chunkData can not be greater than 32767 bytes');
}
const header = Buffer.alloc(4);
header.writeUInt16LE(id);
const chunkLengthBinaryString = chunkData.length.toString(2).padStart(15, '0');
const secondByteBinary = `${hasSubchunks ? '1' : '0'}${chunkLengthBinaryString}`;
header.writeUInt16LE(parseInt(secondByteBinary, 2), 2);
return Buffer.concat([header, chunkData]);
};
+4
View File
@@ -0,0 +1,4 @@
const chunk = require('../chunk');
module.exports = (packetHeaderChunk, trackerListChunk) =>
chunk(0x6755, Buffer.concat([packetHeaderChunk, trackerListChunk]), true);
@@ -0,0 +1,9 @@
const chunk = require('../chunk');
module.exports = (x, y, z) => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
buf.writeFloatLE(z, 8);
return chunk(0x0004, buf, false);
};
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerId, fieldChunks) => chunk(trackerId, Buffer.concat(fieldChunks), true);
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerChunks) => chunk(0x0001, Buffer.concat(trackerChunks), true);
+9
View File
@@ -0,0 +1,9 @@
const chunk = require('../chunk');
module.exports = (x, y, z) => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
buf.writeFloatLE(z, 8);
return chunk(0x0002, buf, false);
};
+9
View File
@@ -0,0 +1,9 @@
const chunk = require('../chunk');
module.exports = (x, y, z) => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
buf.writeFloatLE(z, 8);
return chunk(0x0000, buf, false);
};
@@ -0,0 +1,9 @@
const chunk = require('../chunk');
module.exports = (x, y, z) => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
buf.writeFloatLE(z, 8);
return chunk(0x0001, buf, false);
};
@@ -0,0 +1,7 @@
const chunk = require('../chunk');
module.exports = (validity) => {
const buf = Buffer.alloc(4);
buf.writeFloatLE(validity);
return chunk(0x0003, buf, false);
};
@@ -0,0 +1,10 @@
const chunk = require('../chunk');
module.exports = (timestamp) => {
const buf = Buffer.alloc(8);
const timestampHigh = timestamp.toString(2).padStart(64, '0').substring(0, 32);
const timestampLow = timestamp.toString(2).padStart(64, '0').substring(32);
buf.writeUInt32LE(parseInt(timestampLow, 2));
buf.writeUInt32LE(parseInt(timestampHigh, 2), 4);
return chunk(0x0006, buf, false);
};
@@ -0,0 +1,9 @@
const chunk = require('../chunk');
module.exports = (x, y, z) => {
const buf = Buffer.alloc(12);
buf.writeFloatLE(x);
buf.writeFloatLE(y, 4);
buf.writeFloatLE(z, 8);
return chunk(0x0005, buf, false);
};
+4
View File
@@ -0,0 +1,4 @@
const chunk = require('../chunk');
module.exports = (packetHeaderChunk, systemNameChunk, trackerListChunk) =>
chunk(0x6756, Buffer.concat([packetHeaderChunk, systemNameChunk, trackerListChunk]), true);
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (systemName) => chunk(0x0001, Buffer.from(systemName), false);
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerId, trackerNameChunk) => chunk(trackerId, trackerNameChunk, true);
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerChunks) => chunk(0x0002, Buffer.concat(trackerChunks), true);
+3
View File
@@ -0,0 +1,3 @@
const chunk = require('../chunk');
module.exports = (trackerName) => chunk(0x0000, Buffer.from(trackerName), false);
+15
View File
@@ -0,0 +1,15 @@
const chunk = require('./chunk');
module.exports = (timestamp, versionHigh, versionLow, frameId, framePacketCount) => {
const packetHeader = Buffer.alloc(12);
const timestampHigh = timestamp.toString(2).padStart(64, '0').substring(0, 32);
const timestampLow = timestamp.toString(2).padStart(64, '0').substring(32);
packetHeader.writeUInt32LE(parseInt(timestampLow, 2));
packetHeader.writeUInt32LE(parseInt(timestampHigh, 2), 4);
packetHeader.writeUint8(versionHigh, 8);
packetHeader.writeUint8(versionLow, 9);
packetHeader.writeUint8(frameId, 10);
packetHeader.writeUint8(framePacketCount, 11);
return chunk(0x0000, packetHeader, false);
};
-16
View File
@@ -1,16 +0,0 @@
const dgram = require('dgram');
const { Decoder } = require('.');
const client = dgram.createSocket('udp4');
const psnDecoder = new Decoder();
client.on('listening', () => {
client.addMembership('236.10.10.10');
});
client.on('message', (message) => {
psnDecoder.decode(message);
});
client.bind(56565, '0.0.0.0');
+58
View File
@@ -0,0 +1,58 @@
const dgram = require('dgram');
const Decoder = require('../decoder');
const client = dgram.createSocket('udp4');
const decoder = new Decoder();
client.on('listening', () => {
client.addMembership('236.10.10.10');
});
client.on('message', (buffer) => {
decoder.decode(buffer);
});
client.bind(56565, '0.0.0.0');
setInterval(() => {
if (decoder.info.system_name) {
console.log(`System Name: ${decoder.info.system_name}`);
}
if (Object.keys(decoder.info.trackers).length > 0) {
console.log(`Tracker Count: ${Object.keys(decoder.info.trackers).length}`);
}
Object.entries(decoder.data.trackers).forEach(([trackerId, tracker]) => {
console.log(
`Tracker - id: ${trackerId} | name: ${
decoder.info.trackers[trackerId]?.name ? decoder.info.trackers[trackerId]?.name : ''
}`
);
if (tracker.pos_x !== undefined && tracker.pos_y !== undefined && tracker.pos_z !== undefined) {
console.log(`\tpos: ${tracker.pos_x}, ${tracker.pos_y}, ${tracker.pos_z}`);
}
if (tracker.speed_x !== undefined && tracker.speed_y !== undefined && tracker.speed_z !== undefined) {
console.log(`\tspeed: ${tracker.speed_x}, ${tracker.speed_y}, ${tracker.speed_z}`);
}
if (tracker.ori_x !== undefined && tracker.ori_y !== undefined && tracker.ori_z !== undefined) {
console.log(`\tori: ${tracker.ori_x}, ${tracker.ori_y}, ${tracker.ori_z}`);
}
if (tracker.validity !== undefined) {
console.log(`\tstatus: ${tracker.validity}`);
}
if (tracker.accel_x !== undefined && tracker.accel_y !== undefined && tracker.accel_z !== undefined) {
console.log(`\taccel: ${tracker.accel_x}, ${tracker.accel_y}, ${tracker.accel_z}`);
}
if (tracker.trgtpos_x !== undefined && tracker.trgtpos_y !== undefined && tracker.trgtpos_z !== undefined) {
console.log(`\ttrgtpos: ${tracker.trgtpos_x}, ${tracker.trgtpos_y}, ${tracker.trgtpos_z}`);
}
if (tracker.tracker_timestamp !== undefined) {
console.log(`\ttimestamp: ${tracker.tracker_timestamp}`);
}
});
}, 1000);
+55
View File
@@ -0,0 +1,55 @@
const dgram = require('dgram');
const Encoder = require('../encoder');
const Tracker = require('../models/Tracker');
const client = dgram.createSocket('udp4');
const encoder = new Encoder('Test PSN Server', 2, 0);
const trackers = [];
trackers.push(new Tracker(0, 'Sun'));
trackers.push(new Tracker(1, 'Mercury'));
trackers.push(new Tracker(2, 'Venus'));
trackers.push(new Tracker(3, 'Earth'));
trackers.push(new Tracker(4, 'Mars'));
trackers.push(new Tracker(5, 'Jupiter'));
trackers.push(new Tracker(6, 'Saturn'));
trackers.push(new Tracker(7, 'Uranus'));
trackers.push(new Tracker(8, 'Neptune'));
trackers.push(new Tracker(9, 'Pluto'));
const orbits = [1.0, 88.0, 224.7, 365.2, 687, 4332, 10760, 30700, 60200, 90600];
const distFromSun = [0, 0.58, 1.08, 1.5, 2.28, 7.78, 14.29, 28.71, 45.04, 59.13];
let timestamp = 0;
setInterval(() => {
for (let index = 0; index < trackers.length; index += 1) {
const a = 1.0 / orbits[index];
const b = distFromSun[index];
const x = timestamp;
const cb = Math.cos(a * x) * b;
const sb = Math.sin(a * x) * b;
trackers[index].setPos(sb, 0, cb);
trackers[index].setSpeed(a * cb, 0, -a * sb);
trackers[index].setOri(0, x / 1000.0, 0);
trackers[index].setAccel(-a * a * sb, 0, -a * a * cb);
trackers[index].setTrgtPos(3, 14, 16);
trackers[index].setStatus(index / 10.0);
trackers[index].setTimestamp(timestamp);
}
const dataPackets = encoder.getDataPackets(timestamp, trackers);
dataPackets.forEach((packet) => {
client.send(packet, 56565, '236.10.10.10');
});
timestamp += 1;
}, 20);
setInterval(() => {
const infoPackets = encoder.getInfoPackets(timestamp, trackers);
infoPackets.forEach((packet) => {
client.send(packet, 56565, '236.10.10.10');
});
}, 1000);
+3 -139
View File
@@ -1,143 +1,7 @@
const PacketParser = require('./parsers/index.js');
const InfoPacket = require('./models/InfoPacket.js');
const DataPacket = require('./models/DataPacket.js');
const DATA_PACKET = 0x6755;
const INFO_PACKET = 0x6756;
class Decoder {
lastInfoPacketHeader;
lastDataPacketHeader;
infoPacketFrames = {};
dataPacketFrames = {};
constructor() {
this.info = {
trackers: {},
};
this.data = {
trackers: {},
};
}
updateInfo(framePackets) {
// console.log('info update');
framePackets.forEach((packet) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
// NOTE(jwetzell): system name subChunk
this.info.system_name = subChunk.system_name;
} else if (subChunk.id === 0x0002) {
subChunk.trackers?.forEach((tracker) => {
if (this.info.trackers[tracker.id] === undefined) {
this.info.trackers[tracker.id] = {};
}
this.info.trackers[tracker.id].name = tracker.tracker_name.tracker_name;
});
}
});
});
}
updateData(framePackets) {
// console.log('data update');
framePackets.forEach((packet) => {
packet.subChunks?.forEach((subChunk) => {
if (subChunk.id === 0x0001) {
subChunk.trackers?.forEach((tracker) => {
if (this.data.trackers[tracker.id] === undefined) {
this.data.trackers[tracker.id] = {};
}
tracker.fields?.forEach((field) => {
Object.entries(field).forEach(([key, value]) => {
this.data.trackers[tracker.id][key] = value;
});
});
});
}
});
});
}
// TODO(jwetzell): support multiple frame packets this might require some rework of how chunks are assumed
decode(packetBuf) {
const packet = PacketParser(packetBuf);
if (packet.id === INFO_PACKET) {
const infoPacket = new InfoPacket(packet);
if (infoPacket) {
if (infoPacket.subChunks?.length > 0) {
const currentInfoPacketHeader = infoPacket.getHeaderPacket();
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
return;
}
const systemSubChunk = infoPacket.getSystemPacket();
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
return;
}
if (this.infoPacketFrames[currentInfoPacketHeader.frame_id] === undefined) {
this.infoPacketFrames[currentInfoPacketHeader.frame_id] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.frame_id].push(infoPacket);
if (
this.infoPacketFrames[currentInfoPacketHeader.frame_id].length ===
currentInfoPacketHeader.frame_packet_count
) {
// console.log('found complete info frame');
this.updateInfo(this.infoPacketFrames[currentInfoPacketHeader.frame_id]);
delete this.infoPacketFrames[currentInfoPacketHeader.frame_id];
} else {
// TODO(jwetzell): need to compute whether a frame is complete
// console.log('compute frame completion');
}
this.lastInfoPacketHeader = currentInfoPacketHeader;
}
}
} else if (packet.id === DATA_PACKET) {
const dataPacket = new DataPacket(packet);
if (dataPacket) {
if (dataPacket.subChunks?.length > 0) {
const currentDataPacketHeader = dataPacket.getHeaderPacket();
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
console.error('data packet with no header');
return;
}
if (this.dataPacketFrames[currentDataPacketHeader.frame_id] === undefined) {
this.dataPacketFrames[currentDataPacketHeader.frame_id] = [];
}
this.dataPacketFrames[currentDataPacketHeader.frame_id].push(dataPacket);
if (
this.dataPacketFrames[currentDataPacketHeader.frame_id].length ===
currentDataPacketHeader.frame_packet_count
) {
// console.log('found complete data frame');
this.updateData(this.dataPacketFrames[currentDataPacketHeader.frame_id]);
delete this.dataPacketFrames[currentDataPacketHeader.frame_id];
} else {
// TODO(jwetzell): need to compute whether a frame is complete
// console.log('compute data frame completion');
}
this.lastDataPacketHeader = currentDataPacketHeader;
}
}
}
}
}
const Decoder = require('./decoder.js');
const Encoder = require('./encoder.js');
module.exports = {
Encoder,
Decoder,
};
+92
View File
@@ -0,0 +1,92 @@
const dataTrackerAccelChunk = require('../encoders/data/data-tracker-accel-chunk');
const dataTrackerChunk = require('../encoders/data/data-tracker-chunk');
const dataTrackerOriChunk = require('../encoders/data/data-tracker-ori-chunk');
const dataTrackerPosChunk = require('../encoders/data/data-tracker-pos-chunk');
const dataTrackerSpeedChunk = require('../encoders/data/data-tracker-speed-chunk');
const dataTrackerStatusChunk = require('../encoders/data/data-tracker-status-chunk');
const dataTrackerTimestampChunk = require('../encoders/data/data-tracker-timestamp-chunk');
const dataTrackerTrgtposChunk = require('../encoders/data/data-tracker-trgtpos-chunk');
const infoTrackerChunk = require('../encoders/info/info-tracker-chunk');
const infoTrackerNameChunk = require('../encoders/info/info-tracker-name-chunk');
class Tracker {
constructor(id, name) {
this.id = id;
this.name = name;
this.pos = undefined;
this.speed = undefined;
this.ori = undefined;
this.validity = undefined;
this.accel = undefined;
this.trgtpos = undefined;
this.timestamp = undefined;
}
setPos(x, y, z) {
this.pos = [x, y, z];
}
setSpeed(x, y, z) {
this.speed = [x, y, z];
}
setOri(x, y, z) {
this.ori = [x, y, z];
}
setStatus(validity) {
this.validity = validity;
}
setAccel(x, y, z) {
this.accel = [x, y, z];
}
setTrgtPos(x, y, z) {
this.trgtpos = [x, y, z];
}
setTimestamp(timestamp) {
this.timestamp = timestamp;
}
getDataChunk() {
const fieldChunks = [];
if (this.pos) {
fieldChunks.push(dataTrackerPosChunk(...this.pos));
}
if (this.speed) {
fieldChunks.push(dataTrackerSpeedChunk(...this.speed));
}
if (this.ori) {
fieldChunks.push(dataTrackerOriChunk(...this.ori));
}
if (this.validity) {
fieldChunks.push(dataTrackerStatusChunk(this.validity));
}
if (this.accel) {
fieldChunks.push(dataTrackerAccelChunk(...this.accel));
}
if (this.trgtpos) {
fieldChunks.push(dataTrackerTrgtposChunk(...this.trgtpos));
}
if (this.timestamp) {
fieldChunks.push(dataTrackerTimestampChunk(this.timestamp));
}
return dataTrackerChunk(this.id, fieldChunks);
}
getInfoChunk() {
return infoTrackerChunk(this.id, infoTrackerNameChunk(this.name));
}
}
module.exports = Tracker;
@@ -1,16 +1,16 @@
/* eslint-disable no-case-declarations */
/* eslint-disable no-param-reassign */
const { Parser } = require('binary-parser');
const { ChunkParser, HeaderParser } = require('../parsers/common');
const { DataTrackerListParser, DataTrackerFieldParser } = require('../parsers/data');
const Decoders = require('../../decoders');
class DataPacket {
constructor(packet) {
this.packet = packet;
this.subChunks = [];
// console.log(this.packet);
if (this.packet.has_subchunks) {
this.subChunkParser = new Parser().array('chunks', {
type: ChunkParser,
type: Decoders.Chunk,
lengthInBytes: this.packet.data_len,
});
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
@@ -21,16 +21,16 @@ class DataPacket {
case 0:
populatedSubChunk = {
...subChunk,
...HeaderParser.parse(subChunk.chunk_data),
...Decoders.PacketHeaderChunk.parse(subChunk.chunk_data),
};
break;
case 1:
const dataTrackerList = DataTrackerListParser.parse(subChunk.chunk_data);
const dataTrackerList = Decoders.Data.TrackerListChunk.parse(subChunk.chunk_data);
dataTrackerList.trackers?.forEach((tracker) => {
if (tracker.data && tracker.data_len > 0) {
const fields = new Parser()
.array('fields', {
type: DataTrackerFieldParser,
type: Decoders.Data.TrackerFieldChunk,
lengthInBytes: tracker.data_len,
})
.parse(tracker.data);
+7
View File
@@ -0,0 +1,7 @@
const DataPacket = require('./data-packet');
const InfoPacket = require('./info-packet');
module.exports = {
DataPacket,
InfoPacket,
};
@@ -1,6 +1,5 @@
const { Parser } = require('binary-parser');
const { ChunkParser, HeaderParser } = require('../parsers/common');
const { InfoSystemNameParser, InfoTrackerListParser } = require('../parsers/info');
const Decoders = require('../../decoders');
class InfoPacket {
constructor(packet) {
@@ -8,7 +7,7 @@ class InfoPacket {
this.subChunks = [];
if (this.packet.has_subchunks) {
this.subChunkParser = new Parser().array('chunks', {
type: ChunkParser,
type: Decoders.Chunk,
lengthInBytes: this.packet.data_len,
});
this.subChunks = this.subChunkParser.parse(this.packet.chunk_data)?.chunks;
@@ -19,19 +18,19 @@ class InfoPacket {
case 0:
populatedSubChunk = {
...subChunk,
...HeaderParser.parse(subChunk.chunk_data),
...Decoders.PacketHeaderChunk.parse(subChunk.chunk_data),
};
break;
case 1:
populatedSubChunk = {
...subChunk,
...InfoSystemNameParser.parse(subChunk.chunk_data),
...Decoders.Info.SystemNameChunk.parse(subChunk.chunk_data),
};
break;
case 2:
populatedSubChunk = {
...subChunk,
...InfoTrackerListParser.parse(subChunk.chunk_data),
...Decoders.Info.TrackerListChunk.parse(subChunk.chunk_data),
};
break;
default: