Add new frequency graph
This commit is contained in:
@@ -8,6 +8,7 @@ let AMPLITUDE_THRESHOLD = 50;
|
||||
let FSK_SETS = [];
|
||||
let SIGNAL_INTERVAL_MS = 30;
|
||||
let SIGNAL_TIMEOUT_MS = 400;
|
||||
let LAST_SIGNAL_BEFORE_TIMEOUT = 0;
|
||||
|
||||
let HAS_SIGNAL = false;
|
||||
let SIGNAL_START_MS = -1;
|
||||
@@ -17,6 +18,14 @@ let SAMPLE_RATE;
|
||||
let signalTimeoutId;
|
||||
let SAMPLES = [];
|
||||
|
||||
const setTimeoutMilliseconds = (milliseconds) => {
|
||||
SIGNAL_TIMEOUT_MS = milliseconds;
|
||||
if(signalTimeoutId) {
|
||||
// probably a long timeout. let's reset
|
||||
window.clearTimeout(signalTimeoutId);
|
||||
signalTimeoutId = window.setTimeout(handleSignalLost, SIGNAL_TIMEOUT_MS, LAST_SIGNAL_BEFORE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
const changeConfiguration = ({
|
||||
fskSets,
|
||||
signalIntervalMs,
|
||||
@@ -196,6 +205,7 @@ const handleSignalOn = time => {
|
||||
}
|
||||
const handleSignalOff = time => {
|
||||
if(HAS_SIGNAL && !signalTimeoutId) {
|
||||
LAST_SIGNAL_BEFORE_TIMEOUT = time;
|
||||
signalTimeoutId = window.setTimeout(handleSignalLost, SIGNAL_TIMEOUT_MS, time);
|
||||
}
|
||||
}
|
||||
@@ -222,4 +232,5 @@ export {
|
||||
reset,
|
||||
addEventListener,
|
||||
removeEventListener,
|
||||
setTimeoutMilliseconds
|
||||
}
|
||||
|
||||
@@ -116,14 +116,16 @@ export function stopAt(streamEndSeconds) {
|
||||
);
|
||||
}
|
||||
export function stop() {
|
||||
const time = now();
|
||||
const oscillators = getOscillators();
|
||||
oscillators.forEach(
|
||||
oscillator => {
|
||||
oscillator?.stop(time);
|
||||
oscillator?.disconnect();
|
||||
}
|
||||
)
|
||||
if(this.audioContext) {
|
||||
const time = now();
|
||||
oscillators.forEach(
|
||||
oscillator => {
|
||||
oscillator?.stop(time);
|
||||
oscillator?.disconnect();
|
||||
}
|
||||
)
|
||||
}
|
||||
oscillators.length = 0;
|
||||
futureEventIds.forEach(window.clearTimeout);
|
||||
futureEventIds.length = 0;
|
||||
|
||||
@@ -5,6 +5,7 @@ class AvailableFskPairsPanel extends BasePanel {
|
||||
super('Available FSK Pairs');
|
||||
this.exclude = [];
|
||||
this.fskPairs = [];
|
||||
this.originalSelectedFskPairs = [];
|
||||
this.sampleRate = 48000;
|
||||
|
||||
this.addCanvas('fsk-spectrum', 200, 32);
|
||||
@@ -28,9 +29,25 @@ class AvailableFskPairsPanel extends BasePanel {
|
||||
} else if(!this.exclude.includes(event.id)) {
|
||||
this.exclude.push(event.id);
|
||||
}
|
||||
this.checkChanges();
|
||||
this.drawFskSpectrum();
|
||||
};
|
||||
|
||||
checkChanges = () => {
|
||||
const selected = this.getSelectedFskPairs();
|
||||
const original = this.originalSelectedFskPairs;
|
||||
let changed = false;
|
||||
if(original.length !== selected.length) {
|
||||
changed = true;
|
||||
} else {
|
||||
const hertz = selected.flat();
|
||||
changed = original.flat().some((hz, i) => hz !== hertz[i]);
|
||||
}
|
||||
if(changed) {
|
||||
this.originalSelectedFskPairs = selected;
|
||||
this.dispatcher.emit('change', {selected});
|
||||
}
|
||||
}
|
||||
getSelectedFskPairs = () => this.fskPairs
|
||||
.filter(this.isSelected);
|
||||
|
||||
@@ -38,6 +55,7 @@ class AvailableFskPairsPanel extends BasePanel {
|
||||
setSelectedIndexes = (values) => {
|
||||
this.exclude = values;
|
||||
this.setFskPairs(this.fskPairs);
|
||||
this.checkChanges();
|
||||
}
|
||||
|
||||
setFskPairs = fskPairs => {
|
||||
@@ -51,6 +69,7 @@ class AvailableFskPairsPanel extends BasePanel {
|
||||
eventName: 'select'
|
||||
}));
|
||||
this.replaceCheckedInputs('checkbox', 'fsk-pairs', items);
|
||||
this.checkChanges();
|
||||
this.drawFskSpectrum();
|
||||
}
|
||||
|
||||
|
||||
@@ -195,6 +195,10 @@ class BasePanel {
|
||||
const element = this.getElement(id);
|
||||
element.innerHTML = html;
|
||||
}
|
||||
getNumberById = id => {
|
||||
const value = this.getValueById(id);
|
||||
return parseFloat(value);
|
||||
}
|
||||
getValueById = (id) => {
|
||||
const element = this.getElement(id);
|
||||
switch(element.tagName) {
|
||||
|
||||
249
Panels/FrequencyGraphPanel.js
Normal file
249
Panels/FrequencyGraphPanel.js
Normal file
@@ -0,0 +1,249 @@
|
||||
import BasePanel from './BasePanel';
|
||||
|
||||
class FrequencyGraphPanel extends BasePanel {
|
||||
constructor() {
|
||||
super('Frequency Graph');
|
||||
this.fskPairs = [];
|
||||
this.sampleRate = 48000;
|
||||
this.samplingPeriod = 30;
|
||||
this.signalStart = performance.now();
|
||||
this.signalEnd = this.signalStart;
|
||||
this.samples = [];
|
||||
this.duration = 200;
|
||||
this.amplitudeThreshold = 0;
|
||||
this.addButton('toggle', 'Start', 'toggle');
|
||||
this.addNewLine();
|
||||
this.addCanvas('frequency-graph', 500, 150);
|
||||
this.addEventListener('toggle', this.handleToggle);
|
||||
};
|
||||
setDurationMilliseconds = (millseconds) => {
|
||||
this.duration = millseconds;
|
||||
this.draw(false);
|
||||
}
|
||||
setSignalStart = milliseconds => {
|
||||
this.signalStart = milliseconds;
|
||||
}
|
||||
setSignalEnd = milliseconds => {
|
||||
this.signalEnd = milliseconds;
|
||||
}
|
||||
setSamplingPeriod = (milliseconds) => this.samplingPeriod = milliseconds;
|
||||
|
||||
setAmplitudeThreshold = value => {
|
||||
this.amplitudeThreshold = value;
|
||||
}
|
||||
setSampleRate = (value) => {
|
||||
this.sampleRate = value;
|
||||
}
|
||||
setFskPairs = fskPairs => {
|
||||
this.fskPairs = fskPairs;
|
||||
}
|
||||
setAnalyser = (analyser) => {
|
||||
this.analyser = analyser;
|
||||
}
|
||||
isRunning = () => !!this.intervalId || !!this.animationFrameId;
|
||||
|
||||
handleToggle = () => {
|
||||
if(this.isRunning()) {
|
||||
this.stop();
|
||||
} else {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
start = () => {
|
||||
this.setValueById('toggle', 'Stop');
|
||||
if(!this.intervalId) {
|
||||
this.intervalId = window.setInterval(this.collectSamples, 5);
|
||||
}
|
||||
if(!this.animationFrameId) {
|
||||
this.animationFrameId = window.requestAnimationFrame(this.draw);
|
||||
}
|
||||
}
|
||||
stop = () => {
|
||||
this.setValueById('toggle', 'Start');
|
||||
if(this.intervalId) {
|
||||
window.clearInterval(this.intervalId);
|
||||
this.intervalId = undefined;
|
||||
}
|
||||
if(this.animationFrameId) {
|
||||
window.cancelAnimationFrame(this.animationFrameId);
|
||||
this.animationFrameId = undefined;
|
||||
}
|
||||
// final draw
|
||||
this.draw(false);
|
||||
}
|
||||
collectSamples = () => {
|
||||
// Nothing to collect
|
||||
if(this.fskPairs.length === 0) return;
|
||||
// Nothing to collect with
|
||||
const analyser = this.analyser;
|
||||
if(!analyser) return;
|
||||
|
||||
const frequencyResolution = this.sampleRate / analyser.fftSize;
|
||||
const frequencies = new Uint8Array(analyser.frequencyBinCount);
|
||||
analyser.getByteFrequencyData(frequencies);
|
||||
|
||||
const indexOfHz = hz => Math.round(hz/frequencyResolution);
|
||||
const ampsFromHz = hz => frequencies[indexOfHz(hz)];
|
||||
const ampsFromManyHz = fsk => fsk.map(ampsFromHz);
|
||||
const now = performance.now();
|
||||
this.samples.unshift({
|
||||
time: now,
|
||||
fskPairs: this.fskPairs.map(ampsFromManyHz)
|
||||
});
|
||||
|
||||
this.samples = this.samples.filter(sample => now - sample.time < this.duration);
|
||||
}
|
||||
|
||||
draw = () => {
|
||||
const maxAmps = 280; // inflated for height
|
||||
const ultimateFrequency = this.sampleRate / 2;
|
||||
const canvas = this.getElement('frequency-graph');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const {height, width} = canvas;
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
let now;
|
||||
|
||||
if(this.samples.length > 1) {
|
||||
now = this.samples[0].time;
|
||||
this.fskPairs.forEach((fsk, fskIndex) => {
|
||||
fsk.forEach((hz, hzIndex)=> {
|
||||
ctx.beginPath();
|
||||
const hue = Math.floor(hz/ultimateFrequency * 360);
|
||||
if(hzIndex === 0) {
|
||||
ctx.strokeStyle = `hsla(${hue}, 100%, 50%, 50%)`;
|
||||
ctx.setLineDash([5, 5]);
|
||||
} else {
|
||||
ctx.strokeStyle = `hsl(${hue}, 100%, 50%)`;
|
||||
ctx.setLineDash([]);
|
||||
}
|
||||
this.samples.forEach(({time, fskPairs}, i) => {
|
||||
fsk = fskPairs[fskIndex];
|
||||
if(!fsk) return; // configuration changed
|
||||
let x = ((now - time) / this.duration) * width;
|
||||
const percent = (fsk[hzIndex] / maxAmps);
|
||||
let y = (1 - percent) * height;
|
||||
if(i === 0) {
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
});
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
})
|
||||
})
|
||||
};
|
||||
ctx.setLineDash([]);
|
||||
|
||||
// Amplitude Threshold
|
||||
ctx.strokeStyle = 'hsla(0, 0%, 100%, 20%)';
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
let y = height * (1-(this.amplitudeThreshold * 255) / maxAmps);
|
||||
ctx.moveTo(0, y);
|
||||
ctx.lineTo(width, y);
|
||||
ctx.stroke();
|
||||
|
||||
// sampling periods
|
||||
if(!now) now = performance.now();
|
||||
let lastPeriodStart = now - ((now - this.signalStart) % this.samplingPeriod);
|
||||
let lastTextX = -1000;
|
||||
ctx.lineWidth = 1;
|
||||
this.lastCountX = -1000;
|
||||
for(let time = lastPeriodStart; time > now - this.duration; time -= this.samplingPeriod) {
|
||||
const end = time + this.samplingPeriod;
|
||||
let rightX = ((now - time) / this.duration) * width;
|
||||
let leftX = ((now - end) / this.duration) * width;
|
||||
// Line for when period started
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(rightX, 0);
|
||||
ctx.lineTo(rightX, height);
|
||||
ctx.strokeStyle = 'hsla(120, 100%, 100%, 50%)';
|
||||
ctx.stroke();
|
||||
|
||||
let samplePeriodWidth = rightX - leftX;
|
||||
ctx.fontSize = '24px';
|
||||
|
||||
// Sample Index
|
||||
if(time >= this.signalStart && (this.signalEnd < this.signalStart || time < this.signalEnd)) {
|
||||
|
||||
const signalIndex = Math.floor((time - this.signalStart) / this.samplingPeriod);
|
||||
let text = signalIndex.toLocaleString();
|
||||
let size = ctx.measureText(text);
|
||||
let textX = leftX + (samplePeriodWidth / 2) - (size.width / 2);
|
||||
// far enough from prior text?
|
||||
if(textX - lastTextX > size.width) {
|
||||
lastTextX = textX;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.textBaseline = 'bottom';
|
||||
let textY = height - 12;
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.strokeText(text, textX, textY);
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText(text, textX, textY);
|
||||
}
|
||||
}
|
||||
|
||||
// sample counts
|
||||
this.drawSampleCount(ctx, width, height, time, end, leftX, samplePeriodWidth);
|
||||
}
|
||||
|
||||
this.drawSignalStart(ctx, width, height, now);
|
||||
this.drawSignalEnd(ctx, width, height, now);
|
||||
|
||||
if(this.isRunning()) {
|
||||
this.animationFrameId = requestAnimationFrame(this.draw);
|
||||
}
|
||||
}
|
||||
drawSampleCount = (ctx, width, height, start, end, leftX, samplePeriodWidth) => {
|
||||
const count = this.samples.filter(sample => {
|
||||
return sample.time >= start && sample.time < end;
|
||||
}).length;
|
||||
|
||||
let text = count.toLocaleString();
|
||||
let size = ctx.measureText(text);
|
||||
let textX = leftX + (samplePeriodWidth / 2) - (size.width / 2);
|
||||
|
||||
// far enough from prior text?
|
||||
if(textX - this.lastCountX > size.width) {
|
||||
this.lastCountX = textX;
|
||||
ctx.lineWidth = 2;
|
||||
ctx.textBaseline = 'bottom';
|
||||
let textY = 20;
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.strokeText(text, textX, textY);
|
||||
if(count === 0) {
|
||||
ctx.fillStyle = 'red';
|
||||
} else if(count < 3) {
|
||||
ctx.fillStyle = 'yellow';
|
||||
} else {
|
||||
ctx.fillStyle = 'white';
|
||||
}
|
||||
ctx.fillText(text, textX, textY);
|
||||
}
|
||||
}
|
||||
drawSignalStart = (ctx, width, height, now) => {
|
||||
if(now - this.signalStart < this.duration) {
|
||||
let x = ((now - this.signalStart) / this.duration) * width;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, height);
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeStyle = 'hsla(60, 100%, 50%, 50%)';
|
||||
ctx.stroke();
|
||||
}
|
||||
};
|
||||
drawSignalEnd = (ctx, width, height, now) => {
|
||||
if(now - this.signalEnd < this.duration) {
|
||||
let x = ((now - this.signalEnd) / this.duration) * width;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, height);
|
||||
ctx.lineWidth = 3;
|
||||
ctx.strokeStyle = 'hsla(60, 100%, 50%, 50%)';
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FrequencyGraphPanel;
|
||||
26
Panels/GraphConfigurationPanel.js
Normal file
26
Panels/GraphConfigurationPanel.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import BasePanel from './BasePanel';
|
||||
|
||||
class GraphConfigurationPanel extends BasePanel {
|
||||
constructor() {
|
||||
super('Graphs');
|
||||
this.addCheckboxes('checkboxes', [
|
||||
{text: 'Pause after signal ends', id: 'pause-after-end', eventName: 'pauseAfterEndChange'}
|
||||
])
|
||||
this.openField('Duration');
|
||||
this.addInputNumber('duration', 1, {min: 0.03, step: 0.001, eventName: 'durationChange'});
|
||||
this.addText('s');
|
||||
this.closeField();
|
||||
};
|
||||
|
||||
getDurationSeconds = () => this.getNumberById('duration');
|
||||
setDurationSeconds = (seconds) => this.setValueById('duration', seconds);
|
||||
|
||||
getDurationMilliseconds = () => this.getDurationSeconds() * 1000;
|
||||
setDurationMilliseconds = (milliseconds) => this.setDurationSeconds(milliseconds / 1000);
|
||||
|
||||
getPauseAfterEnd = () => this.getCheckedById('pause-after-end');
|
||||
setPauseAfterEnd = (value) => this.setCheckedById('pause-after-end', value);
|
||||
|
||||
}
|
||||
|
||||
export default GraphConfigurationPanel;
|
||||
@@ -28,12 +28,19 @@ class SignalPanel extends BasePanel {
|
||||
this.addText('%');
|
||||
this.closeField();
|
||||
|
||||
this.openField('Timeout');
|
||||
this.addInputNumber('timeout', 30, {min: 30, max: 1000, eventName: 'timeoutChange'});
|
||||
this.addText('ms');
|
||||
this.closeField();
|
||||
|
||||
this.openField('Smoothing Time Constant');
|
||||
this.addInputNumber('smoothing-time-constant', 0, {min: 0, max: 100, eventName: 'smothingTimeConstantChange', translation: 'percent'});
|
||||
this.addText('%');
|
||||
this.closeField();
|
||||
};
|
||||
|
||||
getTimeoutMilliseconds = () => this.getNumberById('timeout');
|
||||
setTimeoutMilliseconds = (milliseconds) => this.setValueById('timeout', milliseconds);
|
||||
getWaveform = () => this.getValueById('wave-form');
|
||||
setWaveform = (value) => this.setValueById('wave-form', value);
|
||||
|
||||
|
||||
119
index.js
119
index.js
@@ -14,7 +14,8 @@ import FrequencyPanel from "./Panels/FrequencyPanel";
|
||||
import SignalPanel from "./Panels/SignalPanel";
|
||||
import PacketizationPanel from "./Panels/PacketizationPanel";
|
||||
import AvailableFskPairsPanel from "./Panels/AvailableFskPairsPanel";
|
||||
|
||||
import FrequencyGraphPanel from "./Panels/FrequencyGraphPanel";
|
||||
import GraphConfigurationPanel from './Panels/GraphConfigurationPanel'
|
||||
var audioContext;
|
||||
var microphoneStream;
|
||||
var microphoneNode;
|
||||
@@ -48,10 +49,6 @@ let SAMPLES = [];
|
||||
|
||||
var bitStart = [];
|
||||
var PAUSE = false;
|
||||
var PAUSE_AFTER_END = true;
|
||||
|
||||
let USED_FSK = [];
|
||||
let AVAILABLE_FSK = [];
|
||||
|
||||
const communicationsPanel = new CommunicationsPanel();
|
||||
const messagePanel = new MessagePanel();
|
||||
@@ -61,9 +58,13 @@ const frequencyPanel = new FrequencyPanel();
|
||||
const signalPanel = new SignalPanel();
|
||||
const packetizationPanel = new PacketizationPanel();
|
||||
const availableFskPairsPanel = new AvailableFskPairsPanel();
|
||||
const frequencyGraphPanel = new FrequencyGraphPanel();
|
||||
const graphConfigurationPanel = new GraphConfigurationPanel();
|
||||
|
||||
function handleWindowLoad() {
|
||||
const panelContainer = document.getElementById('panel-container');
|
||||
panelContainer.prepend(graphConfigurationPanel.getDomElement());
|
||||
panelContainer.prepend(frequencyGraphPanel.getDomElement());
|
||||
panelContainer.prepend(availableFskPairsPanel.getDomElement());
|
||||
panelContainer.prepend(packetizationPanel.getDomElement());
|
||||
panelContainer.prepend(signalPanel.getDomElement());
|
||||
@@ -96,6 +97,7 @@ function handleWindowLoad() {
|
||||
signalPanel.setSegmentDuration(30);
|
||||
signalPanel.setAmplitudeThreshold(0.78);
|
||||
signalPanel.setSmoothingTimeConstant(0);
|
||||
signalPanel.setTimeoutMilliseconds(60);
|
||||
|
||||
packetizationPanel.setSizePower(5);
|
||||
packetizationPanel.setErrorCorrection(true);
|
||||
@@ -103,7 +105,17 @@ function handleWindowLoad() {
|
||||
|
||||
availableFskPairsPanel.setFskPairs(frequencyPanel.getFskPairs());
|
||||
|
||||
// Communications Events
|
||||
graphConfigurationPanel.setDurationMilliseconds(signalPanel.getSegmentDuration() * 20);
|
||||
graphConfigurationPanel.setPauseAfterEnd(true);
|
||||
|
||||
frequencyGraphPanel.setFskPairs(availableFskPairsPanel.getSelectedFskPairs());
|
||||
frequencyGraphPanel.setAmplitudeThreshold(signalPanel.getAmplitudeThreshold());
|
||||
frequencyGraphPanel.setDurationMilliseconds(graphConfigurationPanel.getDurationMilliseconds());
|
||||
|
||||
AudioReceiver.setTimeoutMilliseconds(signalPanel.getTimeoutMilliseconds());
|
||||
|
||||
|
||||
// Events
|
||||
communicationsPanel.addEventListener('listeningChange', handleChangeListening);
|
||||
communicationsPanel.addEventListener('sendSpeakersChange', handleChangeSendSpeakers);
|
||||
communicationsPanel.addEventListener('sendAnalyzerChange', handleChangeSendAnalyzer);
|
||||
@@ -124,9 +136,18 @@ function handleWindowLoad() {
|
||||
});
|
||||
|
||||
signalPanel.addEventListener('waveformChange', updateAudioSender);
|
||||
signalPanel.addEventListener('segmentDurationChange', configurationChanged);
|
||||
signalPanel.addEventListener('amplitudeThresholdChange', configurationChanged);
|
||||
signalPanel.addEventListener('segmentDurationChange', (event) => {
|
||||
frequencyGraphPanel.setSamplingPeriod(event.value);
|
||||
configurationChanged();
|
||||
});
|
||||
signalPanel.addEventListener('amplitudeThresholdChange', ({value}) => {
|
||||
frequencyGraphPanel.setAmplitudeThreshold(value);
|
||||
configurationChanged();
|
||||
});
|
||||
signalPanel.addEventListener('smoothingConstantChange', configurationChanged);
|
||||
signalPanel.addEventListener('timeoutChange', () => {
|
||||
AudioReceiver.setTimeoutMilliseconds(signalPanel.getTimeoutMilliseconds());
|
||||
})
|
||||
|
||||
packetizationPanel.addEventListener('sizePowerChange', configurationChanged);
|
||||
packetizationPanel.addEventListener('interleavingChange', () => {
|
||||
@@ -137,6 +158,18 @@ function handleWindowLoad() {
|
||||
});
|
||||
packetizationPanel.addEventListener('errorCorrectionChange', configurationChanged);
|
||||
|
||||
availableFskPairsPanel.addEventListener('change', (event) => {
|
||||
frequencyGraphPanel.setFskPairs(event.selected);
|
||||
});
|
||||
graphConfigurationPanel.addEventListener('pauseAfterEndChange', (event) => {
|
||||
if(!frequencyGraphPanel.isRunning()) {
|
||||
frequencyGraphPanel.start();
|
||||
}
|
||||
})
|
||||
graphConfigurationPanel.addEventListener('durationChange', event => {
|
||||
frequencyGraphPanel.setDurationMilliseconds(graphConfigurationPanel.getDurationMilliseconds());
|
||||
});
|
||||
|
||||
// Setup audio sender
|
||||
AudioSender.addEventListener('begin', () => messagePanel.setSendButtonText('Stop'));
|
||||
AudioSender.addEventListener('send', handleAudioSenderSend);
|
||||
@@ -156,11 +189,6 @@ function handleWindowLoad() {
|
||||
receivedChannelGraph.addEventListener('mouseout', handleReceivedChannelGraphMouseout);
|
||||
receivedChannelGraph.addEventListener('mousemove', handleReceivedChannelGraphMousemove);
|
||||
receivedChannelGraph.addEventListener('click', handleReceivedChannelGraphClick);
|
||||
document.getElementById('pause-after-end').checked = PAUSE_AFTER_END;
|
||||
document.getElementById('pause-after-end').addEventListener('change', event => {
|
||||
PAUSE_AFTER_END = event.target.checked;
|
||||
if(!PAUSE_AFTER_END) resumeGraph();
|
||||
})
|
||||
document.getElementById('max-bits-displayed-on-graph').value= MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||
document.getElementById('max-bits-displayed-on-graph').addEventListener('input', (event) => {
|
||||
MAX_BITS_DISPLAYED_ON_GRAPH = parseInt(event.target.value);
|
||||
@@ -185,8 +213,6 @@ function handleAudioSenderSend({bits}) {
|
||||
}
|
||||
function configurationChanged() {
|
||||
if(analyser) analyser.fftSize = frequencyPanel.getFftSize();
|
||||
USED_FSK = calculateMultiFrequencyShiftKeying(false);
|
||||
AVAILABLE_FSK = calculateMultiFrequencyShiftKeying(true);
|
||||
updatePacketUtils();
|
||||
updateStreamManager();
|
||||
updateAudioSender();
|
||||
@@ -197,16 +223,14 @@ function configurationChanged() {
|
||||
}
|
||||
function updateAudioSender() {
|
||||
AudioSender.changeConfiguration({
|
||||
channels: USED_FSK,
|
||||
channels: availableFskPairsPanel.getSelectedFskPairs(),
|
||||
destination: SEND_VIA_SPEAKER ? audioContext.destination : getAnalyser(),
|
||||
waveForm: signalPanel.getWaveform()
|
||||
});
|
||||
}
|
||||
const logFn = text => (...args) => {
|
||||
// console.log(text, ...args);
|
||||
}
|
||||
const handleAudioReceiverStart = ({signalStart}) => {
|
||||
StreamManager.reset();
|
||||
frequencyGraphPanel.setSignalStart(signalStart);
|
||||
RECEIVED_STREAM_START_MS = signalStart;
|
||||
}
|
||||
const handleAudioReceiverReceive = ({signalStart, signalIndex, indexStart, bits}) => {
|
||||
@@ -216,15 +240,17 @@ const handleAudioReceiverReceive = ({signalStart, signalIndex, indexStart, bits}
|
||||
// console.log(signalIndex, packetIndex, segmentIndex, bits.join(''));
|
||||
StreamManager.addBits(packetIndex, segmentIndex, bits);
|
||||
}
|
||||
const handleAudioReceiverEnd = () => {
|
||||
if(PAUSE_AFTER_END) {
|
||||
const handleAudioReceiverEnd = (e) => {
|
||||
frequencyGraphPanel.setSignalEnd(e.signalEnd);
|
||||
if(graphConfigurationPanel.getPauseAfterEnd()) {
|
||||
stopGraph();
|
||||
frequencyGraphPanel.stop();
|
||||
AudioSender.stop();
|
||||
}
|
||||
}
|
||||
function updateAudioReceiver() {
|
||||
AudioReceiver.changeConfiguration({
|
||||
fskSets: USED_FSK,
|
||||
fskSets: availableFskPairsPanel.getSelectedFskPairs(),
|
||||
amplitudeThreshold: Math.floor(signalPanel.getAmplitudeThreshold() * 255),
|
||||
analyser: getAnalyser(),
|
||||
signalIntervalMs: signalPanel.getSegmentDuration(),
|
||||
@@ -238,7 +264,7 @@ function updateStreamManager() {
|
||||
StreamManager.changeConfiguration({
|
||||
bitsPerPacket: PacketUtils.getPacketMaxBitCount(),
|
||||
segmentsPerPacket: PacketUtils.getPacketSegmentCount(),
|
||||
bitsPerSegment: USED_FSK.length,
|
||||
bitsPerSegment: availableFskPairsPanel.getSelectedFskPairs().length,
|
||||
streamHeaders: {
|
||||
'transfer byte count': {
|
||||
index: 0,
|
||||
@@ -255,7 +281,7 @@ function updatePacketUtils() {
|
||||
PacketUtils.setEncoding(
|
||||
packetizationPanel.getErrorCorrection() ? HammingEncoding : undefined
|
||||
);
|
||||
const bitsPerSegment = USED_FSK.length;
|
||||
const bitsPerSegment = availableFskPairsPanel.getSelectedFskPairs().length;
|
||||
PacketUtils.changeConfiguration({
|
||||
segmentDurationMilliseconds: signalPanel.getSegmentDuration(),
|
||||
packetSizeBitCount: packetizationPanel.getSizePower(),
|
||||
@@ -314,7 +340,7 @@ function drawChannels() {
|
||||
const sampleRate = getAudioContext().sampleRate;
|
||||
const fftSize = frequencyPanel.getFftSize();
|
||||
const frequencyResolution = sampleRate / fftSize;
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelCount = channels.length;
|
||||
const canvas = document.getElementById('channel-frequency-graph');
|
||||
const ctx = canvas.getContext('2d');
|
||||
@@ -356,30 +382,7 @@ function percentInFrequency(hz, frequencyResolution) {
|
||||
const percent = hzInSegement / frequencyResolution;
|
||||
return percent;
|
||||
}
|
||||
function calculateMultiFrequencyShiftKeying(includeExcluded = false) {
|
||||
var audioContext = getAudioContext();
|
||||
const sampleRate = audioContext.sampleRate;
|
||||
const fftSize = frequencyPanel.getFftSize();
|
||||
const frequencyResolution = sampleRate / fftSize;
|
||||
const channels = [];
|
||||
const pairStep = frequencyResolution * (2 + frequencyPanel.getMultiFskPadding()) * frequencyPanel.getFskPadding();
|
||||
let channelId = -1;
|
||||
const minimumFrequency = frequencyPanel.getMinimumFrequency();
|
||||
const maximumFrequency = frequencyPanel.getMaximumFrequency();
|
||||
for(let hz = minimumFrequency; hz < maximumFrequency; hz+= pairStep) {
|
||||
const low = hz;
|
||||
const high = hz + frequencyResolution * frequencyPanel.getFskPadding();
|
||||
if(low < minimumFrequency) continue;
|
||||
if(high > maximumFrequency) break;
|
||||
channelId++;
|
||||
|
||||
if(!includeExcluded) {
|
||||
if(EXCLUDED_CHANNELS.includes(channelId)) continue;
|
||||
}
|
||||
channels.push([low, high]);
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
function logSent(text) {
|
||||
// display what is being sent
|
||||
sentDataTextArea.value += text + '\n';
|
||||
@@ -447,7 +450,7 @@ function sendBytes(bytes) {
|
||||
resumeGraph();
|
||||
}
|
||||
function showSentBits() {
|
||||
const channelCount = USED_FSK.length;
|
||||
const channelCount = availableFskPairsPanel.getSelectedFskPairs().length;
|
||||
|
||||
// original bits
|
||||
document.getElementById('sent-data').innerHTML =
|
||||
@@ -474,7 +477,7 @@ function showSentBits() {
|
||||
), ''));
|
||||
}
|
||||
function sendPacket(bits, packetStartSeconds) {
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelCount = channels.length;
|
||||
let bitCount = bits.length;
|
||||
const segmentDurationSeconds = PacketUtils.getSegmentDurationSeconds();
|
||||
@@ -542,7 +545,7 @@ function getTransferredCorrectedBits() {
|
||||
}
|
||||
|
||||
function handleStreamManagerChange() {
|
||||
const channelCount = USED_FSK.length;
|
||||
const channelCount = availableFskPairsPanel.getSelectedFskPairs().length;
|
||||
let allRawBits = StreamManager.getStreamBits();
|
||||
let allEncodedBits = StreamManager.getAllPacketBits();
|
||||
let allDecodedBits = getTransferredCorrectedBits();
|
||||
@@ -642,7 +645,7 @@ function parseTotalBitsTransferring() {
|
||||
const dataByteCount = parseTransmissionByteCount();
|
||||
const bitCount = PacketUtils.getPacketizationBitCountFromByteCount(dataByteCount);
|
||||
const segments = getTotalSegmentCount(bitCount);
|
||||
return segments * USED_FSK.length;
|
||||
return segments * availableFskPairsPanel.getSelectedFskPairs().length;
|
||||
}
|
||||
function parseTransmissionByteCountCrc() {
|
||||
let decodedBits = getTransferredCorrectedBits();
|
||||
@@ -841,6 +844,7 @@ function handleSendButtonClick() {
|
||||
} else {
|
||||
AudioReceiver.reset();
|
||||
StreamManager.reset();
|
||||
frequencyGraphPanel.start();
|
||||
const text = messagePanel.getMessage();
|
||||
sendBytes(textToBytes(text));
|
||||
}
|
||||
@@ -848,6 +852,7 @@ function handleSendButtonClick() {
|
||||
function getAnalyser() {
|
||||
if(analyser) return analyser;
|
||||
analyser = audioContext.createAnalyser();
|
||||
frequencyGraphPanel.setAnalyser(analyser);
|
||||
analyser.smoothingTimeConstant = signalPanel.getSmoothingTimeConstant();
|
||||
analyser.fftSize = frequencyPanel.getFftSize();
|
||||
return analyser;
|
||||
@@ -1122,7 +1127,7 @@ function drawChannelData() {
|
||||
// ended too long ago?
|
||||
if(lastStreamEnded < graphEarliest) return;
|
||||
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelCount = channels.length;
|
||||
|
||||
const canvas = document.getElementById('received-channel-graph');
|
||||
@@ -1316,7 +1321,7 @@ function drawSelectedChannel(ctx, channelCount, width, height) {
|
||||
}
|
||||
function drawChannelNumbers(ctx, channelCount, width, height) {
|
||||
const offset = 0;
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelHeight = height / channelCount;
|
||||
const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||
let fontHeight = Math.min(24, channelHeight, segmentWidth);
|
||||
@@ -1364,7 +1369,7 @@ function drawFrequencyData(forcedDraw) {
|
||||
ctx.stroke();
|
||||
drawBitDurationLines(ctx, 'rgba(255, 255, 0, .25)');
|
||||
drawBitStart(ctx, 'green');
|
||||
const frequencies = USED_FSK;
|
||||
const frequencies = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const high = 1;
|
||||
const low = 0
|
||||
const isSelectedOrOver = CHANNEL_OVER !== -1 || CHANNEL_SELECTED !== -1;
|
||||
@@ -1453,7 +1458,7 @@ function handleReceivedChannelGraphClick(e) {
|
||||
const {channelIndex, segmentIndex} = getChannelAndSegment(e);
|
||||
CHANNEL_SELECTED = channelIndex;
|
||||
SEGMENT_SELECTED = segmentIndex;
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelCount = channels.length;
|
||||
|
||||
const selectedSamples = document.getElementById('selected-samples');
|
||||
@@ -1561,7 +1566,7 @@ function getChannelAndSegment(e) {
|
||||
segmentIndex: -1
|
||||
};
|
||||
// what channel are we over?
|
||||
const channels = USED_FSK;
|
||||
const channels = availableFskPairsPanel.getSelectedFskPairs();
|
||||
const channelCount = channels.length;
|
||||
let channelIndex = Math.floor((y / height) * channelCount);
|
||||
if(channelIndex === channelCount) channelIndex--;
|
||||
|
||||
Reference in New Issue
Block a user