diff --git a/CRC.js b/CRC.js
index 803c8b5..398dab0 100644
--- a/CRC.js
+++ b/CRC.js
@@ -1,3 +1,6 @@
+// CRC codes are not signed
+export const INVALID = -1;
+
function calcCrc(
bytes,
size,
diff --git a/PacketUtils.js b/PacketUtils.js
index 026e998..5a7d310 100644
--- a/PacketUtils.js
+++ b/PacketUtils.js
@@ -2,6 +2,7 @@ let SEGMENT_DURATION = 30;
let PACKET_SIZE_BITS = 8;
let DATA_SIZE_BITS = 8;
let DATA_SIZE_CRC_BITS = 8;
+let DATA_CRC_BITS = 8;
let BITS_PER_SEGMENT = 1;
let PACKET_ENCODING = false;
let PACKET_ENCODING_SIZE = 7;
@@ -14,6 +15,7 @@ export const changeConfiguration = (config) => {
packetSizeBitCount,
dataSizeBitCount,
dataSizeCrcBitCount,
+ dataCrcBitCount,
bitsPerSegment,
packetEncoding,
packetEncodingBitCount,
@@ -23,6 +25,7 @@ export const changeConfiguration = (config) => {
PACKET_SIZE_BITS = packetSizeBitCount;
DATA_SIZE_BITS = dataSizeBitCount;
DATA_SIZE_CRC_BITS = dataSizeCrcBitCount;
+ DATA_CRC_BITS = dataCrcBitCount;
BITS_PER_SEGMENT = bitsPerSegment;
PACKET_ENCODING = packetEncoding;
PACKET_ENCODING_SIZE = packetEncodingBitCount;
@@ -81,7 +84,7 @@ export const canSendPacket = () => {
}
export const getPacketEncodingBlockCount = () =>
isPacketEncoded() ? Math.floor(getPacketMaxBitCount() / packetEncodingBlockSize()) : 0;
-export const getPacketizationHeaderBitCount = () => DATA_SIZE_BITS + DATA_SIZE_CRC_BITS;
+export const getPacketizationHeaderBitCount = () => DATA_SIZE_BITS + DATA_SIZE_CRC_BITS + DATA_CRC_BITS;
export const getPacketizationBitCountFromBitCount = (bitCount) => bitCount + getPacketizationHeaderBitCount();
export const getPacketizationBitCountFromByteCount = (byteCount) =>
getPacketizationBitCountFromBitCount(byteCount * 8);
diff --git a/Panels/PacketizationPanel.js b/Panels/PacketizationPanel.js
index 55d5c30..209e161 100644
--- a/Panels/PacketizationPanel.js
+++ b/Panels/PacketizationPanel.js
@@ -5,25 +5,59 @@ class PacketizationPanel extends BasePanel {
constructor() {
super('Packetization');
- this.openField('Packet Size');
+ this.openField('Max Data Size');
+ this.addText('2^');
+ this.addInputNumber('data-size-power', 16, {min: 0, max: 32, eventName: 'dataSizePowerChange', translation: 'power of 2'});
+ this.addText(' ');
+ this.addDynamicText('data-size', 'n/a')
+ this.closeField();
+
+ this.openField('CRC on Size');
+ this.addDropdown('data-size-crc', [
+ {text: 'None', value: 0},
+ {text: 'CRC-8', value: 8},
+ {text: 'CRC-16', value: 16},
+ {text: 'CRC-32', value: 32},
+ ], 'dataSizeCrcChange');
+ this.closeField();
+
+ this.openField('CRC on Data');
+ this.addDropdown('data-crc', [
+ {text: 'None', value: 0},
+ {text: 'CRC-8', value: 8},
+ {text: 'CRC-16', value: 16},
+ {text: 'CRC-32', value: 32},
+ ], 'dataCrcChange');
+ this.closeField();
+
+ this.addSection('Packets');
+ this.openField('Size');
this.addText('2^');
this.addInputNumber('size-power', 5, {min: 0, max: 16, eventName: 'sizePowerChange', translation: 'power of 2'});
this.addText(' ');
- this.addDynamicText('size')
+ this.addDynamicText('size', 'n/a')
this.closeField();
- this.addSection('Encoding');
+ this.addCheckboxes('packet-encoding', [
+ { text: 'Error Correction', id: 'error-correction', checked: true, eventName: 'errorCorrectionChange' },
+ ]);
+
+
+ this.addSection('Sampling Period');
this.addCheckboxes('packet-encoding', [
{ text: 'Interleaving', id: 'interleaving', checked: true, eventName: 'interleavingChange' },
- { text: 'Error Correction', id: 'error-correction', checked: true, eventName: 'errorCorrectionChange' },
]);
this.addEventListener('sizePowerChange', this.handleSizePowerChange);
this.dispatcher.emit('sizePowerChange', {value: this.getSize()});
+
+ this.addEventListener('dataSizePowerChange', this.handleDataSizePowerChange);
+ this.dispatcher.emit('dataSizePowerChange', {value: this.getDataSizePower()});
+ this.dispatcher.emit('dataSizeChange', {value: this.getDataSize()});
};
- getSizePower = () => parseInt(this.getValueById('size-power'));
+ getSizePower = () => this.getNumberById('size-power');
setSizePower = (value) => {
this.setValueById('size-power', value);
this.handleSizePowerChange({value});
@@ -33,6 +67,22 @@ class PacketizationPanel extends BasePanel {
this.setValueById('size', byteSize(this.getSize()));
}
+ getDataSizePower = () => this.getNumberById('data-size-power');
+ setDataSizePower = (value) => {
+ this.setValueById('data-size-power', value);
+ this.handleDataSizePowerChange({value});
+ }
+ getDataSize = () => 2 ** this.getDataSizePower();
+ handleDataSizePowerChange = () => {
+ this.setValueById('data-size', byteSize(this.getDataSize()));
+ }
+
+ getDataSizeCrc = () => this.getNumberById('data-size-crc');
+ setDataSizeCrc = bitLength => this.setValueById('data-size-crc', bitLength)
+
+ getDataCrc = () => this.getNumberById('data-crc');
+ setDataCrc = bitLength => this.setValueById('data-crc', bitLength)
+
getInterleaving = () => this.getCheckedById('interleaving');
setInterleaving = (value) => this.setCheckedById('interleaving', value);
diff --git a/StreamManager.js b/StreamManager.js
index 5fb599e..e53000a 100644
--- a/StreamManager.js
+++ b/StreamManager.js
@@ -1,5 +1,10 @@
import Dispatcher from "./Dispatcher";
-import { bitsToInt } from "./converters";
+import * as CRC from './CRC';
+import {
+ bitsToBytes,
+ bitsToInt,
+ numberToBytes
+} from "./converters";
const dispatcher = new Dispatcher('StreamManager', ['change']);
@@ -102,6 +107,23 @@ export const getAllPacketBits = () => {
}
return bits;
}
+export const getAllPacketBitsDecoded = () => {
+ const packetCount = getPacketReceivedCount();
+ const bits = [];
+ for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
+ bits.push(...getPacketBitsDecoded(packetIndex));
+ }
+ return bits;
+}
+export const getDataBytes = () => {
+ const bits = getAllPacketBitsDecoded();
+ bits.splice(0, getStreamHeaderBitCount());
+ const bytes = bitsToBytes(bits);
+ if(isTransferByteCountTrusted()) {
+ bytes.length = getTransferByteCount();
+ }
+ return bytes;
+}
export const getPacketBits = (packetIndex, defaultBit = 0) => {
const bits = [];
const packet = BITS[packetIndex] ?? [];
@@ -121,10 +143,19 @@ export const getPacketBitsDecoded = (packetIndex, defaultBit = 0) => {
const bits = getPacketBits(packetIndex, defaultBit);
return PACKET_ENCODING.decode(bits);
}
+const getStreamHeaderBitCount = () => {
+ return Object.keys(STREAM_HEADERS).reduce((lastBit, key) => {
+ const {index = 0, length = 0} = STREAM_HEADERS[key];
+ if(length === 0) return lastBit;
+ if(lastBit < index + length) return index + length;
+ return lastBit;
+ }, 0);
+}
const getStreamHeaderBits = name => {
const header = STREAM_HEADERS[name];
if(!header) return [];
const { index, length } = header;
+ if(length === 0) return [];
const packetCount = getPacketReceivedCount();
const bits = [];
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
@@ -136,6 +167,48 @@ const getStreamHeaderBits = name => {
export const getTransferByteCount = () => {
const name = 'transfer byte count';
const length = STREAM_HEADERS[name].length;
+ if(length === 0) return 1;
const bits = getStreamHeaderBits(name);
return bitsToInt(bits, length);
}
+export const getTransferByteCountCrc = () => {
+ const name = 'transfer byte count crc';
+ const length = STREAM_HEADERS[name].length;
+ if(length === 0) return 0;
+ const bits = getStreamHeaderBits(name);
+ if(bits.length !== length) return CRC.INVALID;
+ return bitsToInt(bits, length);
+}
+export const getTransferByteCountActualCrc = () => {
+ const countBits = getStreamHeaderBits('transfer byte count').length;
+ if(countBits === 0) return 0;
+
+ const crcBits = getStreamHeaderBits('transfer byte count crc').length;
+ if(crcBits === 0) return 0;
+
+ const count = getTransferByteCount();
+ const bytesOfCount = numberToBytes(count, countBits);
+ return CRC.check(bytesOfCount, crcBits)
+}
+export const isTransferByteCountTrusted = () => {
+ return getTransferByteCountCrc() === getTransferByteCountActualCrc();
+}
+export function getTransferDataCrc() {
+ const name = 'transfer byte crc';
+ const length = STREAM_HEADERS[name].length;
+ if(length === 0) return 0;
+ const bits = getStreamHeaderBits(name);
+ if(bits.length !== length) return CRC.INVALID;
+ return bitsToInt(bits, length);
+}
+export function getTransferActualDataCrc() {
+ const name = 'transfer byte crc';
+ const length = STREAM_HEADERS[name].length;
+ if(length === 0) return 0;
+ const crcBits = getStreamHeaderBits(name).length;
+ const bytes = getDataBytes();
+ return CRC.check(bytes, crcBits);
+}
+export const isTransferDataTrusted = () => {
+ return getTransferDataCrc() === getTransferActualDataCrc();
+}
\ No newline at end of file
diff --git a/converters.js b/converters.js
index bcc510b..2e22d82 100644
--- a/converters.js
+++ b/converters.js
@@ -1,10 +1,63 @@
+export const numberToBytes = (number, bitLength) => {
+ const byteCount = Math.ceil(bitLength/8);
+ const bytes = [];
+ for(let i = 0; i < byteCount; i++) {
+ bytes.push((number >> (8 * (byteCount - 1 - i))) & 0xFF);
+ }
+ return bytes;
+}
+export const numberToHex = (bitLength) => {
+ const digits = Math.ceil(bitLength / 4);
+ return (number) => '0x' + number.toString(16).padStart(digits, '0').toUpperCase();
+}
+
+export function numberToBits(number, bitLength) {
+ const bits = [];
+ for(let i = bitLength - 1; i >= 0; i--)
+ bits.push((number >> i) & 1);
+ return bits;
+}
+export function bytesToText(bytes) {
+ if(!(bytes instanceof ArrayBuffer || ArrayBuffer.isView(bytes))) {
+ bytes = new Uint8Array(bytes).buffer;
+ }
+ return new TextDecoder().decode(bytes);
+}
+export function bytesToBits(bytes) {
+ return bytes.reduce((bits, byte) => [
+ ...bits,
+ ...byte.toString(2).padStart(8, '0').split('').map(Number)
+ ], []);
+}
+
+export function textToBytes(text) {
+ return new TextEncoder().encode(text);
+}
+export function textToBits(text) {
+ return bytesToBits(textToBytes(text));
+}
+
+export function bitsToText(bits) {
+ const bytes = new Uint8Array(bitsToBytes(bits));
+ return bytesToText(bytes.buffer);
+}
+export function bitsToBytes(bits) {
+ const bytes = [];
+ for(let i = 0; i < bits.length; i+= 8) {
+ bytes.push(parseInt(bits.slice(i, i + 8).join(''), 2));
+ }
+ return bytes;
+}
export const bitsToInt = (bits, bitLength) => {
- parseInt(bits
+ if(bits.length === 0) return 0;
+ if(bitLength <= 0) return 0;
+ return parseInt(bits
// only grab the bits we need
.slice(0, bitLength)
// combine into string
.join('')
// Assume missing bits were zeros
- .padEnd(bitLength, '0')
+ .padEnd(bitLength, '0'),
+ 2
);
}
diff --git a/index.html b/index.html
index 1fa21de..f9504c3 100644
--- a/index.html
+++ b/index.html
@@ -79,7 +79,8 @@
Decoded
Bytes: N/A
- Length CRC-8: N/A
+ Length CRC: N/A
+ Data CRC: N/A
Errors
Encoded Segments: N/A%
Encoded Packets: N/A%
diff --git a/index.js b/index.js
index 25dac9d..7867161 100644
--- a/index.js
+++ b/index.js
@@ -16,13 +16,23 @@ import PacketizationPanel from "./Panels/PacketizationPanel";
import AvailableFskPairsPanel from "./Panels/AvailableFskPairsPanel";
import FrequencyGraphPanel from "./Panels/FrequencyGraphPanel";
import GraphConfigurationPanel from './Panels/GraphConfigurationPanel'
+import {
+ bitsToInt,
+ bitsToBytes,
+ bitsToText,
+ bytesToBits,
+ bytesToText,
+ numberToBytes,
+ numberToBits,
+ numberToHex,
+ textToBits,
+ textToBytes,
+} from './converters';
var audioContext;
var microphoneStream;
var microphoneNode;
var analyser;
var sentDataTextArea;
-const MAXIMUM_PACKETIZATION_SIZE_BITS = 16;
-const CRC_BIT_COUNT = 8;
// bits as they are sent
let SENT_ORIGINAL_TEXT = '';
@@ -96,6 +106,10 @@ function handleWindowLoad() {
signalPanel.setTimeoutMilliseconds(60);
packetizationPanel.setSizePower(5);
+ packetizationPanel.setDataSizePower(16);
+ packetizationPanel.setDataSizeCrc(8);
+ packetizationPanel.setDataCrc(16);
+
packetizationPanel.setErrorCorrection(true);
packetizationPanel.setInterleaving(true);
@@ -153,6 +167,9 @@ function handleWindowLoad() {
configurationChanged();
});
packetizationPanel.addEventListener('errorCorrectionChange', configurationChanged);
+ packetizationPanel.addEventListener('dataSizePowerChange', configurationChanged);
+ packetizationPanel.addEventListener('dataSizeCrcChange', configurationChanged);
+ packetizationPanel.addEventListener('dataCrcChange', configurationChanged);
availableFskPairsPanel.addEventListener('change', (event) => {
frequencyGraphPanel.setFskPairs(event.selected);
@@ -252,6 +269,10 @@ function updateStreamManager() {
StreamManager.setPacketEncoding(
packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
);
+ const xferCountLength = packetizationPanel.getDataSizePower();
+ const xferCountCrcLength = xferCountLength === 0 ? 0 : packetizationPanel.getDataSizeCrc();
+ const xferCrcLength = packetizationPanel.getDataCrc();
+
StreamManager.changeConfiguration({
bitsPerPacket: PacketUtils.getPacketMaxBitCount(),
segmentsPerPacket: PacketUtils.getPacketSegmentCount(),
@@ -259,11 +280,15 @@ function updateStreamManager() {
streamHeaders: {
'transfer byte count': {
index: 0,
- length: MAXIMUM_PACKETIZATION_SIZE_BITS
+ length: xferCountLength
},
'transfer byte count crc': {
- index: MAXIMUM_PACKETIZATION_SIZE_BITS,
- length: CRC_BIT_COUNT
+ index: xferCountLength,
+ length: xferCountCrcLength
+ },
+ 'transfer byte crc': {
+ index: xferCountLength + xferCountCrcLength,
+ length: xferCrcLength
},
}
});
@@ -276,8 +301,9 @@ function updatePacketUtils() {
PacketUtils.changeConfiguration({
segmentDurationMilliseconds: signalPanel.getSegmentDurationMilliseconds(),
packetSizeBitCount: packetizationPanel.getSizePower(),
- dataSizeBitCount: MAXIMUM_PACKETIZATION_SIZE_BITS,
- dataSizeCrcBitCount: CRC_BIT_COUNT,
+ dataSizeBitCount: packetizationPanel.getDataSizePower(),
+ dataSizeCrcBitCount: packetizationPanel.getDataSizeCrc(),
+ dataCrcBitCount: packetizationPanel.getDataCrc(),
bitsPerSegment,
packetEncoding: packetizationPanel.getErrorCorrection(),
packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE,
@@ -375,22 +401,20 @@ function percentInFrequency(hz, frequencyResolution) {
return percent;
}
-function logSent(text) {
- // display what is being sent
- sentDataTextArea.value += text + '\n';
- sentDataTextArea.scrollTop = sentDataTextArea.scrollHeight;
-}
-
function sendBytes(bytes) {
const byteCount = bytes.length;
if(byteCount === 0) {
- logSent('Nothing to send!');
+ document.getElementById('sent-data').innerText = 'Nothing to send!';
return;
- } else if(byteCount > 0xFFFF) {
- logSent('Too much to send!');
+ } else if(byteCount > packetizationPanel.getDataSize()) {
+ document.getElementById('sent-data').innerText = `Attempted to send too much data. Limit is ${Humanize.byteSize(packetizationPanel.getDataSize())}. Tried to send ${Humanize.byteSize(byteCount)}`;
return;
}
+ AudioReceiver.reset();
+ StreamManager.reset();
+ frequencyGraphPanel.start();
+
const bits = bytesToBits(bytes);
SENT_ORIGINAL_TEXT = bytesToText(bytes);
@@ -398,12 +422,44 @@ function sendBytes(bytes) {
// packetization headers
// data length
- const dataLengthBits = numberToBits(bytes.length, MAXIMUM_PACKETIZATION_SIZE_BITS);
- // crc on data length
- const dataLengthCrcBits = numberToBits(CRC.check(bitsToBytes(dataLengthBits), CRC_BIT_COUNT), CRC_BIT_COUNT);
+ 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);
+ }
+ console.log('sending headers', {
+ // dataLengthBits,
+ // dataLengthCrcBits,
+ // dataCrcBits,
+ dataCrcHex: numberToHex(packetizationPanel.getDataCrc())(dataCrcNumber),
+ dataSizeCrcHex: numberToHex(packetizationPanel.getDataSizeCrc())(dataSizeCrcNumber),
+ })
// prefix with headers
- bits.unshift(...dataLengthBits, ...dataLengthCrcBits);
+ bits.unshift(
+ ...dataLengthBits,
+ ...dataLengthCrcBits,
+ ...dataCrcBits
+ );
const bitCount = bits.length;
@@ -418,13 +474,10 @@ function sendBytes(bytes) {
const packetCount = PacketUtils.getPacketCount(bitCount);
const totalDurationSeconds = PacketUtils.getDataTransferDurationSeconds(bitCount);
- const errorCorrectionBits = [];
-
AudioSender.beginAt(startSeconds);
// send all packets
for(let i = 0; i < packetCount; i++) {
let packet = PacketUtils.getPacketBits(bits, i);
- errorCorrectionBits.push(...packet);
SENT_ENCODED_BITS.push(...packet);
if(packet.length > packetBitCount) {
console.error('Too many bits in the packet. tried to send %s, limited to %s', packet.length, packetBitCount);
@@ -522,40 +575,30 @@ function resumeGraph() {
}
}
-function getTransferredCorrectedBits() {
- const bits = [];
- const packetCount = StreamManager.getPacketReceivedCount();
- for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
- let packetBits = StreamManager.getPacketBits(packetIndex);
- if(packetizationPanel.getErrorCorrection()) {
- bits.push(...HammingEncoding.decode(packetBits));
- } else {
- bits.push(...packetBits);
- }
- }
- return bits;
-}
-
function handleStreamManagerChange() {
const channelCount = availableFskPairsPanel.getSelectedFskPairs().length;
let allRawBits = StreamManager.getStreamBits();
let allEncodedBits = StreamManager.getAllPacketBits();
- let allDecodedBits = getTransferredCorrectedBits();
+ let allDecodedBits = StreamManager.getAllPacketBitsDecoded();
// get packet data before removing decoded bits
- const transmissionByteCount = parseTransmissionByteCount(allDecodedBits);
- const transmissionByteCountCrc = parseTransmissionByteCountCrc(allDecodedBits)
- const transmissionByteCountActualCrc = CRC.check(
- bitsToBytes(
- numberToBits(
- transmissionByteCount,
- MAXIMUM_PACKETIZATION_SIZE_BITS
- )
- ), CRC_BIT_COUNT
- );
- const trustedLength = transmissionByteCountCrc === transmissionByteCountActualCrc;
+ const transmissionByteCount = StreamManager.getTransferByteCount();
+ const transmissionByteCountCrc = StreamManager.getTransferByteCountCrc();
+ const transmissionByteCountActualCrc = StreamManager.getTransferByteCountActualCrc();
+
+ const trustedLength = StreamManager.isTransferByteCountTrusted();
const totalBitsTransferring = parseTotalBitsTransferring(allDecodedBits);
+ const transmissionDataCrc = StreamManager.getTransferDataCrc();
+ const transmissionDataActualCrc = StreamManager.getTransferActualDataCrc();
+ const transmissionCrc = document.getElementById('received-packet-original-data-crc');
+ transmissionCrc.innerText = numberToHex(packetizationPanel.getDataCrc())(transmissionDataCrc);
+ const trustedData = StreamManager.isTransferDataTrusted();
+ transmissionCrc.className = trustedData ? 'bit-correct' : 'bit-wrong';
+ if(!trustedData) {
+ transmissionCrc.innerText += ' (Expected ' + numberToHex(packetizationPanel.getDataCrc())(transmissionDataActualCrc) + ')';
+ }
+
// reduce all decoded bits based on original data sent
allDecodedBits = removeDecodedHeadersAndPadding(allDecodedBits);
@@ -604,10 +647,10 @@ function handleStreamManagerChange() {
'');
document.getElementById('received-packet-original-bytes').innerText = transmissionByteCount.toLocaleString();
const packetCrc = document.getElementById('received-packet-original-bytes-crc');
- packetCrc.innerText = '0x' + asHex(2)(transmissionByteCountCrc);
+ packetCrc.innerText = numberToHex(packetizationPanel.getDataSizeCrc())(transmissionByteCountCrc);
packetCrc.className = trustedLength ? 'bit-correct' : 'bit-wrong';
if(!trustedLength) {
- packetCrc.innerText += ' (Expected 0x' + asHex(2)(transmissionByteCountActualCrc) + ')';
+ packetCrc.innerText += ' (Expected ' + numberToHex(packetizationPanel.getDataSizeCrc())(transmissionByteCountActualCrc) + ')';
}
document.getElementById('received-encoded-bits-error-percent').innerText = (
@@ -619,44 +662,23 @@ function handleStreamManagerChange() {
document.getElementById('received-decoded-bits-error-percent').innerText = (
Math.floor((1 - (correctedDecodedBits / allDecodedBits.length)) * 10000) * 0.01
).toLocaleString();
- // ArrayBuffer / ArrayBufferView
- const receivedText = bitsToText(allDecodedBits);
+
+ const bytes = StreamManager.getDataBytes();
+ const receivedText = bytesToText(bytes);
+
messagePanel.setReceived(
receivedText.split('').reduce(textExpectorReducer(SENT_ORIGINAL_TEXT), '')
);
}
-function asHex(length) {
- return (number) => number.toString(16).padStart(length, '0').toUpperCase();
-}
-function parseDataTransferDurationMilliseconds() {
- const decodedBits = getTransferredCorrectedBits();
- const byteCount = parseTransmissionByteCount(decodedBits);
- return PacketUtils.getDataTransferDurationMillisecondsFromByteCount(byteCount);
-}
function parseTotalBitsTransferring() {
- const dataByteCount = parseTransmissionByteCount();
+ const dataByteCount = StreamManager.getTransferByteCount();
const bitCount = PacketUtils.getPacketizationBitCountFromByteCount(dataByteCount);
const segments = getTotalSegmentCount(bitCount);
return segments * availableFskPairsPanel.getSelectedFskPairs().length;
}
-function parseTransmissionByteCountCrc() {
- let decodedBits = getTransferredCorrectedBits();
- const offset = MAXIMUM_PACKETIZATION_SIZE_BITS;
- decodedBits = decodedBits.slice(offset, offset + CRC_BIT_COUNT);
- return bitsToInt(decodedBits, CRC_BIT_COUNT);
-}
-function parseTransmissionByteCount() {
- let decodedBits = getTransferredCorrectedBits();
- decodedBits = decodedBits.slice(0, MAXIMUM_PACKETIZATION_SIZE_BITS);
- while(decodedBits.length < MAXIMUM_PACKETIZATION_SIZE_BITS) {
- // assume maximum value possible
- // until we have enough bits to find the real size
- decodedBits.push(1);
- }
- return bitsToInt(decodedBits, MAXIMUM_PACKETIZATION_SIZE_BITS);
-}
+
function removeEncodedPadding(bits) {
- const sizeBits = MAXIMUM_PACKETIZATION_SIZE_BITS;
+ const sizeBits = packetizationPanel.getDataSizePower();
const dataSize = ERROR_CORRECTION_DATA_SIZE;
const blockSize = ERROR_CORRECTION_BLOCK_SIZE;
let bitsNeeded = sizeBits;
@@ -675,7 +697,10 @@ function removeEncodedPadding(bits) {
const dataByteCount = StreamManager.getTransferByteCount();
// determine how many decoded bits need to be sent (including the size)
- const totalBits = (dataByteCount * 8) + MAXIMUM_PACKETIZATION_SIZE_BITS + CRC_BIT_COUNT;
+ let totalBits = (dataByteCount * 8);
+ totalBits += packetizationPanel.getDataSizePower();
+ if(packetizationPanel.getDataSizePower() !== 0) totalBits += packetizationPanel.getDataSizeCrc();
+ totalBits += packetizationPanel.getDataCrc();
let encodingBitCount = totalBits;
if(packetizationPanel.getErrorCorrection()) {
const blocks = Math.ceil(encodingBitCount / dataSize);
@@ -691,17 +716,51 @@ function removeEncodedPadding(bits) {
return bits;
}
function removeDecodedHeadersAndPadding(bits) {
- const sizeBits = MAXIMUM_PACKETIZATION_SIZE_BITS;
- let bitCount = bits.length / 8;
- if(bits.length >= sizeBits) {
- bitCount = bitsToInt(bits.slice(0, sizeBits), sizeBits);
+ const sizeBitCount = packetizationPanel.getDataSizePower();
+ const sizeCrcBitCount = packetizationPanel.getDataSizeCrc();
+ const dataCrcBitCount = packetizationPanel.getDataCrc();
+ let byteCount;
+ let offset = 0;
+ if(sizeBitCount !== 0) {
+ offset += sizeBitCount;
+ // header bits only?
+ if(bits.length <= offset) return [];
+ byteCount = bitsToInt(bits.slice(0, sizeBitCount), sizeBitCount);
+ if(sizeCrcBitCount !== 0) {
+ offset += sizeBitCount;
+ // header bits only?
+ if(bits.length <= offset) return [];
+ let countCrc = bitsToInt(bits.slice(sizeBitCount, sizeBitCount + sizeCrcBitCount), sizeCrcBitCount);
+ let actualCountCrc = CRC.check(numberToBytes(sizeCrcBitCount, sizeBitCount), sizeCrcBitCount);
+ // can we trust the size?
+ if(countCrc !== actualCountCrc) {
+ if(dataCrcBitCount !== 0) {
+ offset += dataCrcBitCount;
+ if(bits.length <= offset) return [];
+ }
+ // Change based off of header bits
+ byteCount = (bits.length - offset) / 8;
+ }
+ } else if(dataCrcBitCount !== 0) {
+ offset += dataCrcBitCount;
+ if(bits.length <= offset) return [];
+ }
+ // remove headers and excessive bits
+ const bitCount = byteCount * 8;
+ return bits.slice(offset, offset + bitCount).splice(bitCount);
+ } else {
+ // size not included. Means 1 byte max
+ // crc not valid on size
+ // crc valid on byte
+ const dataCrcBitCount = packetizationPanel.getDataCrc();
+ if(dataCrcBitCount === 0) {
+ // bits are pure data for 1 byte
+ return bits.slice(0, 8);
+ } else {
+ // get byte after data crc
+ return bits.slice(dataCrcBitCount, dataCrcBitCount + 8);
+ }
}
- // remove size and crc header
- bits.splice(0, sizeBits + CRC_BIT_COUNT);
-
- // remove excessive bits
- bits.splice(bitCount * 8);
- return bits;
}
const bitReducer = (packetBitSize, blockSize, blockCallback) => (all, bit, i) => {
const packetIndex = Math.floor(i / packetBitSize);
@@ -780,63 +839,10 @@ function getAudioContext() {
}
return audioContext;
}
-function bitsToInt(bits, bitLength) {
- // only grab the bits we need
- const bitString = bits.slice(0, bitLength)
- // combine into string
- .join('')
- // Assume missing bits were zeros
- .padEnd(bitLength, '0');
- // parse as int
- return parseInt(bitString, 2);
-}
-function intToBytes(int, bitLength) {
- const byteCount = Math.ceil(bitLength/8);
- const bytes = [];
- for(let i = 0; i < byteCount; i++) {
- bytes.push((int >> (8 * (byteCount - 1 - i))) & 0xFF);
- }
- return bytes;
-}
-function numberToBits(number, bitLength) {
- const bits = [];
- for(let i = bitLength - 1; i >= 0; i--)
- bits.push((number >> i) & 1);
- return bits;
-}
-function bytesToText(bytes) {
- return new TextDecoder().decode(bytes);
-}
-function textToBytes(text) {
- return new TextEncoder().encode(text);
-}
-function bytesToBits(bytes) {
- return bytes.reduce((bits, byte) => [
- ...bits,
- ...byte.toString(2).padStart(8, '0').split('').map(Number)
- ], []);
-}
-function bitsToBytes(bits) {
- const bytes = [];
- for(let i = 0; i < bits.length; i+= 8) {
- bytes.push(parseInt(bits.slice(i, i + 8).join(''), 2));
- }
- return bytes;
-}
-function textToBits(text) {
- return bytesToBits(textToBytes(text));
-}
-function bitsToText(bits) {
- const bytes = new Uint8Array(bitsToBytes(bits));
- return bytesToText(bytes.buffer);
-}
function handleSendButtonClick() {
if(messagePanel.getSendButtonText() === 'Stop') {
AudioSender.stop();
} else {
- AudioReceiver.reset();
- StreamManager.reset();
- frequencyGraphPanel.start();
const text = messagePanel.getMessage();
sendBytes(textToBytes(text));
}