send data over multiple packets

This commit is contained in:
Lewis Moten
2024-05-08 00:21:55 -04:00
parent bc26da11f3
commit f7e2dc82cc

175
index.js
View File

@@ -274,7 +274,7 @@ function updatePacketStats() {
document.getElementById('data-byte-count').innerText = (bits.length / 8).toLocaleString(); document.getElementById('data-byte-count').innerText = (bits.length / 8).toLocaleString();
document.getElementById('data-bit-count').innerText = bits.length.toLocaleString(); document.getElementById('data-bit-count').innerText = bits.length.toLocaleString();
document.getElementById('packet-bit-count').innerText = getPacketBitCount().toLocaleString(); document.getElementById('packet-bit-count').innerText = getPacketBitCount().toLocaleString();
document.getElementById('packet-count').innerText = getPacketCount(bits).toLocaleString(); document.getElementById('packet-count').innerText = getPacketCount(bits.length).toLocaleString();
document.getElementById('packet-error-correction').innerText = HAMMING_ERROR_CORRECTION ? 'Yes' : 'No'; document.getElementById('packet-error-correction').innerText = HAMMING_ERROR_CORRECTION ? 'Yes' : 'No';
document.getElementById('packet-error-block-count').innerText = getPacketErrorBlockCount().toLocaleString(); document.getElementById('packet-error-block-count').innerText = getPacketErrorBlockCount().toLocaleString();
document.getElementById('packet-data-bit-count').innerText = getPacketDataBitCount().toLocaleString(); document.getElementById('packet-data-bit-count').innerText = getPacketDataBitCount().toLocaleString();
@@ -283,7 +283,7 @@ function updatePacketStats() {
document.getElementById('last-segment-unused-channel-count').innerText = getPacketLastSegmentUnusedChannelCount().toLocaleString() document.getElementById('last-segment-unused-channel-count').innerText = getPacketLastSegmentUnusedChannelCount().toLocaleString()
document.getElementById('packet-transfer-duration').innerText = getPacketDurationSeconds(bits).toLocaleString() + 's'; document.getElementById('packet-transfer-duration').innerText = getPacketDurationSeconds(bits).toLocaleString() + 's';
document.getElementById('segment-transfer-duration').innerText = getSegmentTransferDurationSeconds().toLocaleString() + 's'; document.getElementById('segment-transfer-duration').innerText = getSegmentTransferDurationSeconds().toLocaleString() + 's';
document.getElementById('data-transfer-duration').innerText = getDataTransferDurationSeconds(bits).toLocaleString() + 's'; document.getElementById('data-transfer-duration').innerText = getDataTransferDurationSeconds(bits.length).toLocaleString() + 's';
document.getElementById('segments-per-packet').innerText = getPacketSegmentCount().toLocaleString(); document.getElementById('segments-per-packet').innerText = getPacketSegmentCount().toLocaleString();
} }
function drawChannels() { function drawChannels() {
@@ -471,38 +471,30 @@ function logSent(text) {
sentDataTextArea.scrollTop = sentDataTextArea.scrollHeight; sentDataTextArea.scrollTop = sentDataTextArea.scrollHeight;
} }
function sendBits(bits) { function sendBits(bits) {
if(bits.length === 0) { const bitCount = bits.length;
if(bitCount === 0) {
logSent('No bits to send!'); logSent('No bits to send!');
return; return;
} else if(bits.length % 8 !== 0 || bits.length === 0) {
logSent('Bit count must be divisible by 8.');
return;
} }
const channelCount = getChannels().length;
EXPECTED_BITS = bits.slice(); EXPECTED_BITS = bits.slice();
document.getElementById('sent-data').value = bits.reduce(bitReducer, '');
EXPECTED_ENCODED_BITS = []; EXPECTED_ENCODED_BITS = [];
// add 100ms delay before sending
const startSeconds = audioContext.currentTime + 0.1; const startSeconds = audioContext.currentTime + 0.1;
const startMilliseconds = startSeconds * 1000; const startMilliseconds = startSeconds * 1000;
const packetCount = getPacketCount(bits);
const packetBitCount = getPacketBitCount(); const packetBitCount = getPacketBitCount();
const totalDurationSeconds = getDataTransferDurationSeconds(bits);
const totalDurationMilliseconds = getDataTransferDurationSeconds(bits);
const packetDurationSeconds = getPacketDurationSeconds(); const packetDurationSeconds = getPacketDurationSeconds();
const packetCount = getPacketCount(bitCount);
sendButton.innerText = 'Stop'; const totalDurationSeconds = getDataTransferDurationSeconds(bitCount);
const totalDurationMilliseconds = getDataTransferDurationMilliseconds(bitCount);
createOscillators(startSeconds); createOscillators(startSeconds);
// send all packets // send all packets
for(let i = 0; i < packetCount; i++) { for(let i = 0; i < packetCount; i++) {
let packet = getPacketBits(bits, i); let packet = getPacketBits(bits, i);
if(packet.length > packetBitCount) { if(packet.length > packetBitCount) {
console.error('cant send packet of %s bits with more than %s bits', packet.length, packetBitCount); console.error('Too many bits in the packet.');
disconnectOscillators(); disconnectOscillators();
return; return;
} }
@@ -515,18 +507,35 @@ function sendBits(bits) {
disconnectOscillators, disconnectOscillators,
startMilliseconds + totalDurationMilliseconds startMilliseconds + totalDurationMilliseconds
); );
document.getElementById('encoded-data').value = EXPECTED_ENCODED_BITS.reduce(bitReducer, ''); // show what was sent
document.getElementById('sent-data').value =
EXPECTED_BITS.reduce(bitReducer, '');
document.getElementById('encoded-data').value =
EXPECTED_ENCODED_BITS.reduce(bitReducer, '');
// start the graph moving again // start the graph moving again
resumeGraph(); resumeGraph();
} }
function sendPacket(bits, packetStartSeconds) {
const channels = getChannels();
const channelCount = channels.length;
let bitCount = bits.length;
const segmentDurationSeconds = getSegmentTransferDurationSeconds();
for(let i = 0; i < bitCount; i += channelCount) {
const segmentBits = bits.slice(i, i + channelCount);
const segmentIndex = Math.floor(i / channelCount);
var offsetSeconds = segmentIndex * segmentDurationSeconds;
changeOscillators(segmentBits, packetStartSeconds + offsetSeconds);
}
}
let stopTimeoutId; let stopTimeoutId;
function getDataTransferDurationMilliseconds(bits) { function getDataTransferDurationMilliseconds(bitCount) {
return getPacketCount(bits) * getPacketDurationMilliseconds(); return getPacketCount(bitCount) * getPacketDurationMilliseconds();
} }
function getDataTransferDurationSeconds(bits) { function getDataTransferDurationSeconds(bitCount) {
return getDataTransferDurationMilliseconds(bits) / 1000; return getDataTransferDurationMilliseconds(bitCount) / 1000;
} }
function getPacketDurationMilliseconds() { function getPacketDurationMilliseconds() {
return getPacketSegmentCount() * SEGMENT_DURATION; return getPacketSegmentCount() * SEGMENT_DURATION;
@@ -537,17 +546,23 @@ function getPacketDurationSeconds() {
function getSegmentTransferDurationSeconds() { function getSegmentTransferDurationSeconds() {
return SEGMENT_DURATION / 1000; return SEGMENT_DURATION / 1000;
} }
function getPacketByteCount() {
return 2 ** PACKET_SIZE_BITS;
}
function getPacketBitCount() {
return getPacketByteCount() * 8;
}
function getPacketSegmentCount() { function getPacketSegmentCount() {
return Math.ceil(getPacketBitCount() / getChannels().length); return Math.ceil(getPacketBitCount() / getChannels().length);
} }
function getPacketCount(bits) { function getPacketCount(bitCount) {
if(!canSendPacket()) return 0; if(!canSendPacket()) return 0;
// How many data bits will be encoded in our packet? // How many data bits will be encoded in our packet?
let dataBitCount = getPacketDataBitCount(); let dataBitCount = getPacketDataBitCount();
// Return the total number of packets needed to send all data // Return the total number of packets needed to send all data
return Math.ceil(bits.length / dataBitCount); return Math.ceil(bitCount / dataBitCount);
} }
function getPacketBits(bits, packetIndex) { function getPacketBits(bits, packetIndex) {
if(!canSendPacket()) return []; if(!canSendPacket()) return [];
@@ -590,14 +605,6 @@ function getPacketErrorBlockCount() {
ERROR_CORRECTION_BLOCK_SIZE ERROR_CORRECTION_BLOCK_SIZE
); );
} }
function getPacketByteCount() {
// Convert packet size as power of 2
return 2 ** PACKET_SIZE_BITS;
}
function getPacketBitCount() {
// 8 bits per byte
return getPacketByteCount() * 8;
}
function canSendPacket() { function canSendPacket() {
const max = getPacketBitCount(); const max = getPacketBitCount();
// Need at least 1 bit to send // Need at least 1 bit to send
@@ -618,7 +625,7 @@ function getPacketUnusedBitCount() {
return getPacketBitCount() - getPacketDataBitCount(); return getPacketBitCount() - getPacketDataBitCount();
} }
function getPacketLastUnusedBitCount(bits) { function getPacketLastUnusedBitCount(bits) {
const packetCount = getPacketCount(bits); const packetCount = getPacketCount(bits.length);
const availableBits = getPacketBitCount(); const availableBits = getPacketBitCount();
const usedBits = getPacketUsedBits(bits, packetCount-1).length; const usedBits = getPacketUsedBits(bits, packetCount-1).length;
return availableBits - usedBits; return availableBits - usedBits;
@@ -636,13 +643,8 @@ function padArray(values, length, value) {
while(values.length < length) values.push(value); while(values.length < length) values.push(value);
return values; return values;
} }
const CHANNEL_OSCILLATORS = []; const CHANNEL_OSCILLATORS = [];
function getOscillators() {
return CHANNEL_OSCILLATORS;
}
function createOscillators(streamStartSeconds) { function createOscillators(streamStartSeconds) {
console.log('create oscillators', streamStartSeconds);
const oscillators = getOscillators(); const oscillators = getOscillators();
if(oscillators.length !== 0) disconnectOscillators(); if(oscillators.length !== 0) disconnectOscillators();
var audioContext = getAudioContext(); var audioContext = getAudioContext();
@@ -657,21 +659,26 @@ function createOscillators(streamStartSeconds) {
oscillator.start(streamStartSeconds); oscillator.start(streamStartSeconds);
oscillators.push(oscillator); oscillators.push(oscillator);
} }
sendButton.innerText = 'Stop';
return oscillators; return oscillators;
} }
function disconnectOscillators() { function getOscillators() {
console.log('disconnect oscillators'); return CHANNEL_OSCILLATORS;
stopOscillators(getAudioContext().currentTime); }
function changeOscillators(bits, startSeconds) {
const oscillators = getOscillators(); const oscillators = getOscillators();
oscillators.forEach( getChannels().forEach((channel, i) => {
oscillator => oscillator.disconnect() // missing bits past end of bit stream set to zero
) const isHigh = bits[i] ?? 0;
oscillators.length = 0; const oscillator = oscillators[i];
sendButton.innerText = 'Send'; // already at correct frequency
stopTimeoutId = undefined; if(oscillator.on === isHigh) return;
oscillator.on = isHigh;
const hz = channel[isHigh ? 1 : 0];
oscillator.frequency.setValueAtTime(hz, startSeconds);
});
} }
function stopOscillators(streamEndSeconds) { function stopOscillators(streamEndSeconds) {
console.log('stop oscillators', streamEndSeconds);
const channels = getChannels(); const channels = getChannels();
const oscillators = getOscillators(); const oscillators = getOscillators();
const channelCount = channels.length; const channelCount = channels.length;
@@ -681,36 +688,15 @@ function stopOscillators(streamEndSeconds) {
oscillator?.stop(streamEndSeconds); oscillator?.stop(streamEndSeconds);
} }
} }
function sendPacket(bits, packetStartSeconds) { function disconnectOscillators() {
console.log('packet start', packetStartSeconds); stopOscillators(getAudioContext().currentTime);
const channels = getChannels();
const oscillators = getOscillators(); const oscillators = getOscillators();
const channelCount = channels.length; oscillators.forEach(
let bitCount = bits.length; oscillator => oscillator.disconnect()
const lastChannel = bits.length % channelCount; )
if(lastChannel !== channelCount - 1) { oscillators.length = 0;
// make sure all channels are set for the last segment sendButton.innerText = 'Send';
bitCount += (channelCount - lastChannel) stopTimeoutId = undefined;
}
const segmentDurationSeconds = getSegmentTransferDurationSeconds();
// change our channel frequencies for the bit
for(let i = 0; i < bitCount; i++) {
// missing bits past end of bit stream set to zero
const isHigh = bits[i] ?? 0;
const channel = i % channelCount;
// already at correct frequency
if(oscillators[channel].on === isHigh) continue;
oscillators[channel].on = isHigh;
const segmentIndex = Math.floor(i / channelCount);
var offsetSeconds = segmentIndex * segmentDurationSeconds;
oscillators[channel].frequency.setValueAtTime(
channels[channel][isHigh ? 1 : 0],
packetStartSeconds + offsetSeconds
);
}
} }
function stopGraph() { function stopGraph() {
PAUSE = true; PAUSE = true;
@@ -1227,7 +1213,7 @@ function drawFrequencyLineGraph(ctx, channel, highLowIndex, color, lineWidth, da
const {pairs, time} = frequencyOverTime[i]; const {pairs, time} = frequencyOverTime[i];
const x = getTimeX(time, newest); const x = getTimeX(time, newest);
if(x === -1) continue; if(x === -1) continue;
if(channel > pairs.length) continue; if(channel >= pairs.length) continue;
const amplitude = pairs[channel][highLowIndex]; const amplitude = pairs[channel][highLowIndex];
const y = getPercentY(amplitude / MAX_AMPLITUDE); const y = getPercentY(amplitude / MAX_AMPLITUDE);
if(i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); if(i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
@@ -1490,6 +1476,7 @@ function drawSelectedChannel(ctx, channelCount, width, height) {
} }
function drawChannelNumbers(ctx, channelCount, width, height) { function drawChannelNumbers(ctx, channelCount, width, height) {
const offset = 0; const offset = 0;
const channels = getChannels();
const channelHeight = height / channelCount; const channelHeight = height / channelCount;
const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH;
let fontHeight = Math.min(24, channelHeight, segmentWidth); let fontHeight = Math.min(24, channelHeight, segmentWidth);
@@ -1502,8 +1489,9 @@ function drawChannelNumbers(ctx, channelCount, width, height) {
let top = channelHeight * channelIndex; let top = channelHeight * channelIndex;
let text = realChannel(channelIndex).toString(); let text = realChannel(channelIndex).toString();
const textTop = top + (channelHeight / 2); const textTop = top + (channelHeight / 2);
const hue = channelHue(channelIndex, channelCount); // const hue = channelHue(channelIndex, channelCount);
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`; const highHue = hzHue(channels[channelIndex][1]);
ctx.fillStyle = `hsl(${highHue}, 100%, 50%)`;
ctx.fillText(text, offset + 5, textTop); ctx.fillText(text, offset + 5, textTop);
} }
} }
@@ -1543,18 +1531,24 @@ function drawFrequencyData(forcedDraw) {
const highLuminance = isSelectedOrOver ? 25 : 50; const highLuminance = isSelectedOrOver ? 25 : 50;
const lowLuminance = isSelectedOrOver ? 12 : 25; const lowLuminance = isSelectedOrOver ? 12 : 25;
frequencies.forEach((v, channel) => { frequencies.forEach((v, channel) => {
const hue = channelHue(channel, frequencies.length); // const hue = channelHue(channel, frequencies.length);
drawFrequencyLineGraph(ctx, channel, high, `hsl(${hue}, 100%, ${highLuminance}%)`, 2, false); const lowHue = hzHue(v[0]);
drawFrequencyLineGraph(ctx, channel, low, `hsl(${hue}, 100%, ${lowLuminance}%)`, 1, true); const highHue = hzHue(v[1]);
drawFrequencyLineGraph(ctx, channel, high, `hsl(${highHue}, 100%, ${highLuminance}%)`, 2, false);
drawFrequencyLineGraph(ctx, channel, low, `hsl(${lowHue}, 100%, ${lowLuminance}%)`, 1, true);
}); });
if(CHANNEL_OVER !== -1) { if(CHANNEL_OVER !== -1) {
const hue = channelHue(CHANNEL_OVER, frequencies.length); // const hue = channelHue(CHANNEL_OVER, frequencies.length);
drawFrequencyLineGraph(ctx, CHANNEL_OVER, high, `hsl(${hue}, 100%, 50%)`, 2, false); const lowHue = hzHue(frequencies[CHANNEL_OVER][0]);
drawFrequencyLineGraph(ctx, CHANNEL_OVER, low, `hsl(${hue}, 100%, 25%)`, 1, true); const highHue = hzHue(frequencies[CHANNEL_OVER][1]);
drawFrequencyLineGraph(ctx, CHANNEL_OVER, high, `hsl(${highHue}, 100%, 50%)`, 2, false);
drawFrequencyLineGraph(ctx, CHANNEL_OVER, low, `hsl(${lowHue}, 100%, 25%)`, 1, true);
} else if(CHANNEL_SELECTED !== -1) { } else if(CHANNEL_SELECTED !== -1) {
const hue = channelHue(CHANNEL_SELECTED, frequencies.length); const lowHue = hzHue(frequencies[CHANNEL_SELECTED][0]);
drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, high, `hsl(${hue}, 100%, 50%)`, 2, false); const highHue = hzHue(frequencies[CHANNEL_SELECTED][1]);
drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, low, `hsl(${hue}, 100%, 25%)`, 1, true); // const hue = channelHue(CHANNEL_SELECTED, frequencies.length);
drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, high, `hsl(${highHue}, 100%, 50%)`, 2, false);
drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, low, `hsl(${lowHue}, 100%, 25%)`, 1, true);
} }
drawSegmentIndexes(ctx, width, height); drawSegmentIndexes(ctx, width, height);
@@ -1565,6 +1559,9 @@ function drawFrequencyData(forcedDraw) {
function channelHue(channelId, channelCount) { function channelHue(channelId, channelCount) {
return Math.floor((channelId / channelCount) * 360); return Math.floor((channelId / channelCount) * 360);
} }
function hzHue(hz) {
return Math.floor((hz / 20000) * 360);
}
function drawReceivedData() { function drawReceivedData() {
const ctx = receivedGraph.getContext('2d'); const ctx = receivedGraph.getContext('2d');