Transfer an image

This commit is contained in:
Lewis Moten
2024-05-12 23:41:37 -04:00
parent 838761ae82
commit e8634b98d4
7 changed files with 143 additions and 66 deletions

View File

@@ -27,7 +27,16 @@ class BasePanel {
addCheckboxes = (name, items) => { addCheckboxes = (name, items) => {
this.addCheckedInputs('checkbox', name, items); this.addCheckedInputs('checkbox', name, items);
}; };
openField = name => this.addText(`${name}: `); openField = (name, id) => {
if(id) {
if(this._field) throw `Unable to add field ${id} when prior field was not closed.`;
const field = document.createElement('span');
field.id = this.childId(id);
this.append(field);
this._field = field;
}
this.addText(`${name}: `)
};
addText = text => this.append(document.createTextNode(text)); addText = text => this.append(document.createTextNode(text));
addDynamicText = (id, text) => { addDynamicText = (id, text) => {
const span = document.createElement('span'); const span = document.createElement('span');
@@ -35,7 +44,10 @@ class BasePanel {
span.innerText = text; span.innerText = text;
return this.append(span); return this.append(span);
} }
closeField = () => this.addNewLine(); closeField = () => {
this._field = undefined;
this.addNewLine()
};
addNewLine = () => this.append(document.createElement('br')); addNewLine = () => this.append(document.createElement('br'));
addDropdown = (id, items, eventName = 'change') => { addDropdown = (id, items, eventName = 'change') => {
const select = document.createElement('select'); const select = document.createElement('select');
@@ -169,7 +181,6 @@ class BasePanel {
getElement = id => { getElement = id => {
const element = this.getDomElement().querySelector(`#${this.childId(id)}`); const element = this.getDomElement().querySelector(`#${this.childId(id)}`);
// const element = document.getElementById(this.childId(id));
if(!element) throw new Error(`Unable to find ${id}`); if(!element) throw new Error(`Unable to find ${id}`);
return element; return element;
} }
@@ -180,6 +191,15 @@ class BasePanel {
code.className = type === '' ? 'raw-data' : `raw-data-${type}`; code.className = type === '' ? 'raw-data' : `raw-data-${type}`;
this.append(code); this.append(code);
} }
addImage = (id, src, options) => {
const image = document.createElement('img');
image.id = this.childId(id);
image.src = src;
Object.keys(options).forEach(key => {
image[key] = options[key];
})
this.append(image);
}
setValueById = (id, value) => { setValueById = (id, value) => {
const element = this.getElement(id); const element = this.getElement(id);
switch(element.tagName) { switch(element.tagName) {
@@ -187,6 +207,10 @@ class BasePanel {
case 'SELECT': case 'SELECT':
element.value = value; element.value = value;
break; break;
case 'IMG':
element.src = value;
element.onload = () => URL.revokeObjectURL(value);
break;
default: default:
element.innerText = value; element.innerText = value;
} }
@@ -209,11 +233,21 @@ class BasePanel {
return element.innerText; return element.innerText;
} }
} }
display = (id, isDisplayed) => {
const element = this.getElement(id);
element.style.display = isDisplayed ? 'block' : 'none';
}
setCheckedById = (id, checked = true) => { setCheckedById = (id, checked = true) => {
this.getElement(id).checked = !!checked; this.getElement(id).checked = !!checked;
} }
getCheckedById = (id) => !!(this.getElement(id).checked); getCheckedById = (id) => !!(this.getElement(id).checked);
append = (element) => this.container.appendChild(element); append = (element) => {
if(this._field) {
return this._field.appendChild(element);
} else {
return this.container.appendChild(element);
}
}
clear = () => this.container.innerHTML = ''; clear = () => this.container.innerHTML = '';
addEventListener = (eventName, callback) => this.dispatcher.addListener(eventName, callback); addEventListener = (eventName, callback) => this.dispatcher.addListener(eventName, callback);
removeEventListener = (eventName, callback) => this.dispatcher.removeListener(eventName, callback); removeEventListener = (eventName, callback) => this.dispatcher.removeListener(eventName, callback);

View File

@@ -1,20 +1,82 @@
import { bytesToText, textToBytes, urlToBytes, bytesToUrl } from '../converters';
import { byteSize } from '../Humanize';
import BasePanel from './BasePanel'; import BasePanel from './BasePanel';
class MessagePanel extends BasePanel { class MessagePanel extends BasePanel {
constructor() { constructor() {
super('Message'); super('Message');
this.addInputText('text-to-send', '', 'messageChange');
this.openField('Data Type');
this.addDropdown('data-type', [
{text: 'Text', value: 'text', selected: true},
{text: 'Image', value: 'image'}
], 'dataTypeChange');
this.closeField();
this.openField('Text', 'field-text');
this.addInputText('text-to-send', '', {eventName: 'messageChange'});
this.closeField();
this.openField('Image', 'field-image');
this.addImage('image-to-send', 'interlaced-sample.gif', {eventName: 'messageChange'});
this.closeField();
this.addButton('send-button', 'Send', 'send'); this.addButton('send-button', 'Send', 'send');
this.addNewLine();
this.openField('Bytes');
this.addDynamicText('bytes', 0);
this.closeField();
this.addSection('Received'); this.addSection('Received');
this.addProgressBar('received-progress', .50);
this.addCode('decoded-text', '', 'small'); this.addCode('decoded-text', '', 'small');
this.addImage('decoded-image', undefined, {width: 32, height: 32});
this.addProgressBar('received-progress', .50);
this.addEventListener('dataTypeChange', ({values: [value]}) => {
this.display('field-text', value === 'text');
this.display('field-image', value === 'image');
this.display('decoded-image', value === 'image');
this.display('decoded-text', value==='text');
// should be 487 bytes
this.setValueById('bytes', byteSize(this.getMessageBytes().length));
});
this.addEventListener('messageChange', e => {
this.setValueById('bytes', byteSize(this.getMessageBytes().length));
});
this.dispatcher.emit('dataTypeChange', {values: [this.getDataType()]});
} }
getSendButtonText = () => this.getValueById('send-button'); getSendButtonText = () => this.getValueById('send-button');
setSendButtonText = text => this.setValueById('send-button', text); setSendButtonText = text => this.setValueById('send-button', text);
setMessage = text => this.setValueById('text-to-send', text); setMessageText = text => {
getMessage = () => this.getValueById('text-to-send'); this.setValueById('text-to-send', text);
this.setValueById('bytes', byteSize(textToBytes(text).length));
}
getMessageText = () => this.getValueById('text-to-send');
getMessageBytes = () => {
if(this.getDataType() === 'text') {
return textToBytes(this.getMessageText());
} else {
return urlToBytes(this.getElement('image-to-send').src);
}
}
setProgress = percent => this.setProgressById('received-progress', percent); setProgress = percent => this.setProgressById('received-progress', percent);
setReceived = (html) => this.setHtmlById('decoded-text', html); setReceived = (html) => this.setHtmlById('decoded-text', html);
setReceivedBytes = bytes => {
if(this.getDataType() === 'text') {
this.setReceived(bytesToText(bytes));
} else {
this.setValueById('decoded-image', bytesToUrl(bytes));
}
}
getDataType = () => this.getValueById('data-type');
setDataType = (value) => {
this.setValueById('data-type', value);
this.dispatcher.emit('dataTypeChange', {values: [value]});
}
} }
export default MessagePanel; export default MessagePanel;

View File

@@ -20,18 +20,13 @@ class SpeedPanel extends BasePanel {
this.addDynamicText('transfer-duration', 'n/a'); this.addDynamicText('transfer-duration', 'n/a');
this.closeField(); this.closeField();
this.addSection('Maximum'); this.addSection('Maximum Data');
this.openField('Packets');
this.addDynamicText('max-packets', 'n/a');
this.closeField();
this.openField('Duration'); this.openField('Duration');
this.addDynamicText('max-duration', 'n/a'); this.addDynamicText('max-duration', 'n/a');
this.closeField(); this.closeField();
}; };
setMaximumPackets = (count) => this.setValueById('max-packets', count.toLocaleString());
setMaximumDurationMilliseconds = (milliseconds) => this.setValueById('max-duration', Humanize.durationMilliseconds(milliseconds)); setMaximumDurationMilliseconds = (milliseconds) => this.setValueById('max-duration', Humanize.durationMilliseconds(milliseconds));
setPacketizationBitsPerSecond = (bps) => this.setValueById('bps-packetization', Humanize.bitsPerSecond(bps)); setPacketizationBitsPerSecond = (bps) => this.setValueById('bps-packetization', Humanize.bitsPerSecond(bps));
setDataBitsPerSecond = (bps) => this.setValueById('bps-data', Humanize.bitsPerSecond(bps)); setDataBitsPerSecond = (bps) => this.setValueById('bps-data', Humanize.bitsPerSecond(bps));

View File

@@ -61,3 +61,20 @@ export const bitsToInt = (bits, bitLength) => {
2 2
); );
} }
export const urlToBytes = src => {
const xhr = new XMLHttpRequest();
// we need a synchronous response.
xhr.open('GET', src, false);
xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.send(null);
if(xhr.status !== 200) return [];
let bytes = [];
for(let i = 0; i < xhr.response.length; i++) {
bytes.push(xhr.response.charCodeAt(i) & 0xFF);
}
return bytes;
}
export const bytesToUrl = bytes => {
const blob = new Blob([new Uint8Array(bytes)]);
return URL.createObjectURL(blob);
}

