add data crc
This commit is contained in:
3
CRC.js
3
CRC.js
@@ -1,3 +1,6 @@
|
|||||||
|
// CRC codes are not signed
|
||||||
|
export const INVALID = -1;
|
||||||
|
|
||||||
function calcCrc(
|
function calcCrc(
|
||||||
bytes,
|
bytes,
|
||||||
size,
|
size,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ let SEGMENT_DURATION = 30;
|
|||||||
let PACKET_SIZE_BITS = 8;
|
let PACKET_SIZE_BITS = 8;
|
||||||
let DATA_SIZE_BITS = 8;
|
let DATA_SIZE_BITS = 8;
|
||||||
let DATA_SIZE_CRC_BITS = 8;
|
let DATA_SIZE_CRC_BITS = 8;
|
||||||
|
let DATA_CRC_BITS = 8;
|
||||||
let BITS_PER_SEGMENT = 1;
|
let BITS_PER_SEGMENT = 1;
|
||||||
let PACKET_ENCODING = false;
|
let PACKET_ENCODING = false;
|
||||||
let PACKET_ENCODING_SIZE = 7;
|
let PACKET_ENCODING_SIZE = 7;
|
||||||
@@ -14,6 +15,7 @@ export const changeConfiguration = (config) => {
|
|||||||
packetSizeBitCount,
|
packetSizeBitCount,
|
||||||
dataSizeBitCount,
|
dataSizeBitCount,
|
||||||
dataSizeCrcBitCount,
|
dataSizeCrcBitCount,
|
||||||
|
dataCrcBitCount,
|
||||||
bitsPerSegment,
|
bitsPerSegment,
|
||||||
packetEncoding,
|
packetEncoding,
|
||||||
packetEncodingBitCount,
|
packetEncodingBitCount,
|
||||||
@@ -23,6 +25,7 @@ export const changeConfiguration = (config) => {
|
|||||||
PACKET_SIZE_BITS = packetSizeBitCount;
|
PACKET_SIZE_BITS = packetSizeBitCount;
|
||||||
DATA_SIZE_BITS = dataSizeBitCount;
|
DATA_SIZE_BITS = dataSizeBitCount;
|
||||||
DATA_SIZE_CRC_BITS = dataSizeCrcBitCount;
|
DATA_SIZE_CRC_BITS = dataSizeCrcBitCount;
|
||||||
|
DATA_CRC_BITS = dataCrcBitCount;
|
||||||
BITS_PER_SEGMENT = bitsPerSegment;
|
BITS_PER_SEGMENT = bitsPerSegment;
|
||||||
PACKET_ENCODING = packetEncoding;
|
PACKET_ENCODING = packetEncoding;
|
||||||
PACKET_ENCODING_SIZE = packetEncodingBitCount;
|
PACKET_ENCODING_SIZE = packetEncodingBitCount;
|
||||||
@@ -81,7 +84,7 @@ export const canSendPacket = () => {
|
|||||||
}
|
}
|
||||||
export const getPacketEncodingBlockCount = () =>
|
export const getPacketEncodingBlockCount = () =>
|
||||||
isPacketEncoded() ? Math.floor(getPacketMaxBitCount() / packetEncodingBlockSize()) : 0;
|
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 getPacketizationBitCountFromBitCount = (bitCount) => bitCount + getPacketizationHeaderBitCount();
|
||||||
export const getPacketizationBitCountFromByteCount = (byteCount) =>
|
export const getPacketizationBitCountFromByteCount = (byteCount) =>
|
||||||
getPacketizationBitCountFromBitCount(byteCount * 8);
|
getPacketizationBitCountFromBitCount(byteCount * 8);
|
||||||
|
|||||||
@@ -5,25 +5,59 @@ class PacketizationPanel extends BasePanel {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super('Packetization');
|
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.addText('2^');
|
||||||
this.addInputNumber('size-power', 5, {min: 0, max: 16, eventName: 'sizePowerChange', translation: 'power of 2'});
|
this.addInputNumber('size-power', 5, {min: 0, max: 16, eventName: 'sizePowerChange', translation: 'power of 2'});
|
||||||
this.addText(' ');
|
this.addText(' ');
|
||||||
this.addDynamicText('size')
|
this.addDynamicText('size', 'n/a')
|
||||||
this.closeField();
|
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', [
|
this.addCheckboxes('packet-encoding', [
|
||||||
{ text: 'Interleaving', id: 'interleaving', checked: true, eventName: 'interleavingChange' },
|
{ text: 'Interleaving', id: 'interleaving', checked: true, eventName: 'interleavingChange' },
|
||||||
{ text: 'Error Correction', id: 'error-correction', checked: true, eventName: 'errorCorrectionChange' },
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.addEventListener('sizePowerChange', this.handleSizePowerChange);
|
this.addEventListener('sizePowerChange', this.handleSizePowerChange);
|
||||||
this.dispatcher.emit('sizePowerChange', {value: this.getSize()});
|
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) => {
|
setSizePower = (value) => {
|
||||||
this.setValueById('size-power', value);
|
this.setValueById('size-power', value);
|
||||||
this.handleSizePowerChange({value});
|
this.handleSizePowerChange({value});
|
||||||
@@ -33,6 +67,22 @@ class PacketizationPanel extends BasePanel {
|
|||||||
this.setValueById('size', byteSize(this.getSize()));
|
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');
|
getInterleaving = () => this.getCheckedById('interleaving');
|
||||||
setInterleaving = (value) => this.setCheckedById('interleaving', value);
|
setInterleaving = (value) => this.setCheckedById('interleaving', value);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import Dispatcher from "./Dispatcher";
|
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']);
|
const dispatcher = new Dispatcher('StreamManager', ['change']);
|
||||||
|
|
||||||
@@ -102,6 +107,23 @@ export const getAllPacketBits = () => {
|
|||||||
}
|
}
|
||||||
return bits;
|
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) => {
|
export const getPacketBits = (packetIndex, defaultBit = 0) => {
|
||||||
const bits = [];
|
const bits = [];
|
||||||
const packet = BITS[packetIndex] ?? [];
|
const packet = BITS[packetIndex] ?? [];
|
||||||
@@ -121,10 +143,19 @@ export const getPacketBitsDecoded = (packetIndex, defaultBit = 0) => {
|
|||||||
const bits = getPacketBits(packetIndex, defaultBit);
|
const bits = getPacketBits(packetIndex, defaultBit);
|
||||||
return PACKET_ENCODING.decode(bits);
|
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 getStreamHeaderBits = name => {
|
||||||
const header = STREAM_HEADERS[name];
|
const header = STREAM_HEADERS[name];
|
||||||
if(!header) return [];
|
if(!header) return [];
|
||||||
const { index, length } = header;
|
const { index, length } = header;
|
||||||
|
if(length === 0) return [];
|
||||||
const packetCount = getPacketReceivedCount();
|
const packetCount = getPacketReceivedCount();
|
||||||
const bits = [];
|
const bits = [];
|
||||||
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
|
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
|
||||||
@@ -136,6 +167,48 @@ const getStreamHeaderBits = name => {
|
|||||||
export const getTransferByteCount = () => {
|
export const getTransferByteCount = () => {
|
||||||
const name = 'transfer byte count';
|
const name = 'transfer byte count';
|
||||||
const length = STREAM_HEADERS[name].length;
|
const length = STREAM_HEADERS[name].length;
|
||||||
|
if(length === 0) return 1;
|
||||||
const bits = getStreamHeaderBits(name);
|
const bits = getStreamHeaderBits(name);
|
||||||
return bitsToInt(bits, length);
|
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();
|
||||||
|
}
|
||||||
@@ -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) => {
|
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
|
// only grab the bits we need
|
||||||
.slice(0, bitLength)
|
.slice(0, bitLength)
|
||||||
// combine into string
|
// combine into string
|
||||||
.join('')
|
.join('')
|
||||||
// Assume missing bits were zeros
|
// Assume missing bits were zeros
|
||||||
.padEnd(bitLength, '0')
|
.padEnd(bitLength, '0'),
|
||||||
|
2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
<h2>Decoded</h2>
|
<h2>Decoded</h2>
|
||||||
<div>
|
<div>
|
||||||
Bytes: <span id="received-packet-original-bytes">N/A</span><br>
|
Bytes: <span id="received-packet-original-bytes">N/A</span><br>
|
||||||
Length CRC-8: <span id="received-packet-original-bytes-crc">N/A</span><br>
|
Length CRC: <span id="received-packet-original-bytes-crc">N/A</span><br>
|
||||||
|
Data CRC: <span id="received-packet-original-data-crc">N/A</span><br>
|
||||||
<h4>Errors</h4>
|
<h4>Errors</h4>
|
||||||
Encoded Segments: <span id="received-raw-bits-error-percent">N/A</span>%<br>
|
Encoded Segments: <span id="received-raw-bits-error-percent">N/A</span>%<br>
|
||||||
Encoded Packets: <span id="received-encoded-bits-error-percent">N/A</span>%<br>
|
Encoded Packets: <span id="received-encoded-bits-error-percent">N/A</span>%<br>
|
||||||
|
|||||||
292
index.js
292
index.js
@@ -16,13 +16,23 @@ import PacketizationPanel from "./Panels/PacketizationPanel";
|
|||||||
import AvailableFskPairsPanel from "./Panels/AvailableFskPairsPanel";
|
import AvailableFskPairsPanel from "./Panels/AvailableFskPairsPanel";
|
||||||
import FrequencyGraphPanel from "./Panels/FrequencyGraphPanel";
|
import FrequencyGraphPanel from "./Panels/FrequencyGraphPanel";
|
||||||
import GraphConfigurationPanel from './Panels/GraphConfigurationPanel'
|
import GraphConfigurationPanel from './Panels/GraphConfigurationPanel'
|
||||||
|
import {
|
||||||
|
bitsToInt,
|
||||||
|
bitsToBytes,
|
||||||
|
bitsToText,
|
||||||
|
bytesToBits,
|
||||||
|
bytesToText,
|
||||||
|
numberToBytes,
|
||||||
|
numberToBits,
|
||||||
|
numberToHex,
|
||||||
|
textToBits,
|
||||||
|
textToBytes,
|
||||||
|
} from './converters';
|
||||||
var audioContext;
|
var audioContext;
|
||||||
var microphoneStream;
|
var microphoneStream;
|
||||||
var microphoneNode;
|
var microphoneNode;
|
||||||
var analyser;
|
var analyser;
|
||||||
var sentDataTextArea;
|
var sentDataTextArea;
|
||||||
const MAXIMUM_PACKETIZATION_SIZE_BITS = 16;
|
|
||||||
const CRC_BIT_COUNT = 8;
|
|
||||||
|
|
||||||
// bits as they are sent
|
// bits as they are sent
|
||||||
let SENT_ORIGINAL_TEXT = '';
|
let SENT_ORIGINAL_TEXT = '';
|
||||||
@@ -96,6 +106,10 @@ function handleWindowLoad() {
|
|||||||
signalPanel.setTimeoutMilliseconds(60);
|
signalPanel.setTimeoutMilliseconds(60);
|
||||||
|
|
||||||
packetizationPanel.setSizePower(5);
|
packetizationPanel.setSizePower(5);
|
||||||
|
packetizationPanel.setDataSizePower(16);
|
||||||
|
packetizationPanel.setDataSizeCrc(8);
|
||||||
|
packetizationPanel.setDataCrc(16);
|
||||||
|
|
||||||
packetizationPanel.setErrorCorrection(true);
|
packetizationPanel.setErrorCorrection(true);
|
||||||
packetizationPanel.setInterleaving(true);
|
packetizationPanel.setInterleaving(true);
|
||||||
|
|
||||||
@@ -153,6 +167,9 @@ function handleWindowLoad() {
|
|||||||
configurationChanged();
|
configurationChanged();
|
||||||
});
|
});
|
||||||
packetizationPanel.addEventListener('errorCorrectionChange', configurationChanged);
|
packetizationPanel.addEventListener('errorCorrectionChange', configurationChanged);
|
||||||
|
packetizationPanel.addEventListener('dataSizePowerChange', configurationChanged);
|
||||||
|
packetizationPanel.addEventListener('dataSizeCrcChange', configurationChanged);
|
||||||
|
packetizationPanel.addEventListener('dataCrcChange', configurationChanged);
|
||||||
|
|
||||||
availableFskPairsPanel.addEventListener('change', (event) => {
|
availableFskPairsPanel.addEventListener('change', (event) => {
|
||||||
frequencyGraphPanel.setFskPairs(event.selected);
|
frequencyGraphPanel.setFskPairs(event.selected);
|
||||||
@@ -252,6 +269,10 @@ function updateStreamManager() {
|
|||||||
StreamManager.setPacketEncoding(
|
StreamManager.setPacketEncoding(
|
||||||
packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
|
packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
|
||||||
);
|
);
|
||||||
|
const xferCountLength = packetizationPanel.getDataSizePower();
|
||||||
|
const xferCountCrcLength = xferCountLength === 0 ? 0 : packetizationPanel.getDataSizeCrc();
|
||||||
|
const xferCrcLength = packetizationPanel.getDataCrc();
|
||||||
|
|
||||||
StreamManager.changeConfiguration({
|
StreamManager.changeConfiguration({
|
||||||
bitsPerPacket: PacketUtils.getPacketMaxBitCount(),
|
bitsPerPacket: PacketUtils.getPacketMaxBitCount(),
|
||||||
segmentsPerPacket: PacketUtils.getPacketSegmentCount(),
|
segmentsPerPacket: PacketUtils.getPacketSegmentCount(),
|
||||||
@@ -259,11 +280,15 @@ function updateStreamManager() {
|
|||||||
streamHeaders: {
|
streamHeaders: {
|
||||||
'transfer byte count': {
|
'transfer byte count': {
|
||||||
index: 0,
|
index: 0,
|
||||||
length: MAXIMUM_PACKETIZATION_SIZE_BITS
|
length: xferCountLength
|
||||||
},
|
},
|
||||||
'transfer byte count crc': {
|
'transfer byte count crc': {
|
||||||
index: MAXIMUM_PACKETIZATION_SIZE_BITS,
|
index: xferCountLength,
|
||||||
length: CRC_BIT_COUNT
|
length: xferCountCrcLength
|
||||||
|
},
|
||||||
|
'transfer byte crc': {
|
||||||
|
index: xferCountLength + xferCountCrcLength,
|
||||||
|
length: xferCrcLength
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -276,8 +301,9 @@ function updatePacketUtils() {
|
|||||||
PacketUtils.changeConfiguration({
|
PacketUtils.changeConfiguration({
|
||||||
segmentDurationMilliseconds: signalPanel.getSegmentDurationMilliseconds(),
|
segmentDurationMilliseconds: signalPanel.getSegmentDurationMilliseconds(),
|
||||||
packetSizeBitCount: packetizationPanel.getSizePower(),
|
packetSizeBitCount: packetizationPanel.getSizePower(),
|
||||||
dataSizeBitCount: MAXIMUM_PACKETIZATION_SIZE_BITS,
|
dataSizeBitCount: packetizationPanel.getDataSizePower(),
|
||||||
dataSizeCrcBitCount: CRC_BIT_COUNT,
|
dataSizeCrcBitCount: packetizationPanel.getDataSizeCrc(),
|
||||||
|
dataCrcBitCount: packetizationPanel.getDataCrc(),
|
||||||
bitsPerSegment,
|
bitsPerSegment,
|
||||||
packetEncoding: packetizationPanel.getErrorCorrection(),
|
packetEncoding: packetizationPanel.getErrorCorrection(),
|
||||||
packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE,
|
packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE,
|
||||||
@@ -375,22 +401,20 @@ function percentInFrequency(hz, frequencyResolution) {
|
|||||||
return percent;
|
return percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function logSent(text) {
|
|
||||||
// display what is being sent
|
|
||||||
sentDataTextArea.value += text + '\n';
|
|
||||||
sentDataTextArea.scrollTop = sentDataTextArea.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendBytes(bytes) {
|
function sendBytes(bytes) {
|
||||||
const byteCount = bytes.length;
|
const byteCount = bytes.length;
|
||||||
if(byteCount === 0) {
|
if(byteCount === 0) {
|
||||||
logSent('Nothing to send!');
|
document.getElementById('sent-data').innerText = 'Nothing to send!';
|
||||||
return;
|
return;
|
||||||
} else if(byteCount > 0xFFFF) {
|
} else if(byteCount > packetizationPanel.getDataSize()) {
|
||||||
logSent('Too much to send!');
|
document.getElementById('sent-data').innerText = `Attempted to send too much data. Limit is ${Humanize.byteSize(packetizationPanel.getDataSize())}. Tried to send ${Humanize.byteSize(byteCount)}`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioReceiver.reset();
|
||||||
|
StreamManager.reset();
|
||||||
|
frequencyGraphPanel.start();
|
||||||
|
|
||||||
const bits = bytesToBits(bytes);
|
const bits = bytesToBits(bytes);
|
||||||
|
|
||||||
SENT_ORIGINAL_TEXT = bytesToText(bytes);
|
SENT_ORIGINAL_TEXT = bytesToText(bytes);
|
||||||
@@ -398,12 +422,44 @@ function sendBytes(bytes) {
|
|||||||
|
|
||||||
// packetization headers
|
// packetization headers
|
||||||
// data length
|
// data length
|
||||||
const dataLengthBits = numberToBits(bytes.length, MAXIMUM_PACKETIZATION_SIZE_BITS);
|
let dataLengthBits = [];
|
||||||
// crc on data length
|
let dataLengthCrcBits = [];
|
||||||
const dataLengthCrcBits = numberToBits(CRC.check(bitsToBytes(dataLengthBits), CRC_BIT_COUNT), CRC_BIT_COUNT);
|
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
|
// prefix with headers
|
||||||
bits.unshift(...dataLengthBits, ...dataLengthCrcBits);
|
bits.unshift(
|
||||||
|
...dataLengthBits,
|
||||||
|
...dataLengthCrcBits,
|
||||||
|
...dataCrcBits
|
||||||
|
);
|
||||||
|
|
||||||
const bitCount = bits.length;
|
const bitCount = bits.length;
|
||||||
|
|
||||||
@@ -418,13 +474,10 @@ function sendBytes(bytes) {
|
|||||||
const packetCount = PacketUtils.getPacketCount(bitCount);
|
const packetCount = PacketUtils.getPacketCount(bitCount);
|
||||||
const totalDurationSeconds = PacketUtils.getDataTransferDurationSeconds(bitCount);
|
const totalDurationSeconds = PacketUtils.getDataTransferDurationSeconds(bitCount);
|
||||||
|
|
||||||
const errorCorrectionBits = [];
|
|
||||||
|
|
||||||
AudioSender.beginAt(startSeconds);
|
AudioSender.beginAt(startSeconds);
|
||||||
// send all packets
|
// send all packets
|
||||||
for(let i = 0; i < packetCount; i++) {
|
for(let i = 0; i < packetCount; i++) {
|
||||||
let packet = PacketUtils.getPacketBits(bits, i);
|
let packet = PacketUtils.getPacketBits(bits, i);
|
||||||
errorCorrectionBits.push(...packet);
|
|
||||||
SENT_ENCODED_BITS.push(...packet);
|
SENT_ENCODED_BITS.push(...packet);
|
||||||
if(packet.length > packetBitCount) {
|
if(packet.length > packetBitCount) {
|
||||||
console.error('Too many bits in the packet. tried to send %s, limited to %s', 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() {
|
function handleStreamManagerChange() {
|
||||||
const channelCount = availableFskPairsPanel.getSelectedFskPairs().length;
|
const channelCount = availableFskPairsPanel.getSelectedFskPairs().length;
|
||||||
let allRawBits = StreamManager.getStreamBits();
|
let allRawBits = StreamManager.getStreamBits();
|
||||||
let allEncodedBits = StreamManager.getAllPacketBits();
|
let allEncodedBits = StreamManager.getAllPacketBits();
|
||||||
let allDecodedBits = getTransferredCorrectedBits();
|
let allDecodedBits = StreamManager.getAllPacketBitsDecoded();
|
||||||
|
|
||||||
// get packet data before removing decoded bits
|
// get packet data before removing decoded bits
|
||||||
const transmissionByteCount = parseTransmissionByteCount(allDecodedBits);
|
const transmissionByteCount = StreamManager.getTransferByteCount();
|
||||||
const transmissionByteCountCrc = parseTransmissionByteCountCrc(allDecodedBits)
|
const transmissionByteCountCrc = StreamManager.getTransferByteCountCrc();
|
||||||
const transmissionByteCountActualCrc = CRC.check(
|
const transmissionByteCountActualCrc = StreamManager.getTransferByteCountActualCrc();
|
||||||
bitsToBytes(
|
|
||||||
numberToBits(
|
const trustedLength = StreamManager.isTransferByteCountTrusted();
|
||||||
transmissionByteCount,
|
|
||||||
MAXIMUM_PACKETIZATION_SIZE_BITS
|
|
||||||
)
|
|
||||||
), CRC_BIT_COUNT
|
|
||||||
);
|
|
||||||
const trustedLength = transmissionByteCountCrc === transmissionByteCountActualCrc;
|
|
||||||
const totalBitsTransferring = parseTotalBitsTransferring(allDecodedBits);
|
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
|
// reduce all decoded bits based on original data sent
|
||||||
allDecodedBits = removeDecodedHeadersAndPadding(allDecodedBits);
|
allDecodedBits = removeDecodedHeadersAndPadding(allDecodedBits);
|
||||||
|
|
||||||
@@ -604,10 +647,10 @@ function handleStreamManagerChange() {
|
|||||||
'');
|
'');
|
||||||
document.getElementById('received-packet-original-bytes').innerText = transmissionByteCount.toLocaleString();
|
document.getElementById('received-packet-original-bytes').innerText = transmissionByteCount.toLocaleString();
|
||||||
const packetCrc = document.getElementById('received-packet-original-bytes-crc');
|
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';
|
packetCrc.className = trustedLength ? 'bit-correct' : 'bit-wrong';
|
||||||
if(!trustedLength) {
|
if(!trustedLength) {
|
||||||
packetCrc.innerText += ' (Expected 0x' + asHex(2)(transmissionByteCountActualCrc) + ')';
|
packetCrc.innerText += ' (Expected ' + numberToHex(packetizationPanel.getDataSizeCrc())(transmissionByteCountActualCrc) + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('received-encoded-bits-error-percent').innerText = (
|
document.getElementById('received-encoded-bits-error-percent').innerText = (
|
||||||
@@ -619,44 +662,23 @@ function handleStreamManagerChange() {
|
|||||||
document.getElementById('received-decoded-bits-error-percent').innerText = (
|
document.getElementById('received-decoded-bits-error-percent').innerText = (
|
||||||
Math.floor((1 - (correctedDecodedBits / allDecodedBits.length)) * 10000) * 0.01
|
Math.floor((1 - (correctedDecodedBits / allDecodedBits.length)) * 10000) * 0.01
|
||||||
).toLocaleString();
|
).toLocaleString();
|
||||||
// ArrayBuffer / ArrayBufferView
|
|
||||||
const receivedText = bitsToText(allDecodedBits);
|
const bytes = StreamManager.getDataBytes();
|
||||||
|
const receivedText = bytesToText(bytes);
|
||||||
|
|
||||||
messagePanel.setReceived(
|
messagePanel.setReceived(
|
||||||
receivedText.split('').reduce(textExpectorReducer(SENT_ORIGINAL_TEXT), '')
|
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() {
|
function parseTotalBitsTransferring() {
|
||||||
const dataByteCount = parseTransmissionByteCount();
|
const dataByteCount = StreamManager.getTransferByteCount();
|
||||||
const bitCount = PacketUtils.getPacketizationBitCountFromByteCount(dataByteCount);
|
const bitCount = PacketUtils.getPacketizationBitCountFromByteCount(dataByteCount);
|
||||||
const segments = getTotalSegmentCount(bitCount);
|
const segments = getTotalSegmentCount(bitCount);
|
||||||
return segments * availableFskPairsPanel.getSelectedFskPairs().length;
|
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) {
|
function removeEncodedPadding(bits) {
|
||||||
const sizeBits = MAXIMUM_PACKETIZATION_SIZE_BITS;
|
const sizeBits = packetizationPanel.getDataSizePower();
|
||||||
const dataSize = ERROR_CORRECTION_DATA_SIZE;
|
const dataSize = ERROR_CORRECTION_DATA_SIZE;
|
||||||
const blockSize = ERROR_CORRECTION_BLOCK_SIZE;
|
const blockSize = ERROR_CORRECTION_BLOCK_SIZE;
|
||||||
let bitsNeeded = sizeBits;
|
let bitsNeeded = sizeBits;
|
||||||
@@ -675,7 +697,10 @@ function removeEncodedPadding(bits) {
|
|||||||
const dataByteCount = StreamManager.getTransferByteCount();
|
const dataByteCount = StreamManager.getTransferByteCount();
|
||||||
|
|
||||||
// determine how many decoded bits need to be sent (including the size)
|
// 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;
|
let encodingBitCount = totalBits;
|
||||||
if(packetizationPanel.getErrorCorrection()) {
|
if(packetizationPanel.getErrorCorrection()) {
|
||||||
const blocks = Math.ceil(encodingBitCount / dataSize);
|
const blocks = Math.ceil(encodingBitCount / dataSize);
|
||||||
@@ -691,17 +716,51 @@ function removeEncodedPadding(bits) {
|
|||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
function removeDecodedHeadersAndPadding(bits) {
|
function removeDecodedHeadersAndPadding(bits) {
|
||||||
const sizeBits = MAXIMUM_PACKETIZATION_SIZE_BITS;
|
const sizeBitCount = packetizationPanel.getDataSizePower();
|
||||||
let bitCount = bits.length / 8;
|
const sizeCrcBitCount = packetizationPanel.getDataSizeCrc();
|
||||||
if(bits.length >= sizeBits) {
|
const dataCrcBitCount = packetizationPanel.getDataCrc();
|
||||||
bitCount = bitsToInt(bits.slice(0, sizeBits), sizeBits);
|
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 bitReducer = (packetBitSize, blockSize, blockCallback) => (all, bit, i) => {
|
||||||
const packetIndex = Math.floor(i / packetBitSize);
|
const packetIndex = Math.floor(i / packetBitSize);
|
||||||
@@ -780,63 +839,10 @@ function getAudioContext() {
|
|||||||
}
|
}
|
||||||
return audioContext;
|
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() {
|
function handleSendButtonClick() {
|
||||||
if(messagePanel.getSendButtonText() === 'Stop') {
|
if(messagePanel.getSendButtonText() === 'Stop') {
|
||||||
AudioSender.stop();
|
AudioSender.stop();
|
||||||
} else {
|
} else {
|
||||||
AudioReceiver.reset();
|
|
||||||
StreamManager.reset();
|
|
||||||
frequencyGraphPanel.start();
|
|
||||||
const text = messagePanel.getMessage();
|
const text = messagePanel.getMessage();
|
||||||
sendBytes(textToBytes(text));
|
sendBytes(textToBytes(text));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user