From da7feaf09e441f91d01b0075c4a3afccbd2452b9 Mon Sep 17 00:00:00 2001 From: Lewis Moten Date: Tue, 14 May 2024 16:08:23 -0400 Subject: [PATCH] let packer add the headers --- Dispatcher.js | 1 - PacketUtils.js | 119 +++++++++++++++++++++++++++++--------- Panels/MicrophonePanel.js | 2 +- StreamManager.js | 6 +- converters.js | 7 ++- index.js | 87 ++++++++-------------------- 6 files changed, 124 insertions(+), 98 deletions(-) diff --git a/Dispatcher.js b/Dispatcher.js index d26a87c..920016f 100644 --- a/Dispatcher.js +++ b/Dispatcher.js @@ -5,7 +5,6 @@ class Dispatcher { this.domain = domain; } emit = (eventName, ...args) => { - // console.log(`${this.domain}.${eventName}`, ...args); if(!this.LISTENERS[eventName]) return; this.LISTENERS[eventName].forEach(callback => callback(...args)); } diff --git a/PacketUtils.js b/PacketUtils.js index 8b6cd1d..6eb1931 100644 --- a/PacketUtils.js +++ b/PacketUtils.js @@ -1,4 +1,11 @@ -import { bitsToBytes, bitsToInt, numberToBits, numberToBytes, numberToHex } from "./converters"; +import { + bitsToBytes, + bitsToInt, + numberToBits, + numberToBytes, + numberToHex, + bytesToBits + } from "./converters"; import * as CRC from './CRC'; let SEGMENT_DURATION = 30; @@ -105,34 +112,42 @@ export const canSendPacket = () => { // Make sure we have enough encoding blocks within a packet return IS_ENCODED ? maxBits >= PACKET_ENCODED_BLOCK_SIZE : true; } -export const getPacketizationHeaderBitCount = () => DATA_SIZE_BITS + DATA_SIZE_CRC_BITS + DATA_CRC_BITS; +export const getPacketizationHeaderBitCount = (padUnusedBits = true) => { + let count = DATA_SIZE_BITS + DATA_SIZE_CRC_BITS + DATA_CRC_BITS; + if(padUnusedBits && count % 8 !== 0) { + count += 8 - (count % 8); + } + return count; +} +export const getPacketizationHeaderByteCount = () => getPacketizationHeaderBitCount() / 8; + +export const getPacketizationHeaderUnusedBitCount = () => { + return getPacketizationHeaderBitCount(true) - getPacketizationHeaderBitCount(false); +} export const getPacketizationBitCountFromBitCount = (bitCount) => bitCount + getPacketizationHeaderBitCount(); -export const getPacketizationBitCountFromByteCount = (byteCount) => - getPacketizationBitCountFromBitCount(byteCount * 8); -export const getPacketizationByteCountFromByteCount = (byteCount) => - Math.ceil(getPacketizationBitCountFromByteCount(byteCount) / 8); -export const getPacketizationByteCountFromBitCount = bitCount => - Math.ceil(getPacketizationBitCountFromBitCount(bitCount) / 8); - -export const getDataTransferDurationMillisecondsFromByteCount = (byteCount) => - getDataTransferDurationMilliseconds(getPacketizationBitCountFromByteCount(byteCount)); -export const getDataTransferDurationSeconds = (bitCount) => - getDataTransferDurationMilliseconds(bitCount) / 1000; export const packetStats = byteCount => { - const bitCount = byteCount * 8; - const packetCount = getPacketCount(bitCount); + + const byteCountWithHeaders = byteCount + getPacketizationHeaderByteCount(); + const packetCount = Math.ceil(byteCountWithHeaders / getPacketDataByteCount()); + const packetByteSize = (2 ** PACKET_SIZE_BITS); + const samplesPerPacket = Math.ceil((packetByteSize * 8) / BITS_PER_SAMPLE) + const packetDurationSeconds = (samplesPerPacket * SEGMENT_DURATION) / 1000; + const samplePeriodCount = packetCount * samplesPerPacket; + const transferBitCount = samplePeriodCount * BITS_PER_SAMPLE; return ({ packetCount, - sampleCount: packetCount * getPacketSegmentCount(), - durationMilliseconds: packetCount * getPacketDurationMilliseconds(), - totalBitCount: packetCount * getPacketMaxBitCount(), + samplePeriodCount, // to packet utils, these are "blocks" + transferBitCount: transferBitCount, + transferByteCount: Math.ceil(transferBitCount / 8), + totalDurationSeconds: packetCount * packetDurationSeconds, + packetDurationSeconds, }); }; -export const getPacketCount = (bitCount) => - canSendPacket() ? Math.ceil(bitCount / getPacketEncodedBitCount()) : 0; +const packetsNeededToTransferBytes = (byteCount) => + canSendPacket() ? Math.ceil(byteCount / getPacketDataByteCount()) : 0; export const getDataTransferDurationMilliseconds = (bitCount) => - getPacketCount(bitCount) * getPacketDurationMilliseconds(); + packetsNeededToTransferBytes(bitCount/8) * getPacketDurationMilliseconds(); export const getPacketDurationSeconds = () => getPacketDurationMilliseconds() / 1000; export const getSegmentDurationSeconds = () => getSegmentDurationMilliseconds() / 1000; export const getPacketSegmentCount = () => Math.ceil(getPacketMaxBitCount() / BITS_PER_SAMPLE); @@ -172,7 +187,58 @@ export const getPacketHeaderBitCount = (padAsBytes = true) => { } return bitCount; } -export const pack = (bits) => ({ +export const pack = (bytes) => { + + const getHeaderBytes = () => { + + // packetization headers + // data length + let dataLengthBits = []; + let dataLengthCrcBits = []; + let dataSizeCrcNumber = 0; + if(DATA_SIZE_BITS !== 0) { + dataLengthBits = numberToBits(bytes.length, DATA_SIZE_BITS); + + // crc on data length + if(DATA_SIZE_CRC_BITS !== 0) { + const dataLengthBytes = bitsToBytes(dataLengthBits); + dataSizeCrcNumber = CRC.check(dataLengthBytes, DATA_SIZE_CRC_BITS); + dataLengthCrcBits = numberToBits(dataSizeCrcNumber, DATA_SIZE_CRC_BITS); + } + } + + // crc on data + let dataCrcBits = []; + let dataCrcNumber = 0; + if(DATA_CRC_BITS !== 0) { + dataCrcNumber = CRC.check(bytes, DATA_CRC_BITS); + dataCrcBits = numberToBits(dataCrcNumber, DATA_CRC_BITS); + } + const headers = [ + ...dataLengthBits, + ...dataLengthCrcBits, + ...dataCrcBits + ]; + // pad headers to take full bytes + while(headers.length % 8 !== 0) { + headers.push(0); + } + + const unusedBitCount = getPacketizationHeaderUnusedBitCount(); + headers.push(...new Array(unusedBitCount).fill(0)); + + if(headers.length !== getPacketizationHeaderBitCount()) { + throw new Error(`Malformed header. Expected ${getPacketizationHeaderBitCount()} bits. We have ${headers.length}`); + } + + // prefix bits with headers + return bitsToBytes(headers); + } + + const bits = bytesToBits([...getHeaderBytes(), ...bytes]); + + return ({ + getBits: (packetIndex) => { // Returns a packet in the following order: // - [CRC] @@ -189,6 +255,9 @@ export const pack = (bits) => ({ const startIndex = packetIndex * dataBitCount; const endIndex = startIndex + dataBitCount; let packetBits = bits.slice(startIndex, endIndex); + if(packetBits.length === 0) { + throw new Error(`Attempted to send packet ${packetIndex}, but no data available.`) + } if(packetBits.length % 8 !== 0) { throw new Error('Attempted to create a packet with extra bits.'); } @@ -212,14 +281,9 @@ export const pack = (bits) => ({ if(PACKET_CRC_BIT_COUNT !== 0) { // convert to bytes const crcCheckBits = [...headerBits, ...packetBits]; - console.log('crc check bits', crcCheckBits.length) // 136 bits const bytes = bitsToBytes(crcCheckBits); const crc = CRC.check(bytes, PACKET_CRC_BIT_COUNT); const crcBits = numberToBits(crc, PACKET_CRC_BIT_COUNT); - if(packetIndex === 0) { - console.log('header without crc was', headerBits.length) - console.log('we did crc on this', crcCheckBits.join('')) - } // CRC must be first headerBits.unshift(...crcBits); @@ -234,6 +298,7 @@ export const pack = (bits) => ({ return encodedBits; } }); +} export const unpack = (bits) => ({ getPacketFromBits: (packetBits, packetIndex) => { diff --git a/Panels/MicrophonePanel.js b/Panels/MicrophonePanel.js index 6c3492b..d454b07 100644 --- a/Panels/MicrophonePanel.js +++ b/Panels/MicrophonePanel.js @@ -33,7 +33,7 @@ class MicrophonePanel extends BasePanel { }) .catch(error => { this.error = error; - console.log(error); + console.error(error); this.setListening(false); this.disconnectStream(); this.stopSampling(); diff --git a/StreamManager.js b/StreamManager.js index efe0ca6..1a9b03f 100644 --- a/StreamManager.js +++ b/StreamManager.js @@ -6,7 +6,8 @@ import { bitsToInt, bytesToBits, numberToBytes, - numberToHex + numberToHex, + numberToAscii } from "./converters"; const dispatcher = new Dispatcher('StreamManager', ['change']); @@ -74,13 +75,14 @@ export const applyPacket = ({ } if(DATA.length < length) { const copy = new Uint8ClampedArray(length); - copy.set(DATA.subarray(0, DATA.length)); + copy.set(DATA.subarray(0, DATA.length), 0); DATA = copy; } DATA.set(bytes, offset); delete BITS[packetIndex]; dispatcher.emit('packetReceived'); } else { + console.log("Failed", sequence); if(!FAILED_SEQUENCES.includes(sequence)) FAILED_SEQUENCES.push(sequence); } diff --git a/converters.js b/converters.js index 527d934..3ab76f9 100644 --- a/converters.js +++ b/converters.js @@ -6,11 +6,12 @@ export const numberToBytes = (number, bitLength) => { } return bytes; } -export const numberToHex = (bitLength) => { +export const numberToHex = (bitLength, prefix = '0x') => { const digits = Math.ceil(bitLength / 4); - return (number) => '0x' + number.toString(16).padStart(digits, '0').toUpperCase(); + return (number) => prefix + Number(number).toString(16).padStart(digits, '0').toUpperCase(); } - +export const numberToAscii = (number) => String.fromCharCode(clamp(Number(number), 0, 255)); +const clamp = (value, min, max) => Math.min(max, Math.max(min, value)); export function numberToBits(number, bitLength) { const bits = []; for(let i = bitLength - 1; i >= 0; i--) diff --git a/index.js b/index.js index cbebb57..b91f2f0 100644 --- a/index.js +++ b/index.js @@ -340,28 +340,32 @@ function updatePacketUtils() { speedPanel.setMaximumDurationMilliseconds(PacketUtils.getMaxDurationMilliseconds()); speedPanel.setDataBitsPerSecond(PacketUtils.getEffectiveBaud()); speedPanel.setPacketizationBitsPerSecond(PacketUtils.getBaud()); - speedPanel.setTransferDurationMilliseconds(PacketUtils.getDataTransferDurationMillisecondsFromByteCount( - messagePanel.getMessageBytes().length - )); + const { + totalDurationSeconds + } = PacketUtils.packetStats(messagePanel.getMessageBytes().length); + speedPanel.setTransferDurationMilliseconds(totalDurationSeconds * 1000); } function updatePacketStats() { const bytes = messagePanel.getMessageBytes(); - const bits = bytesToBits(bytes); const byteCount = bytes.length; - const bitCount = PacketUtils.getPacketizationBitCountFromBitCount(bits.length);; + + const { + transferBitCount, + transferByteCount, + packetCount, + samplePeriodCount + } = PacketUtils.packetStats(byteCount); // Data document.getElementById('original-byte-count').innerText = byteCount.toLocaleString(); - document.getElementById('packetization-byte-count').innerText = PacketUtils.getPacketizationByteCountFromBitCount(bits.length).toLocaleString(); - document.getElementById('packetization-bit-count').innerText = bitCount.toLocaleString(); - document.getElementById('packet-count').innerText = PacketUtils.getPacketCount(bitCount).toLocaleString(); - // ## Packet Encoding - - // Data + document.getElementById('packetization-byte-count').innerText = transferByteCount.toLocaleString(); + document.getElementById('packetization-bit-count').innerText = transferBitCount.toLocaleString(); + document.getElementById('packet-count').innerText = packetCount.toLocaleString(); document.getElementById('last-packet-unused-bit-count').innerText = PacketUtils.fromByteCountGetPacketLastUnusedBitCount(byteCount).toLocaleString(); + document.getElementById('total-segments').innerText = samplePeriodCount.toLocaleString(); + frequencyGraphPanel.setSamplePeriodsPerGroup(PacketUtils.getPacketSegmentCount()); - document.getElementById('total-segments').innerText = getTotalSegmentCount(bitCount).toLocaleString(); } @@ -427,46 +431,6 @@ function sendBytes(bytes) { SENT_ORIGINAL_TEXT = bytesToText(bytes); SENT_ORIGINAL_BITS = bits.slice(); - // packetization headers - // data length - let dataLengthBits = []; - let dataLengthCrcBits = []; - let dataSizeCrcNumber = 0; - const dataLengthBitLength = packetizationPanel.getDataSizePower(); - if(dataLengthBitLength !== 0) { - dataLengthBits = numberToBits(bytes.length, dataLengthBitLength); - - // crc on data length - const dataSizeCrcBitLength = packetizationPanel.getDataSizeCrc(); - if(dataSizeCrcBitLength !== 0) { - const bytes = bitsToBytes(dataLengthBits); - dataSizeCrcNumber = CRC.check(bytes, dataSizeCrcBitLength); - dataLengthCrcBits = numberToBits(dataSizeCrcNumber, dataSizeCrcBitLength); - } - } - - // crc on data - let dataCrcBits = []; - const dataCrcBitLength = packetizationPanel.getDataCrc(); - let dataCrcNumber = 0; - if(dataCrcBitLength !== 0) { - dataCrcNumber = CRC.check(bytes, dataCrcBitLength); - dataCrcBits = numberToBits(dataCrcNumber, dataCrcBitLength); - } - const headers = [ - ...dataLengthBits, - ...dataLengthCrcBits, - ...dataCrcBits - ]; - // pad headers to take full bytes - while(headers.length % 8 !== 0) { - headers.push(0); - } - // prefix bits with headers - bits.unshift(...headers); - - const bitCount = bits.length; - SENT_TRANSFER_BITS.length = 0; SENT_ENCODED_BITS.length = 0; @@ -474,11 +438,12 @@ function sendBytes(bytes) { const startSeconds = AudioSender.now() + 0.1; const packetBitCount = PacketUtils.getPacketMaxBitCount(); - const packetDurationSeconds = PacketUtils.getPacketDurationSeconds(); - const packetCount = PacketUtils.getPacketCount(bitCount); - const totalDurationSeconds = PacketUtils.getDataTransferDurationSeconds(bitCount); - - const packer = PacketUtils.pack(bits); + const { + packetCount, + totalDurationSeconds, + packetDurationSeconds + } = PacketUtils.packetStats(byteCount); + const packer = PacketUtils.pack(bytes); try { AudioSender.beginAt(startSeconds); @@ -555,7 +520,7 @@ function getPacketEndMilliseconds(packetStartedMilliseconds) { return getNextPacketStartMilliseconds(packetStartedMilliseconds) - 0.1; } function getTotalSegmentCount(bitCount) { - return PacketUtils.getPacketCount(bitCount) * PacketUtils.getPacketSegmentCount(); + return PacketUtils.packetsNeededToTransferBytes(bitCount/8) * PacketUtils.getPacketSegmentCount(); } function padArray(values, length, value) { values = values.slice();//copy @@ -598,12 +563,6 @@ function handleStreamManagerChange() { receivePanel.setReceivedBytes(bytes); } } -function parseTotalBitsTransferring() { - const dataByteCount = StreamManager.getTransferByteCount(); - const bitCount = PacketUtils.getPacketizationBitCountFromByteCount(dataByteCount); - const segments = getTotalSegmentCount(bitCount); - return segments * availableFskPairsPanel.getSelectedFskPairs().length; -} function removeEncodedPadding(bits) { const sizeBits = packetizationPanel.getDataSizePower();