Setup packetization and available fks pairs

This commit is contained in:
Lewis Moten
2024-05-12 00:21:58 -04:00
parent 716aa046c4
commit 51a51d7e96
7 changed files with 246 additions and 97 deletions

View File

@@ -9,6 +9,17 @@ export function byteSize(count) {
count = Math.floor(count * 10) * 0.1 count = Math.floor(count * 10) * 0.1
return `${count.toLocaleString()} ${units[unitIndex]}` return `${count.toLocaleString()} ${units[unitIndex]}`
} }
export function hertz(hz) {
let unitIndex = 0;
const units = ['Hz', 'kHz', 'mHz', 'gHz', 'tHz', 'pHz'];
while(hz >= 1000) {
hz /= 1000;
unitIndex++;
if(unitIndex === units.length - 1) break;
}
hz = Math.floor(hz * 1000) * 0.001
return `${hz.toLocaleString()} ${units[unitIndex]}`
}
export function bitsPerSecond(bps) { export function bitsPerSecond(bps) {
let unitIndex = 0; let unitIndex = 0;
const units = ['baud', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps']; const units = ['baud', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps'];

View File

@@ -0,0 +1,123 @@
import BasePanel from './BasePanel';
import { hertz } from '../Humanize';
class AvailableFskPairsPanel extends BasePanel {
constructor() {
super('Available FSK Pairs');
this.exclude = [];
this.fskPairs = [];
this.sampleRate = 48000;
this.addCanvas('fsk-spectrum', 200, 32);
this.addNewLine();
this.addCheckboxes('fsk-pairs', this.fskPairs);
this.addDynamicText('fsk-available', 'None');
this.addEventListener('select', this.handleSelect);
this.drawFskSpectrum();
};
setSampleRate = (value) => {
this.sampleRate = value;
this.drawFskSpectrum();
}
handleSelect = (event) => {
if(event.checked) {
this.exclude = this.exclude.filter(id => id !== event.id)
} else if(!this.exclude.includes(event.id)) {
this.exclude.push(event.id);
}
this.drawFskSpectrum();
};
getSelectedFskPairs = () => this.fskPairs
.filter(this.isSelected);
getSelectedIndexes = () => this.fskPairs.map((_, id) => id).filter(id => !this.exclude.includes(id));
setSelectedIndexes = (values) => {
this.exclude = values;
this.setFskPairs(this.fskPairs);
}
setFskPairs = fskPairs => {
this.fskPairs = fskPairs;
this.setValueById('fsk-available', fskPairs.length === 0 ? 'None' : '');
const items = fskPairs.map(([lowHz, highHz], index) => ({
text: `${index}: ${hertz(lowHz)} / ${hertz(highHz)}`,
id: index,
value: index,
checked: !this.exclude.includes(index),
eventName: 'select'
}));
this.replaceCheckedInputs('checkbox', 'fsk-pairs', items);
this.drawFskSpectrum();
}
drawFskSpectrum = () => {
const ultimateFrequency = this.sampleRate / 2;
const fskPairs = this.fskPairs;
const canvas = this.getElement('fsk-spectrum');
const ctx = canvas.getContext('2d');
const {height, width} = canvas;
ctx.clearRect(0, 0, width, height);
if(fskPairs.length === 0) return;
const minHz = fskPairs.reduce(
(min, fsk) => {
const lowestHz = fsk.reduce((min, hz) => Math.min(min, hz), Infinity)
return Math.min(min, lowestHz)
}, Infinity
);
const maxHz = fskPairs.reduce(
(max, fsk) => {
const lowestHz = fsk.reduce((max, hz) => Math.max(max, hz), -Infinity)
return Math.max(max, lowestHz)
}, -Infinity
);
const range = maxHz - minHz;
// Human Hearing
let x1 = Math.max(0, ((20-minHz)/range) * width);
let x2 = Math.min(width, ((20000-minHz)/range) * width);
if(x1 !== x2) {
ctx.fillStyle = 'hsla(0, 0%, 100%, 20%)';
ctx.fillRect(
x1,
0,
x2 - x1,
height
);
}
// Telephone
x1 = Math.max(0, ((300-minHz)/range) * width);
x2 = Math.min(width, ((3400-minHz)/range) * width);
if(x1 !== x2) {
ctx.fillStyle = 'hsla(60, 50%, 50%, 20%)';
ctx.fillRect(
x1,
0,
x2 - x1,
height
);
}
ctx.lineWith = 1;
const plotHz = hz => {
const hue = Math.floor(hz/ultimateFrequency * 360);
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
const x = ((hz-minHz) / range) * width;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
fskPairs.filter(this.isSelected).forEach(fsk => fsk.forEach(plotHz));
}
isSelected = (delude, allude) => !this.exclude.includes(allude);
}
export default AvailableFskPairsPanel;

View File

@@ -65,6 +65,20 @@ class BasePanel {
this.append(select); this.append(select);
} }
addCheckedInputs = (type, name, items, value) => { addCheckedInputs = (type, name, items, value) => {
const div = document.createElement('div');
div.id = this.childId(name);
this.createCheckedInputs(type, name, items, value)
.forEach(element => div.appendChild(element));
this.append(div);
}
replaceCheckedInputs = (type, name, items, value) => {
const div = this.getElement(name);
div.innerHTML = '';
this.createCheckedInputs(type, name, items, value)
.forEach(element => div.appendChild(element));
}
createCheckedInputs = (type, name, items, value) => {
const elements = [];
items.forEach(({id, text, checked = false, eventName = 'change'})=> { items.forEach(({id, text, checked = false, eventName = 'change'})=> {
const label = document.createElement('label'); const label = document.createElement('label');
label.for = this.childId(id); label.for = this.childId(id);
@@ -72,9 +86,10 @@ class BasePanel {
label.appendChild(input); label.appendChild(input);
const textNode = document.createTextNode(text); const textNode = document.createTextNode(text);
label.append(textNode); label.append(textNode);
this.append(label); elements.push(label);
this.addNewLine(); elements.push(document.createElement('br'));
}); });
return elements;
} }
addInputText = (id, value, options = {}) => { addInputText = (id, value, options = {}) => {
this.append(this.createInput(id, value, {...options, type: 'text'})); this.append(this.createInput(id, value, {...options, type: 'text'}));

View File

@@ -1,11 +1,14 @@
import BasePanel from './BasePanel'; import BasePanel from './BasePanel';
class FrequencyPanel extends BasePanel { class FrequencyPanel extends BasePanel {
constructor(sampleRate = 48000) { constructor() {
super('Frequencies'); super('Frequencies');
this.sampleRate = sampleRate; this.sampleRate = 48000;
const ultimateFrequency = sampleRate / 2; const ultimateFrequency = this.sampleRate / 2;
this.addCanvas('frequency-spectrum', 200, 32);
this.addNewLine();
this.openField('Minimum'); this.openField('Minimum');
this.addInputNumber('minimum-frequency', 0, {min: 0, max: ultimateFrequency, eventName: 'minimumFrequencyChange'}); this.addInputNumber('minimum-frequency', 0, {min: 0, max: ultimateFrequency, eventName: 'minimumFrequencyChange'});
@@ -35,9 +38,6 @@ class FrequencyPanel extends BasePanel {
this.addInputNumber('multi-fsk-padding', 0, {min: 0, max: 20, eventName: 'multiFskPaddingChange'}); this.addInputNumber('multi-fsk-padding', 0, {min: 0, max: 20, eventName: 'multiFskPaddingChange'});
this.closeField(); this.closeField();
this.addCanvas('frequency-spectrum', 200, 32);
this.addNewLine();
this.openField('FSK Pairs Available'); this.openField('FSK Pairs Available');
this.addDynamicText('fsk-count', 'N/A'); this.addDynamicText('fsk-count', 'N/A');
this.closeField(); this.closeField();
@@ -51,6 +51,11 @@ class FrequencyPanel extends BasePanel {
this.originalFskPairs = this.getFskPairs(); this.originalFskPairs = this.getFskPairs();
this.drawFrequencySpectrum(); this.drawFrequencySpectrum();
}; };
setSampleRate = (value) => {
this.sampleRate = value;
this.checkFskPairsChanged();
}
getMinimumFrequency = () => parseInt(this.getValueById('minimum-frequency')); getMinimumFrequency = () => parseInt(this.getValueById('minimum-frequency'));
setMinimumFrequency = value => { setMinimumFrequency = value => {
this.setValueById('minimum-frequency', value); this.setValueById('minimum-frequency', value);
@@ -96,10 +101,8 @@ class FrequencyPanel extends BasePanel {
if(original.length !== current.length) { if(original.length !== current.length) {
changed = true; changed = true;
} else { } else {
changed = original.some( const currentHz = current.flat();
(fsk, fskIndex) => { changed = original.flat().some((hz, i) => hz !== currentHz[i]);
return fsk.some((hz, hzIndex) => hz !== original[fskIndex][hzIndex]);
})
} }
if(changed) { if(changed) {
this.originalFskPairs = current; this.originalFskPairs = current;

View File

@@ -0,0 +1,43 @@
import BasePanel from './BasePanel';
import { byteSize } from '../Humanize';
class PacketizationPanel extends BasePanel {
constructor() {
super('Packetization');
this.openField('Packet 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.closeField();
this.addSection('Encoding');
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()});
};
getSizePower = () => parseInt(this.getValueById('size-power'));
setSizePower = (value) => {
this.setValueById('size-power', value);
this.handleSizePowerChange({value});
}
getSize = () => 2 ** this.getSizePower();
handleSizePowerChange = () => {
this.setValueById('size', byteSize(this.getSize()));
}
getInterleaving = () => this.getCheckedById('interleaving');
setInterleaving = (value) => this.setCheckedById('interleaving', value);
getErrorCorrection = () => this.getCheckedById('error-correction');
setErrorCorrection = (value) => this.setCheckedById('error-correction', value);
}
export default PacketizationPanel;

View File

@@ -10,28 +10,6 @@
<body> <body>
<h1>Data Over Audio</h1> <h1>Data Over Audio</h1>
<div class="panels" id="panel-container"> <div class="panels" id="panel-container">
<div>
<h2>Packetizaton</h2>
<div>
Packet Size:
2^<input id="packet-size-power" type="number" min="0" max="16">
<span id="packet-size"></span>
<br>
<h4>Encoding</h4>
<label>
<input type="checkbox" id="periodic-interleaving" checked>Interleaving
</label><br />
<label>
<input type="checkbox" id="error-correction-hamming" checked>Error Correction
</label><br>
</div>
</div>
<div>
<h2>Channels</h2>
<div>
<ol id="channel-list" start="0"></ol>
</div>
</div>
<div class="chart"> <div class="chart">
<h2>Frequency Graph</h2> <h2>Frequency Graph</h2>
<div> <div>

102
index.js
View File

@@ -12,6 +12,8 @@ import MessagePanel from "./Panels/MessagePanel";
import CodePanel from "./Panels/CodePanel"; import CodePanel from "./Panels/CodePanel";
import FrequencyPanel from "./Panels/FrequencyPanel"; import FrequencyPanel from "./Panels/FrequencyPanel";
import SignalPanel from "./Panels/SignalPanel"; import SignalPanel from "./Panels/SignalPanel";
import PacketizationPanel from "./Panels/PacketizationPanel";
import AvailableFskPairsPanel from "./Panels/AvailableFskPairsPanel";
var audioContext; var audioContext;
var microphoneStream; var microphoneStream;
@@ -32,8 +34,6 @@ let SENT_TRANSFER_BITS = []; // bits sent in the transfer
let EXCLUDED_CHANNELS = []; let EXCLUDED_CHANNELS = [];
var MAX_BITS_DISPLAYED_ON_GRAPH = 79; var MAX_BITS_DISPLAYED_ON_GRAPH = 79;
var HAMMING_ERROR_CORRECTION = true;
let PERIODIC_INTERLEAVING = true;
const ERROR_CORRECTION_BLOCK_SIZE = 7; const ERROR_CORRECTION_BLOCK_SIZE = 7;
const ERROR_CORRECTION_DATA_SIZE = 4; const ERROR_CORRECTION_DATA_SIZE = 4;
@@ -49,7 +49,6 @@ let SAMPLES = [];
var bitStart = []; var bitStart = [];
var PAUSE = false; var PAUSE = false;
var PAUSE_AFTER_END = true; var PAUSE_AFTER_END = true;
var PACKET_SIZE_BITS = 5; // 32 bytes, 256 bits
let USED_FSK = []; let USED_FSK = [];
let AVAILABLE_FSK = []; let AVAILABLE_FSK = [];
@@ -60,9 +59,13 @@ const bitsSentPanel = new CodePanel('Bits Sent');
const bitsReceivedPanel = new CodePanel('Bits Received'); const bitsReceivedPanel = new CodePanel('Bits Received');
const frequencyPanel = new FrequencyPanel(); const frequencyPanel = new FrequencyPanel();
const signalPanel = new SignalPanel(); const signalPanel = new SignalPanel();
const packetizationPanel = new PacketizationPanel();
const availableFskPairsPanel = new AvailableFskPairsPanel();
function handleWindowLoad() { function handleWindowLoad() {
const panelContainer = document.getElementById('panel-container'); const panelContainer = document.getElementById('panel-container');
panelContainer.prepend(availableFskPairsPanel.getDomElement());
panelContainer.prepend(packetizationPanel.getDomElement());
panelContainer.prepend(signalPanel.getDomElement()); panelContainer.prepend(signalPanel.getDomElement());
panelContainer.prepend(frequencyPanel.getDomElement()); panelContainer.prepend(frequencyPanel.getDomElement());
panelContainer.prepend(bitsReceivedPanel.getDomElement()); panelContainer.prepend(bitsReceivedPanel.getDomElement());
@@ -94,6 +97,12 @@ function handleWindowLoad() {
signalPanel.setAmplitudeThreshold(0.78); signalPanel.setAmplitudeThreshold(0.78);
signalPanel.setSmoothingTimeConstant(0); signalPanel.setSmoothingTimeConstant(0);
packetizationPanel.setSizePower(5);
packetizationPanel.setErrorCorrection(true);
packetizationPanel.setInterleaving(true);
availableFskPairsPanel.setFskPairs(frequencyPanel.getFskPairs());
// Communications Events // Communications Events
communicationsPanel.addEventListener('listeningChange', handleChangeListening); communicationsPanel.addEventListener('listeningChange', handleChangeListening);
communicationsPanel.addEventListener('sendSpeakersChange', handleChangeSendSpeakers); communicationsPanel.addEventListener('sendSpeakersChange', handleChangeSendSpeakers);
@@ -110,12 +119,24 @@ function handleWindowLoad() {
}); });
frequencyPanel.addEventListener('fskPaddingChange', configurationChanged); frequencyPanel.addEventListener('fskPaddingChange', configurationChanged);
frequencyPanel.addEventListener('multiFskPaddingChange', configurationChanged); frequencyPanel.addEventListener('multiFskPaddingChange', configurationChanged);
frequencyPanel.addEventListener('fskPairsChange', ({value}) => {
availableFskPairsPanel.setFskPairs(value);
});
signalPanel.addEventListener('waveformChange', updateAudioSender); signalPanel.addEventListener('waveformChange', updateAudioSender);
signalPanel.addEventListener('segmentDurationChange', configurationChanged); signalPanel.addEventListener('segmentDurationChange', configurationChanged);
signalPanel.addEventListener('amplitudeThresholdChange', configurationChanged); signalPanel.addEventListener('amplitudeThresholdChange', configurationChanged);
signalPanel.addEventListener('smoothingConstantChange', configurationChanged); signalPanel.addEventListener('smoothingConstantChange', configurationChanged);
packetizationPanel.addEventListener('sizePowerChange', configurationChanged);
packetizationPanel.addEventListener('interleavingChange', () => {
StreamManager.setSegmentEncoding(
packetizationPanel.getInterleaving() ? InterleaverEncoding : undefined
);
configurationChanged();
});
packetizationPanel.addEventListener('errorCorrectionChange', configurationChanged);
// Setup audio sender // Setup audio sender
AudioSender.addEventListener('begin', () => messagePanel.setSendButtonText('Stop')); AudioSender.addEventListener('begin', () => messagePanel.setSendButtonText('Stop'));
AudioSender.addEventListener('send', handleAudioSenderSend); AudioSender.addEventListener('send', handleAudioSenderSend);
@@ -135,27 +156,7 @@ function handleWindowLoad() {
receivedChannelGraph.addEventListener('mouseout', handleReceivedChannelGraphMouseout); receivedChannelGraph.addEventListener('mouseout', handleReceivedChannelGraphMouseout);
receivedChannelGraph.addEventListener('mousemove', handleReceivedChannelGraphMousemove); receivedChannelGraph.addEventListener('mousemove', handleReceivedChannelGraphMousemove);
receivedChannelGraph.addEventListener('click', handleReceivedChannelGraphClick); receivedChannelGraph.addEventListener('click', handleReceivedChannelGraphClick);
document.getElementById('packet-size-power').value = PACKET_SIZE_BITS;
document.getElementById('packet-size').innerText = Humanize.byteSize(2 ** PACKET_SIZE_BITS);
document.getElementById('packet-size-power').addEventListener('input', event => {
PACKET_SIZE_BITS = parseInt(event.target.value);
document.getElementById('packet-size').innerText = Humanize.byteSize(2 ** PACKET_SIZE_BITS);
configurationChanged();
});
document.getElementById('pause-after-end').checked = PAUSE_AFTER_END; document.getElementById('pause-after-end').checked = PAUSE_AFTER_END;
document.getElementById('error-correction-hamming').checked = HAMMING_ERROR_CORRECTION;
document.getElementById('error-correction-hamming').addEventListener('change', event => {
HAMMING_ERROR_CORRECTION = event.target.checked;
configurationChanged();
})
document.getElementById('periodic-interleaving').checked = PERIODIC_INTERLEAVING;
document.getElementById('periodic-interleaving').addEventListener('change', event => {
PERIODIC_INTERLEAVING = event.target.checked;
configurationChanged();
StreamManager.setSegmentEncoding(
PERIODIC_INTERLEAVING ? InterleaverEncoding : undefined
);
});
document.getElementById('pause-after-end').addEventListener('change', event => { document.getElementById('pause-after-end').addEventListener('change', event => {
PAUSE_AFTER_END = event.target.checked; PAUSE_AFTER_END = event.target.checked;
if(!PAUSE_AFTER_END) resumeGraph(); if(!PAUSE_AFTER_END) resumeGraph();
@@ -178,33 +179,6 @@ function updateFrequencyResolution() {
document.getElementById('frequency-count').innerText = frequencyCount.toFixed(2); document.getElementById('frequency-count').innerText = frequencyCount.toFixed(2);
} }
function showChannelList() {
const allChannels = AVAILABLE_FSK;
const channelList = document.getElementById('channel-list');
channelList.innerHTML = "";
allChannels.forEach(([low, high], i) => {
const li = document.createElement('li');
const label = document.createElement('label');
li.appendChild(label);
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = !EXCLUDED_CHANNELS.includes(i);
checkbox.addEventListener('input', event => {
if(event.target.checked) {
EXCLUDED_CHANNELS = EXCLUDED_CHANNELS.filter(channel => channel !== i)
} else {
EXCLUDED_CHANNELS.push(i);
}
configurationChanged();
})
label.append(checkbox);
const text = document.createTextNode(`Low: ${low} Hz High: ${high} Hz`);
label.append(text);
channelList.appendChild(li);
})
drawChannels();
}
function handleAudioSenderSend({bits}) { function handleAudioSenderSend({bits}) {
SENT_TRANSFER_BITS.push(...bits); SENT_TRANSFER_BITS.push(...bits);
showSentBits(); showSentBits();
@@ -217,7 +191,7 @@ function configurationChanged() {
updateStreamManager(); updateStreamManager();
updateAudioSender(); updateAudioSender();
updateAudioReceiver(); updateAudioReceiver();
showChannelList(); drawChannels();
updateFrequencyResolution(); updateFrequencyResolution();
updatePacketStats(); updatePacketStats();
} }
@@ -259,7 +233,7 @@ function updateAudioReceiver() {
} }
function updateStreamManager() { function updateStreamManager() {
StreamManager.setPacketEncoding( StreamManager.setPacketEncoding(
HAMMING_ERROR_CORRECTION ? HammingEncoding : undefined packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
); );
StreamManager.changeConfiguration({ StreamManager.changeConfiguration({
bitsPerPacket: PacketUtils.getPacketMaxBitCount(), bitsPerPacket: PacketUtils.getPacketMaxBitCount(),
@@ -279,16 +253,16 @@ function updateStreamManager() {
} }
function updatePacketUtils() { function updatePacketUtils() {
PacketUtils.setEncoding( PacketUtils.setEncoding(
HAMMING_ERROR_CORRECTION ? HammingEncoding : undefined packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
); );
const bitsPerSegment = USED_FSK.length; const bitsPerSegment = USED_FSK.length;
PacketUtils.changeConfiguration({ PacketUtils.changeConfiguration({
segmentDurationMilliseconds: signalPanel.getSegmentDuration(), segmentDurationMilliseconds: signalPanel.getSegmentDuration(),
packetSizeBitCount: PACKET_SIZE_BITS, packetSizeBitCount: packetizationPanel.getSizePower(),
dataSizeBitCount: MAXIMUM_PACKETIZATION_SIZE_BITS, dataSizeBitCount: MAXIMUM_PACKETIZATION_SIZE_BITS,
dataSizeCrcBitCount: CRC_BIT_COUNT, dataSizeCrcBitCount: CRC_BIT_COUNT,
bitsPerSegment, bitsPerSegment,
packetEncoding: HAMMING_ERROR_CORRECTION, packetEncoding: packetizationPanel.getErrorCorrection(),
packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE, packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE,
packetDecodingBitCount: ERROR_CORRECTION_DATA_SIZE, packetDecodingBitCount: ERROR_CORRECTION_DATA_SIZE,
}); });
@@ -479,11 +453,11 @@ function showSentBits() {
document.getElementById('sent-data').innerHTML = document.getElementById('sent-data').innerHTML =
SENT_ORIGINAL_BITS.reduce(bitReducer( SENT_ORIGINAL_BITS.reduce(bitReducer(
PacketUtils.getPacketMaxBitCount(), PacketUtils.getPacketMaxBitCount(),
HAMMING_ERROR_CORRECTION ? ERROR_CORRECTION_DATA_SIZE : 8 packetizationPanel.getErrorCorrection() ? ERROR_CORRECTION_DATA_SIZE : 8
), ''); ), '');
// error correcting bits // error correcting bits
if(HAMMING_ERROR_CORRECTION) { if(packetizationPanel.getErrorCorrection()) {
document.getElementById('error-correcting-data').innerHTML = document.getElementById('error-correcting-data').innerHTML =
SENT_ENCODED_BITS.reduce(bitReducer( SENT_ENCODED_BITS.reduce(bitReducer(
PacketUtils.getPacketDataBitCount(), PacketUtils.getPacketDataBitCount(),
@@ -506,7 +480,7 @@ function sendPacket(bits, packetStartSeconds) {
const segmentDurationSeconds = PacketUtils.getSegmentDurationSeconds(); const segmentDurationSeconds = PacketUtils.getSegmentDurationSeconds();
for(let i = 0; i < bitCount; i += channelCount) { for(let i = 0; i < bitCount; i += channelCount) {
let segmentBits = bits.slice(i, i + channelCount); let segmentBits = bits.slice(i, i + channelCount);
if(PERIODIC_INTERLEAVING) { if(packetizationPanel.getInterleaving()) {
segmentBits = InterleaverEncoding.encode(segmentBits); segmentBits = InterleaverEncoding.encode(segmentBits);
} }
const segmentIndex = Math.floor(i / channelCount); const segmentIndex = Math.floor(i / channelCount);
@@ -558,7 +532,7 @@ function getTransferredCorrectedBits() {
const packetCount = StreamManager.getPacketReceivedCount(); const packetCount = StreamManager.getPacketReceivedCount();
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) { for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
let packetBits = StreamManager.getPacketBits(packetIndex); let packetBits = StreamManager.getPacketBits(packetIndex);
if(HAMMING_ERROR_CORRECTION) { if(packetizationPanel.getErrorCorrection()) {
bits.push(...HammingEncoding.decode(packetBits)); bits.push(...HammingEncoding.decode(packetBits));
} else { } else {
bits.push(...packetBits); bits.push(...packetBits);
@@ -613,7 +587,7 @@ function handleStreamManagerChange() {
(packetIndex, blockIndex) => `${blockIndex === 0 ? '' : '<br>'}Segment ${blockIndex}: ` (packetIndex, blockIndex) => `${blockIndex === 0 ? '' : '<br>'}Segment ${blockIndex}: `
), ),
'')); ''));
if(HAMMING_ERROR_CORRECTION) { if(packetizationPanel.getErrorCorrection()) {
document.getElementById('received-encoded-bits').innerHTML = allEncodedBits document.getElementById('received-encoded-bits').innerHTML = allEncodedBits
.reduce( .reduce(
bitExpectorReducer( bitExpectorReducer(
@@ -630,7 +604,7 @@ function handleStreamManagerChange() {
bitExpectorReducer( bitExpectorReducer(
SENT_ORIGINAL_BITS, SENT_ORIGINAL_BITS,
PacketUtils.getPacketDataBitCount(), PacketUtils.getPacketDataBitCount(),
HAMMING_ERROR_CORRECTION ? ERROR_CORRECTION_DATA_SIZE : 8 packetizationPanel.getErrorCorrection() ? ERROR_CORRECTION_DATA_SIZE : 8
), ),
''); '');
document.getElementById('received-packet-original-bytes').innerText = transmissionByteCount.toLocaleString(); document.getElementById('received-packet-original-bytes').innerText = transmissionByteCount.toLocaleString();
@@ -693,7 +667,7 @@ function removeEncodedPadding(bits) {
let bitsNeeded = sizeBits; let bitsNeeded = sizeBits;
let blocksNeeded = sizeBits; let blocksNeeded = sizeBits;
// need to calc max bits // need to calc max bits
if(HAMMING_ERROR_CORRECTION) { if(packetizationPanel.getErrorCorrection()) {
blocksNeeded = Math.ceil(sizeBits / dataSize); blocksNeeded = Math.ceil(sizeBits / dataSize);
bitsNeeded = blocksNeeded * blockSize; bitsNeeded = blocksNeeded * blockSize;
} }
@@ -708,7 +682,7 @@ function removeEncodedPadding(bits) {
// 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; const totalBits = (dataByteCount * 8) + MAXIMUM_PACKETIZATION_SIZE_BITS + CRC_BIT_COUNT;
let encodingBitCount = totalBits; let encodingBitCount = totalBits;
if(HAMMING_ERROR_CORRECTION) { if(packetizationPanel.getErrorCorrection()) {
const blocks = Math.ceil(encodingBitCount / dataSize); const blocks = Math.ceil(encodingBitCount / dataSize);
encodingBitCount = blocks * blockSize; encodingBitCount = blocks * blockSize;
} }
@@ -803,6 +777,8 @@ function resetGraphData() {
function getAudioContext() { function getAudioContext() {
if(!audioContext) { if(!audioContext) {
audioContext = new (window.AudioContext || webkitAudioContext)(); audioContext = new (window.AudioContext || webkitAudioContext)();
frequencyPanel.setSampleRate(audioContext.sampleRate);
availableFskPairsPanel.setSampleRate(audioContext.sampleRate);
} }
if(audioContext.state === 'suspended') { if(audioContext.state === 'suspended') {
audioContext.resume(); audioContext.resume();