View File

@@ -16,26 +16,6 @@
<canvas id="received-channel-graph" width="800" height="300"></canvas> <canvas id="received-channel-graph" width="800" height="300"></canvas>
</div> </div>
</div> </div>
<div>
<h2>Packet Configuration</h2>
<div>
Bytes per packet: <span id="bytes-per-packet"></span><br>
Bits per Packet: <span id="bits-per-packet"></span><br>
<h4>Encoding</h4>
Packet Encoding: <span id="packet-encoding"></span><br>
Bits per block: <span id="packet-encoding-bits-per-block"></span><br>
Blocks per packet: <span id="packet-encoding-block-count"></span><br>
Encoding bits per Packet: <span id="packet-encoding-bit-count"></span><br>
<h4>Utilization</h4>
Data Bits per Packet: <span id="packet-data-bit-count"></span><br>
Unused bits per packet: <span id="packet-unused-bit-count"></span><br>
<h4>Segments</h4>
Segments Per Packet: <span id="segments-per-packet"></span><br>
Bits per segment: <span id="bits-per-segment">N/A</span><br />
Last segment unused bits: <span id="last-segment-unused-bit-count"></span><br>
</div>
</div>
<div> <div>
<h2>Data</h2> <h2>Data</h2>
<div> <div>

View File

@@ -74,10 +74,10 @@ function handleWindowLoad() {
panelContainer.prepend(speedPanel.getDomElement()); panelContainer.prepend(speedPanel.getDomElement());
panelContainer.prepend(graphConfigurationPanel.getDomElement()); panelContainer.prepend(graphConfigurationPanel.getDomElement());
panelContainer.prepend(frequencyGraphPanel.getDomElement()); panelContainer.prepend(frequencyGraphPanel.getDomElement());
panelContainer.prepend(availableFskPairsPanel.getDomElement());
panelContainer.prepend(packetizationPanel.getDomElement()); panelContainer.prepend(packetizationPanel.getDomElement());
panelContainer.prepend(signalPanel.getDomElement()); panelContainer.prepend(availableFskPairsPanel.getDomElement());
panelContainer.prepend(frequencyPanel.getDomElement()); panelContainer.prepend(frequencyPanel.getDomElement());
panelContainer.prepend(signalPanel.getDomElement());
panelContainer.prepend(bitsReceivedPanel.getDomElement()); panelContainer.prepend(bitsReceivedPanel.getDomElement());
panelContainer.prepend(bitsSentPanel.getDomElement()); panelContainer.prepend(bitsSentPanel.getDomElement());
panelContainer.prepend(messagePanel.getDomElement()); panelContainer.prepend(messagePanel.getDomElement());
@@ -88,19 +88,20 @@ function handleWindowLoad() {
communicationsPanel.setSendSpeakers(false); communicationsPanel.setSendSpeakers(false);
communicationsPanel.setSendAnalyzer(true); communicationsPanel.setSendAnalyzer(true);
messagePanel.setMessage(Randomizer.text(5)); messagePanel.setMessageText(Randomizer.text(5));
messagePanel.setProgress(0); messagePanel.setProgress(0);
messagePanel.setReceived(''); messagePanel.setReceived('');
messagePanel.setDataType('image');
messagePanel.setSendButtonText('Send'); messagePanel.setSendButtonText('Send');
bitsSentPanel.setCode(''); bitsSentPanel.setCode('');
bitsReceivedPanel.setCode(''); bitsReceivedPanel.setCode('');
frequencyPanel.setMinimumFrequency(9000); frequencyPanel.setMinimumFrequency(2500);
frequencyPanel.setMaximumFrequency(15000); frequencyPanel.setMaximumFrequency(23000);
frequencyPanel.setFftSize(2 ** 9); frequencyPanel.setFftSize(2 ** 9);
frequencyPanel.setFskPadding(1); frequencyPanel.setFskPadding(3);
frequencyPanel.setMultiFskPadding(1); frequencyPanel.setMultiFskPadding(4);
signalPanel.setWaveform('triangle'); signalPanel.setWaveform('triangle');
signalPanel.setSegmentDurationMilliseconds(30); signalPanel.setSegmentDurationMilliseconds(30);
@@ -125,7 +126,6 @@ function handleWindowLoad() {
frequencyGraphPanel.setAmplitudeThreshold(signalPanel.getAmplitudeThreshold()); frequencyGraphPanel.setAmplitudeThreshold(signalPanel.getAmplitudeThreshold());
frequencyGraphPanel.setDurationMilliseconds(graphConfigurationPanel.getDurationMilliseconds()); frequencyGraphPanel.setDurationMilliseconds(graphConfigurationPanel.getDurationMilliseconds());
speedPanel.setMaximumPackets(0);
speedPanel.setMaximumDurationMilliseconds(0); speedPanel.setMaximumDurationMilliseconds(0);
speedPanel.setDataBitsPerSecond(0); speedPanel.setDataBitsPerSecond(0);
speedPanel.setPacketizationBitsPerSecond(0); speedPanel.setPacketizationBitsPerSecond(0);
@@ -319,43 +319,29 @@ function updatePacketUtils() {
packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE, packetEncodingBitCount: ERROR_CORRECTION_BLOCK_SIZE,
packetDecodingBitCount: ERROR_CORRECTION_DATA_SIZE, packetDecodingBitCount: ERROR_CORRECTION_DATA_SIZE,
}); });
speedPanel.setMaximumPackets(PacketUtils.getMaxPackets());
speedPanel.setMaximumDurationMilliseconds(PacketUtils.getMaxDurationMilliseconds()); speedPanel.setMaximumDurationMilliseconds(PacketUtils.getMaxDurationMilliseconds());
speedPanel.setDataBitsPerSecond(PacketUtils.getEffectiveBaud()); speedPanel.setDataBitsPerSecond(PacketUtils.getEffectiveBaud());
speedPanel.setPacketizationBitsPerSecond(PacketUtils.getBaud()); speedPanel.setPacketizationBitsPerSecond(PacketUtils.getBaud());
speedPanel.setTransferDurationMilliseconds(PacketUtils.getDataTransferDurationMillisecondsFromByteCount( speedPanel.setTransferDurationMilliseconds(PacketUtils.getDataTransferDurationMillisecondsFromByteCount(
textToBytes(messagePanel.getMessage()).length messagePanel.getMessageBytes().length
)); ));
} }
function updatePacketStats() { function updatePacketStats() {
const text = messagePanel.getMessage(); const bytes = messagePanel.getMessageBytes();
const bits = textToBits(text); const bits = bytesToBits(bytes);
const byteCount = text.length; const byteCount = bytes.length;
const bitCount = PacketUtils.getPacketizationBitCountFromBitCount(bits.length);; const bitCount = PacketUtils.getPacketizationBitCountFromBitCount(bits.length);;
// Data // Data
document.getElementById('original-byte-count').innerText = textToBytes(text).length.toLocaleString(); document.getElementById('original-byte-count').innerText = byteCount.toLocaleString();
document.getElementById('packetization-byte-count').innerText = PacketUtils.getPacketizationByteCountFromBitCount(bits.length).toLocaleString(); document.getElementById('packetization-byte-count').innerText = PacketUtils.getPacketizationByteCountFromBitCount(bits.length).toLocaleString();
document.getElementById('packetization-bit-count').innerText = bitCount.toLocaleString(); document.getElementById('packetization-bit-count').innerText = bitCount.toLocaleString();
document.getElementById('packet-count').innerText = PacketUtils.getPacketCount(bitCount).toLocaleString(); document.getElementById('packet-count').innerText = PacketUtils.getPacketCount(bitCount).toLocaleString();
// # Packet Config
document.getElementById('bits-per-packet').innerText = PacketUtils.getPacketMaxBitCount().toLocaleString();
document.getElementById('bytes-per-packet').innerText = Humanize.byteSize(PacketUtils.getPacketMaxByteCount());
// ## Packet Encoding // ## Packet Encoding
document.getElementById('packet-encoding').innerText = PacketUtils.isPacketEncoded() ? 'Yes' : 'No';
document.getElementById('packet-encoding-block-count').innerText = PacketUtils.getPacketEncodingBlockCount().toLocaleString();
document.getElementById('packet-encoding-bits-per-block').innerText = PacketUtils.packetEncodingBlockSize().toLocaleString();
document.getElementById('packet-encoding-bit-count').innerText = PacketUtils.getEncodedPacketDataBitCount().toLocaleString();
document.getElementById('bits-per-segment').innerText = PacketUtils.getBitsPerSegment();
// Data // Data
document.getElementById('packet-data-bit-count').innerText = PacketUtils.getPacketDataBitCount().toLocaleString();
document.getElementById('packet-unused-bit-count').innerText = PacketUtils.getPacketUnusedBitCount().toLocaleString();
document.getElementById('last-packet-unused-bit-count').innerText = PacketUtils.fromByteCountGetPacketLastUnusedBitCount(byteCount).toLocaleString(); document.getElementById('last-packet-unused-bit-count').innerText = PacketUtils.fromByteCountGetPacketLastUnusedBitCount(byteCount).toLocaleString();
document.getElementById('last-segment-unused-bit-count').innerText = PacketUtils.getPacketLastSegmentUnusedBitCount().toLocaleString()
document.getElementById('segments-per-packet').innerText = PacketUtils.getPacketSegmentCount().toLocaleString();
frequencyGraphPanel.setSamplePeriodsPerGroup(PacketUtils.getPacketSegmentCount()); frequencyGraphPanel.setSamplePeriodsPerGroup(PacketUtils.getPacketSegmentCount());
document.getElementById('total-segments').innerText = getTotalSegmentCount(bitCount).toLocaleString(); document.getElementById('total-segments').innerText = getTotalSegmentCount(bitCount).toLocaleString();
} }
@@ -673,9 +659,13 @@ function handleStreamManagerChange() {
const bytes = StreamManager.getDataBytes(); const bytes = StreamManager.getDataBytes();
const receivedText = bytesToText(bytes); const receivedText = bytesToText(bytes);
messagePanel.setReceived( if(messagePanel.getDataType() === 'text') {
receivedText.split('').reduce(textExpectorReducer(SENT_ORIGINAL_TEXT), '') messagePanel.setReceived(
); receivedText.split('').reduce(textExpectorReducer(SENT_ORIGINAL_TEXT), '')
);
} else {
messagePanel.setReceivedBytes(bytes);
}
} }
function parseTotalBitsTransferring() { function parseTotalBitsTransferring() {
const dataByteCount = StreamManager.getTransferByteCount(); const dataByteCount = StreamManager.getTransferByteCount();
@@ -850,8 +840,7 @@ function handleSendButtonClick() {
if(messagePanel.getSendButtonText() === 'Stop') { if(messagePanel.getSendButtonText() === 'Stop') {
AudioSender.stop(); AudioSender.stop();
} else { } else {
const text = messagePanel.getMessage(); sendBytes(messagePanel.getMessageBytes());
sendBytes(textToBytes(text));
} }
} }
function getAnalyser() { function getAnalyser() {

BIN
interlaced-sample.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B