show failed segments per channel
This commit is contained in:
@@ -41,7 +41,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<h2>Frequency Graph</h2>
|
<h2>Frequency Graph</h2>
|
||||||
<canvas id="received-graph" width="800" height="150"></canvas><br>
|
<canvas id="received-graph" width="800" height="150"></canvas><br>
|
||||||
<canvas id="received-channel-graph" width="800" height="150"></canvas><br>
|
<canvas id="received-channel-graph" width="800" height="300"></canvas><br>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" id="pause-after-end" checked>Pause after end
|
<input type="checkbox" id="pause-after-end" checked>Pause after end
|
||||||
</label><br>
|
</label><br>
|
||||||
|
|||||||
342
index.js
342
index.js
@@ -10,41 +10,44 @@ var receivedDataTextarea;
|
|||||||
var sentDataTextArea;
|
var sentDataTextArea;
|
||||||
var receivedGraph;
|
var receivedGraph;
|
||||||
var receivedData = [];
|
var receivedData = [];
|
||||||
var MAX_BITS_DISPLAYED_ON_GRAPH = 9;
|
|
||||||
var MAX_DATA = 300;
|
var MAX_DATA = 300;
|
||||||
var pauseTimeoutId;
|
var pauseTimeoutId;
|
||||||
var sampleIntervalId;
|
var sampleIntervalId;
|
||||||
|
|
||||||
|
var TEXT_TO_SEND = "U";
|
||||||
|
var MAX_BITS_DISPLAYED_ON_GRAPH = 115;
|
||||||
|
var SEGMENT_DURATION = 30;
|
||||||
|
var AMPLITUDE_THRESHOLD_PERCENT = .75;
|
||||||
|
var AMPLITUDE_THRESHOLD = 160;
|
||||||
|
var MINIMUM_FREQUENCY = 308;
|
||||||
|
var MAXIMUM_FREQUENCY = 3400;
|
||||||
|
var LAST_SEGMENT_PERCENT = 0.6;
|
||||||
|
var FFT_SIZE_POWER = 10;
|
||||||
|
var FREQUENCY_RESOLUTION_MULTIPLIER = 2;
|
||||||
|
var SMOOTHING_TIME_CONSTANT = 0;
|
||||||
var HAMMING_ERROR_CORRECTION = true;
|
var HAMMING_ERROR_CORRECTION = true;
|
||||||
|
|
||||||
// 20 to 20,000 - human
|
|
||||||
var TEXT_TO_SEND = "Hello World!";
|
|
||||||
var MINIMUM_FREQUENCY = 5000;
|
|
||||||
var MAXIMUM_FREQUENCY = 10000;
|
|
||||||
var FREQUENCY_DURATION = 60;
|
|
||||||
var FREQUENCY_THRESHOLD_PERCENT = .75;
|
|
||||||
var FREQUENCY_THRESHOLD = 150;
|
|
||||||
var FREQUENCY_RESOLUTION_MULTIPLIER = 2;
|
|
||||||
var SAMPLE_DELAY_MS = 1;
|
var SAMPLE_DELAY_MS = 1;
|
||||||
var FFT_POWER = 10;
|
|
||||||
var LAST_BIT_PERCENT = 0.8;
|
|
||||||
var SMOOTHING_TIME_CONSTANT = 0;
|
|
||||||
var frequencyOverTime = [];
|
var frequencyOverTime = [];
|
||||||
var bitStart = [];
|
var bitStart = [];
|
||||||
var samplesPerBit = [];
|
var samplesPerBit = [];
|
||||||
var bitSampleCount = 0;
|
var bitSampleCount = 0;
|
||||||
var PAUSE = false;
|
var PAUSE = false;
|
||||||
var PAUSE_AFTER_END = true;
|
var PAUSE_AFTER_END = true;
|
||||||
var PACKET_SIZE_BITS = 10;
|
var PACKET_SIZE_BITS = 8;
|
||||||
|
|
||||||
var EXPECTED_ENCODED_BITS = [];
|
var EXPECTED_ENCODED_BITS = [];
|
||||||
var EXPECTED_BITS = [];
|
var EXPECTED_BITS = [];
|
||||||
var EXPECTED_TEXT = '';
|
var EXPECTED_TEXT = '';
|
||||||
|
|
||||||
const packetBits = [];
|
const packetReceivedBits = [];
|
||||||
const packetDecodedBits = [];
|
const packetDecodedBits = [];
|
||||||
let packetDataByteCount = -1;
|
let packetDataByteCount = -1;
|
||||||
|
|
||||||
function handleWindowLoad() {
|
function handleWindowLoad() {
|
||||||
|
const printable = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>?";
|
||||||
|
TEXT_TO_SEND = new Array(128).fill(0).map(() => printable[Math.floor(Math.random() * printable.length)]).join('');
|
||||||
|
|
||||||
// grab dom elements
|
// grab dom elements
|
||||||
sendButton = document.getElementById('send-button');
|
sendButton = document.getElementById('send-button');
|
||||||
isListeningCheckbox = document.getElementById('is-listening-checkbox');
|
isListeningCheckbox = document.getElementById('is-listening-checkbox');
|
||||||
@@ -70,7 +73,7 @@ function handleWindowLoad() {
|
|||||||
showSpeed();
|
showSpeed();
|
||||||
})
|
})
|
||||||
document.getElementById('bit-duration-text').addEventListener('input', (event) => {
|
document.getElementById('bit-duration-text').addEventListener('input', (event) => {
|
||||||
FREQUENCY_DURATION = parseInt(event.target.value);
|
SEGMENT_DURATION = parseInt(event.target.value);
|
||||||
bitSampleCount = 0;
|
bitSampleCount = 0;
|
||||||
samplesPerBit.length = 0;
|
samplesPerBit.length = 0;
|
||||||
showSpeed();
|
showSpeed();
|
||||||
@@ -79,18 +82,18 @@ function handleWindowLoad() {
|
|||||||
document.getElementById('max-bits-displayed-on-graph').addEventListener('input', (event) => {
|
document.getElementById('max-bits-displayed-on-graph').addEventListener('input', (event) => {
|
||||||
MAX_BITS_DISPLAYED_ON_GRAPH = parseInt(event.target.value);
|
MAX_BITS_DISPLAYED_ON_GRAPH = parseInt(event.target.value);
|
||||||
})
|
})
|
||||||
document.getElementById('bit-duration-text').value = FREQUENCY_DURATION;
|
document.getElementById('bit-duration-text').value = SEGMENT_DURATION;
|
||||||
document.getElementById('amplitude-threshold-text').value = Math.floor(FREQUENCY_THRESHOLD_PERCENT * 100);
|
document.getElementById('amplitude-threshold-text').value = Math.floor(AMPLITUDE_THRESHOLD_PERCENT * 100);
|
||||||
FREQUENCY_THRESHOLD = Math.floor(FREQUENCY_THRESHOLD_PERCENT * 255);
|
AMPLITUDE_THRESHOLD = Math.floor(AMPLITUDE_THRESHOLD_PERCENT * 255);
|
||||||
document.getElementById('maximum-frequency').value = MAXIMUM_FREQUENCY;
|
document.getElementById('maximum-frequency').value = MAXIMUM_FREQUENCY;
|
||||||
document.getElementById('minimum-frequency').value = MINIMUM_FREQUENCY;
|
document.getElementById('minimum-frequency').value = MINIMUM_FREQUENCY;
|
||||||
document.getElementById('last-bit-percent').value = Math.floor(LAST_BIT_PERCENT * 100);
|
document.getElementById('last-bit-percent').value = Math.floor(LAST_SEGMENT_PERCENT * 100);
|
||||||
document.getElementById('fft-size-power-text').value = FFT_POWER;
|
document.getElementById('fft-size-power-text').value = FFT_SIZE_POWER;
|
||||||
document.getElementById('smoothing-time-constant-text').value = SMOOTHING_TIME_CONSTANT.toFixed(2);
|
document.getElementById('smoothing-time-constant-text').value = SMOOTHING_TIME_CONSTANT.toFixed(2);
|
||||||
|
|
||||||
document.getElementById('amplitude-threshold-text').addEventListener('input', (event) => {
|
document.getElementById('amplitude-threshold-text').addEventListener('input', (event) => {
|
||||||
FREQUENCY_THRESHOLD_PERCENT = parseInt(event.target.value) / 100;
|
AMPLITUDE_THRESHOLD_PERCENT = parseInt(event.target.value) / 100;
|
||||||
FREQUENCY_THRESHOLD = Math.floor(FREQUENCY_THRESHOLD_PERCENT * 255);
|
AMPLITUDE_THRESHOLD = Math.floor(AMPLITUDE_THRESHOLD_PERCENT * 255);
|
||||||
});
|
});
|
||||||
document.getElementById('maximum-frequency').addEventListener('input', (event) => {
|
document.getElementById('maximum-frequency').addEventListener('input', (event) => {
|
||||||
MAXIMUM_FREQUENCY = parseInt(event.target.value);
|
MAXIMUM_FREQUENCY = parseInt(event.target.value);
|
||||||
@@ -101,11 +104,11 @@ function handleWindowLoad() {
|
|||||||
showSpeed();
|
showSpeed();
|
||||||
});
|
});
|
||||||
document.getElementById('last-bit-percent').addEventListener('input', (event) => {
|
document.getElementById('last-bit-percent').addEventListener('input', (event) => {
|
||||||
LAST_BIT_PERCENT = parseInt(event.target.value) / 100;
|
LAST_SEGMENT_PERCENT = parseInt(event.target.value) / 100;
|
||||||
});
|
});
|
||||||
document.getElementById('fft-size-power-text').addEventListener('input', (event) => {
|
document.getElementById('fft-size-power-text').addEventListener('input', (event) => {
|
||||||
FFT_POWER = parseInt(event.target.value);
|
FFT_SIZE_POWER = parseInt(event.target.value);
|
||||||
if(analyser) analyser.fftSize = 2 ** FFT_POWER;
|
if(analyser) analyser.fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
updateFrequencyResolution();
|
updateFrequencyResolution();
|
||||||
resetGraphData();
|
resetGraphData();
|
||||||
});
|
});
|
||||||
@@ -133,7 +136,7 @@ function handleTextToSendInput() {
|
|||||||
const totalBytes = Math.ceil(totalBits / 8);
|
const totalBytes = Math.ceil(totalBits / 8);
|
||||||
const channelCount = getChannels().length;
|
const channelCount = getChannels().length;
|
||||||
const segmentCount = Math.ceil(totalBits / channelCount);
|
const segmentCount = Math.ceil(totalBits / channelCount);
|
||||||
const totalDuration = ((segmentCount * FREQUENCY_DURATION) / 1000);
|
const totalDuration = ((segmentCount * SEGMENT_DURATION) / 1000);
|
||||||
|
|
||||||
document.getElementById('error-correction-bits').innerText = errorCorrectionBits.toLocaleString();
|
document.getElementById('error-correction-bits').innerText = errorCorrectionBits.toLocaleString();
|
||||||
document.getElementById('data-bytes-to-send').innerText = dataByteCount.toLocaleString();
|
document.getElementById('data-bytes-to-send').innerText = dataByteCount.toLocaleString();
|
||||||
@@ -143,13 +146,13 @@ function handleTextToSendInput() {
|
|||||||
document.getElementById('duration-to-send').innerText = totalDuration.toLocaleString();
|
document.getElementById('duration-to-send').innerText = totalDuration.toLocaleString();
|
||||||
document.getElementById('packet-send-channel-count').innerText = channelCount.toLocaleString();
|
document.getElementById('packet-send-channel-count').innerText = channelCount.toLocaleString();
|
||||||
document.getElementById('packet-send-segment-count').innerText = segmentCount.toLocaleString();
|
document.getElementById('packet-send-segment-count').innerText = segmentCount.toLocaleString();
|
||||||
document.getElementById('packet-send-segment-duration').innerText = (FREQUENCY_DURATION / 1000).toLocaleString();
|
document.getElementById('packet-send-segment-duration').innerText = (SEGMENT_DURATION / 1000).toLocaleString();
|
||||||
document.getElementById('data-size-header-bits').innerText = PACKET_SIZE_BITS.toLocaleString();
|
document.getElementById('data-size-header-bits').innerText = PACKET_SIZE_BITS.toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFrequencyResolution() {
|
function updateFrequencyResolution() {
|
||||||
const sampleRate = getAudioContext().sampleRate;
|
const sampleRate = getAudioContext().sampleRate;
|
||||||
const fftSize = 2 ** FFT_POWER;
|
const fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
const frequencyResolution = sampleRate / fftSize;
|
const frequencyResolution = sampleRate / fftSize;
|
||||||
const frequencyCount = (sampleRate/2) / frequencyResolution;
|
const frequencyCount = (sampleRate/2) / frequencyResolution;
|
||||||
document.getElementById('frequency-resolution').innerText = frequencyResolution.toFixed(2);
|
document.getElementById('frequency-resolution').innerText = frequencyResolution.toFixed(2);
|
||||||
@@ -159,7 +162,7 @@ function updateFrequencyResolution() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showSpeed() {
|
function showSpeed() {
|
||||||
const segmentsPerSecond = 1000 / FREQUENCY_DURATION;
|
const segmentsPerSecond = 1000 / SEGMENT_DURATION;
|
||||||
const channels = getChannels();
|
const channels = getChannels();
|
||||||
const bitsPerSegment = channels.length;
|
const bitsPerSegment = channels.length;
|
||||||
const baud = bitsPerSegment * segmentsPerSecond;
|
const baud = bitsPerSegment * segmentsPerSecond;
|
||||||
@@ -192,7 +195,7 @@ function showSpeed() {
|
|||||||
}
|
}
|
||||||
function drawChannels() {
|
function drawChannels() {
|
||||||
const sampleRate = getAudioContext().sampleRate;
|
const sampleRate = getAudioContext().sampleRate;
|
||||||
const fftSize = 2 ** FFT_POWER;
|
const fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
const frequencyResolution = sampleRate / fftSize;
|
const frequencyResolution = sampleRate / fftSize;
|
||||||
//const frequencyCount = (sampleRate/2) / frequencyResolution;
|
//const frequencyCount = (sampleRate/2) / frequencyResolution;
|
||||||
const channels = getChannels();
|
const channels = getChannels();
|
||||||
@@ -285,7 +288,7 @@ function getFrequency(bit) {
|
|||||||
function getChannels() {
|
function getChannels() {
|
||||||
var audioContext = getAudioContext();
|
var audioContext = getAudioContext();
|
||||||
const sampleRate = audioContext.sampleRate;
|
const sampleRate = audioContext.sampleRate;
|
||||||
const fftSize = 2 ** FFT_POWER;
|
const fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
const frequencyResolution = sampleRate / fftSize;
|
const frequencyResolution = sampleRate / fftSize;
|
||||||
const channels = [];
|
const channels = [];
|
||||||
const pairStep = frequencyResolution * 2 * FREQUENCY_RESOLUTION_MULTIPLIER;
|
const pairStep = frequencyResolution * 2 * FREQUENCY_RESOLUTION_MULTIPLIER;
|
||||||
@@ -337,6 +340,8 @@ function sendBits(bits) {
|
|||||||
}
|
}
|
||||||
document.getElementById('encoded-data').value = encodedBits.reduce(bitReducer, '');
|
document.getElementById('encoded-data').value = encodedBits.reduce(bitReducer, '');
|
||||||
bits = encodedBits;
|
bits = encodedBits;
|
||||||
|
} else {
|
||||||
|
document.getElementById('encoded-data').value = bits.reduce(bitReducer, '');
|
||||||
}
|
}
|
||||||
EXPECTED_ENCODED_BITS = bits.slice();
|
EXPECTED_ENCODED_BITS = bits.slice();
|
||||||
|
|
||||||
@@ -359,7 +364,7 @@ function sendBits(bits) {
|
|||||||
const isHigh = bits[i];
|
const isHigh = bits[i];
|
||||||
const channel = i % channelCount;
|
const channel = i % channelCount;
|
||||||
const segment = Math.floor(i / channelCount);
|
const segment = Math.floor(i / channelCount);
|
||||||
var offset = ((segment * FREQUENCY_DURATION)/1000);
|
var offset = ((segment * SEGMENT_DURATION)/1000);
|
||||||
oscillators[channel].frequency.setValueAtTime(
|
oscillators[channel].frequency.setValueAtTime(
|
||||||
channels[channel][isHigh ? 1 : 0],
|
channels[channel][isHigh ? 1 : 0],
|
||||||
currentTime + offset
|
currentTime + offset
|
||||||
@@ -373,7 +378,7 @@ function sendBits(bits) {
|
|||||||
for(let i = bits.length; i < bits.length + channelCount; i++) {
|
for(let i = bits.length; i < bits.length + channelCount; i++) {
|
||||||
const channel = i % channelCount;
|
const channel = i % channelCount;
|
||||||
const segment = Math.floor(i / channelCount);
|
const segment = Math.floor(i / channelCount);
|
||||||
const offset = ((segment * FREQUENCY_DURATION) / 1000);
|
const offset = ((segment * SEGMENT_DURATION) / 1000);
|
||||||
oscillators[channel].stop(currentTime + offset);
|
oscillators[channel].stop(currentTime + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +435,7 @@ function collectSample() {
|
|||||||
analyser.getByteFrequencyData(frequencies);
|
analyser.getByteFrequencyData(frequencies);
|
||||||
const data = {
|
const data = {
|
||||||
time,
|
time,
|
||||||
frequencies,
|
frequencies: [...frequencies],
|
||||||
length,
|
length,
|
||||||
streamEnded: priorStreamEnded
|
streamEnded: priorStreamEnded
|
||||||
};
|
};
|
||||||
@@ -438,8 +443,8 @@ function collectSample() {
|
|||||||
data.pairs = getChannels().map(([low, high], i) => {
|
data.pairs = getChannels().map(([low, high], i) => {
|
||||||
const lowAmp = frequencies[Math.round(low / length)];
|
const lowAmp = frequencies[Math.round(low / length)];
|
||||||
const highAmp = frequencies[Math.round(high / length)];
|
const highAmp = frequencies[Math.round(high / length)];
|
||||||
const isLow = lowAmp > FREQUENCY_THRESHOLD;
|
const isLow = lowAmp > AMPLITUDE_THRESHOLD;
|
||||||
const isHigh = highAmp > FREQUENCY_THRESHOLD;
|
const isHigh = highAmp > AMPLITUDE_THRESHOLD;
|
||||||
if(isLow || isHigh ) hasSignal = true;
|
if(isLow || isHigh ) hasSignal = true;
|
||||||
return {
|
return {
|
||||||
channel: i,
|
channel: i,
|
||||||
@@ -463,13 +468,13 @@ function collectSample() {
|
|||||||
// new bit stream
|
// new bit stream
|
||||||
data.streamStarted = time;
|
data.streamStarted = time;
|
||||||
// clear last packet
|
// clear last packet
|
||||||
packetBits.length = 0;
|
packetReceivedBits.length = 0;
|
||||||
packetDataByteCount = 0;
|
packetDataByteCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of bit in the stream
|
// number of bit in the stream
|
||||||
const segmentIndex = data.segmentIndex = Math.floor((time - initialStreamStart) / FREQUENCY_DURATION);
|
const segmentIndex = data.segmentIndex = Math.floor((time - data.streamStarted) / SEGMENT_DURATION);
|
||||||
if(priorSegmentIndex !== segmentIndex && priorSegmentIndex !== -1) {
|
if(priorSegmentIndex !== segmentIndex && priorSegmentIndex > -1) {
|
||||||
processSegment = true;
|
processSegment = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -490,7 +495,7 @@ function collectSample() {
|
|||||||
pauseTimeoutId = window.setTimeout(() => {
|
pauseTimeoutId = window.setTimeout(() => {
|
||||||
pauseTimeoutId = undefined;
|
pauseTimeoutId = undefined;
|
||||||
if(PAUSE_AFTER_END) stopGraph();
|
if(PAUSE_AFTER_END) stopGraph();
|
||||||
}, FREQUENCY_DURATION * 1.5);
|
}, SEGMENT_DURATION * 0.5);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// continued stopping (or never started)
|
// continued stopping (or never started)
|
||||||
@@ -498,70 +503,73 @@ function collectSample() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
frequencyOverTime.unshift(data);
|
frequencyOverTime.unshift(data);
|
||||||
if(processSegment) processSegmentReceived();
|
if(processSegment) processSegmentReceived(initialStreamStart, priorSegmentIndex);
|
||||||
truncateGraphData();
|
truncateGraphData();
|
||||||
}
|
}
|
||||||
|
|
||||||
function evaluateBit(samples, segment, channel) {
|
function GET_SEGMENT_BITS(streamStarted, segmentIndex) {
|
||||||
const started = samples.find(s => s.streamStarted > 0).streamStarted;
|
|
||||||
const bitSamples = samples.filter(sample => {
|
|
||||||
return sample.time >= started + (segment * FREQUENCY_DURATION) &&
|
|
||||||
sample.time < started + ((segment + 1) * FREQUENCY_DURATION)
|
|
||||||
}).map(samples => samples.pairs[channel])
|
|
||||||
.reduce((bitSamples, { lowAmp, highAmp }) => {
|
|
||||||
bitSamples.high += highAmp;
|
|
||||||
bitSamples.low += lowAmp;
|
|
||||||
}, {high: 0, low: 0});
|
|
||||||
return bitSamples.high >= bitSamples.low ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processSegmentReceived() {
|
|
||||||
const {
|
|
||||||
segmentIndex,
|
|
||||||
streamStarted,
|
|
||||||
pairs: {
|
|
||||||
length: channelCount
|
|
||||||
}
|
|
||||||
} = frequencyOverTime[0];
|
|
||||||
const bits = frequencyOverTime.filter(f =>
|
const bits = frequencyOverTime.filter(f =>
|
||||||
f.segmentIndex === segmentIndex &&
|
f.segmentIndex === segmentIndex &&
|
||||||
f.streamStarted === streamStarted
|
f.streamStarted === streamStarted
|
||||||
);
|
);
|
||||||
const bitEnded = bits[0].time;
|
const channelCount = frequencyOverTime[0].pairs.length;
|
||||||
const bitStarted = streamStarted + (FREQUENCY_DURATION * segmentIndex);
|
const sums = new Array(channelCount).fill(0).map(() => ({
|
||||||
const bitDuration = bitEnded - bitStarted;
|
high: 0,
|
||||||
|
low: 0,
|
||||||
// if(bitDuration < FREQUENCY_DURATION * LAST_BIT_PERCENT) {
|
heard: 0
|
||||||
// return;
|
}));
|
||||||
// }
|
|
||||||
|
|
||||||
const channels = new Array(channelCount).fill(0).map(() => ({highAmp: 0, lowAmp: 0, isMissing: 0}));
|
|
||||||
|
|
||||||
bits.forEach(({pairs}) => {
|
bits.forEach(({pairs}) => {
|
||||||
|
pairs.forEach(({ highAmp, lowAmp, channel }) => {
|
||||||
pairs.forEach(({ highAmp, lowAmp, isMissing }, i) => {
|
sums[channel].high += highAmp;
|
||||||
channels[i].highAmp += highAmp;
|
sums[channel].low += lowAmp;
|
||||||
channels[i].lowAmp += lowAmp;
|
if(highAmp > AMPLITUDE_THRESHOLD || lowAmp > AMPLITUDE_THRESHOLD) {
|
||||||
// else if(isMissing) channels[i].isMissing ++;
|
sums[channel].heard++;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
const bitValues = channels.map(({highAmp, lowAmp, isMissing}) => {
|
const bitValues = sums.map(({high, low}) => high >= low ? 1 : 0);
|
||||||
return highAmp >= lowAmp ? 1 : 0;
|
// cut off silent bits
|
||||||
});
|
// const lastHeard = sums.lastIndexOf(s => s.heard !== 0);
|
||||||
|
const lastHeard = sums.length -1;
|
||||||
|
return bitValues.slice(0, lastHeard + 1);
|
||||||
|
}
|
||||||
|
function processSegmentReceived(streamStarted, segmentIndex) {
|
||||||
|
const {
|
||||||
|
pairs: {
|
||||||
|
length: channelCount
|
||||||
|
}
|
||||||
|
} = frequencyOverTime[0];
|
||||||
|
// is our segment long enough?
|
||||||
|
|
||||||
packetBits.push(...bitValues);
|
const samples = frequencyOverTime.filter(
|
||||||
|
fot => fot.streamStarted === streamStarted &&
|
||||||
|
fot.segmentIndex === segmentIndex
|
||||||
|
);
|
||||||
|
if(samples.length <= 1) return; // too short
|
||||||
|
const sampleEnd = samples[0].time;
|
||||||
|
const sampleStart = samples[samples.length-1].time;
|
||||||
|
const sampleDuration = sampleEnd - sampleStart;
|
||||||
|
|
||||||
|
// not long enough to qualify as a segment
|
||||||
|
if((sampleDuration / SEGMENT_DURATION) < LAST_SEGMENT_PERCENT) return;
|
||||||
|
|
||||||
|
const bitValues = GET_SEGMENT_BITS(streamStarted, segmentIndex);
|
||||||
|
|
||||||
|
|
||||||
|
console.log("%s Received: %s from %s samples", segmentIndex, bitValues.join(''), samples.length);
|
||||||
|
packetReceivedBits.push(...bitValues);
|
||||||
|
|
||||||
const encodingRatio = HAMMING_ERROR_CORRECTION ? 7/4 : 1;
|
const encodingRatio = HAMMING_ERROR_CORRECTION ? 7/4 : 1;
|
||||||
if(HAMMING_ERROR_CORRECTION) {
|
if(HAMMING_ERROR_CORRECTION) {
|
||||||
packetDecodedBits.length = 0;
|
packetDecodedBits.length = 0;
|
||||||
for(let i = 0; i < packetBits.length; i += 7) {
|
for(let i = 0; i < packetReceivedBits.length; i += 7) {
|
||||||
const hamming = packetBits.slice(i, i + 7);
|
const hamming = packetReceivedBits.slice(i, i + 7);
|
||||||
const nibble = hammingToNibble(hamming);
|
const nibble = hammingToNibble(hamming);
|
||||||
packetDecodedBits.push(...nibble);
|
packetDecodedBits.push(...nibble);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
packetDecodedBits.length = 0;
|
packetDecodedBits.length = 0;
|
||||||
packetDecodedBits.push(...packetBits);
|
packetDecodedBits.push(...packetReceivedBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine if we can identify the length of data comming
|
// Determine if we can identify the length of data comming
|
||||||
@@ -578,7 +586,7 @@ function processSegmentReceived() {
|
|||||||
// let's get the end time
|
// let's get the end time
|
||||||
const totalBits = Math.ceil(((packetDataByteCount * 8) + PACKET_SIZE_BITS) * encodingRatio);
|
const totalBits = Math.ceil(((packetDataByteCount * 8) + PACKET_SIZE_BITS) * encodingRatio);
|
||||||
const segments = Math.ceil(totalBits / channelCount);
|
const segments = Math.ceil(totalBits / channelCount);
|
||||||
const duration = segments * FREQUENCY_DURATION;
|
const duration = segments * SEGMENT_DURATION;
|
||||||
const streamEnded = streamStarted + duration;
|
const streamEnded = streamStarted + duration;
|
||||||
// console.log({
|
// console.log({
|
||||||
// tenBitNum: packetDecodedBits
|
// tenBitNum: packetDecodedBits
|
||||||
@@ -600,19 +608,19 @@ function processSegmentReceived() {
|
|||||||
// }
|
// }
|
||||||
// remove phantom bits
|
// remove phantom bits
|
||||||
// const totalBits = Math.ceil(((packetDataByteCount * 8) + PACKET_SIZE_BITS) * encodingRatio);
|
// const totalBits = Math.ceil(((packetDataByteCount * 8) + PACKET_SIZE_BITS) * encodingRatio);
|
||||||
if(packetBits.length > totalBits) {
|
if(packetReceivedBits.length > totalBits) {
|
||||||
// const excess = packetBits.length % totalBits;
|
// const excess = packetReceivedBits.length % totalBits;
|
||||||
// packetBits.length = totalBits;
|
// packetReceivedBits.length = totalBits;
|
||||||
// bitValues.length = bitValues.length - excess;
|
// bitValues.length = bitValues.length - excess;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('decoded-data').innerHTML = packetDecodedBits.reduce(bitExpectorReducer(EXPECTED_BITS), '');
|
document.getElementById('decoded-data').innerHTML = packetDecodedBits.reduce(bitExpectorReducer(EXPECTED_BITS), '');
|
||||||
document.getElementById('received-data').innerHTML = packetBits.reduce(bitExpectorReducer(EXPECTED_ENCODED_BITS), '');
|
document.getElementById('received-data').innerHTML = packetReceivedBits.reduce(bitExpectorReducer(EXPECTED_ENCODED_BITS), '');
|
||||||
|
|
||||||
const encodedBitCount = EXPECTED_ENCODED_BITS.length;
|
const encodedBitCount = EXPECTED_ENCODED_BITS.length;
|
||||||
const decodedBitCount = EXPECTED_BITS.length;
|
const decodedBitCount = EXPECTED_BITS.length;
|
||||||
const correctEncodedBits = packetBits.filter((b, i) => i < encodedBitCount && b === EXPECTED_ENCODED_BITS[i]).length;
|
const correctEncodedBits = packetReceivedBits.filter((b, i) => i < encodedBitCount && b === EXPECTED_ENCODED_BITS[i]).length;
|
||||||
const correctedDecodedBits = packetDecodedBits.filter((b, i) => i < decodedBitCount && b === EXPECTED_BITS[i]).length;
|
const correctedDecodedBits = packetDecodedBits.filter((b, i) => i < decodedBitCount && b === EXPECTED_BITS[i]).length;
|
||||||
document.getElementById('received-data-error-percent').innerText = (
|
document.getElementById('received-data-error-percent').innerText = (
|
||||||
Math.floor((1 - (correctEncodedBits / encodedBitCount)) * 1000) * 0.1
|
Math.floor((1 - (correctEncodedBits / encodedBitCount)) * 1000) * 0.1
|
||||||
@@ -635,7 +643,7 @@ const bitExpectorReducer = expected => (all, bit, i) => {
|
|||||||
} else if(expected[i] !== bit) {
|
} else if(expected[i] !== bit) {
|
||||||
all += '<span class="bit-wrong">';
|
all += '<span class="bit-wrong">';
|
||||||
}
|
}
|
||||||
all += bit;
|
all += bit.toString();
|
||||||
if(i >= expected.length || expected[i] !== bit) {
|
if(i >= expected.length || expected[i] !== bit) {
|
||||||
all += '</span>';
|
all += '</span>';
|
||||||
}
|
}
|
||||||
@@ -665,7 +673,7 @@ function resetGraphData() {
|
|||||||
bitStart.length = 0;
|
bitStart.length = 0;
|
||||||
}
|
}
|
||||||
function truncateGraphData() {
|
function truncateGraphData() {
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
let length = frequencyOverTime.length;
|
let length = frequencyOverTime.length;
|
||||||
while(length !== 0) {
|
while(length !== 0) {
|
||||||
@@ -713,7 +721,7 @@ function handleListeningCheckbox(e) {
|
|||||||
microphoneNode = audioContext.createMediaStreamSource(stream);
|
microphoneNode = audioContext.createMediaStreamSource(stream);
|
||||||
analyser = audioContext.createAnalyser();
|
analyser = audioContext.createAnalyser();
|
||||||
analyser.smoothingTimeConstant = SMOOTHING_TIME_CONSTANT;
|
analyser.smoothingTimeConstant = SMOOTHING_TIME_CONSTANT;
|
||||||
analyser.fftSize = 2 ** FFT_POWER;
|
analyser.fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
microphoneNode.connect(analyser);
|
microphoneNode.connect(analyser);
|
||||||
resumeGraph();
|
resumeGraph();
|
||||||
}
|
}
|
||||||
@@ -765,7 +773,7 @@ let lastSegmentIndex = 0;
|
|||||||
|
|
||||||
function canHear(hz, {frequencies, length}) {
|
function canHear(hz, {frequencies, length}) {
|
||||||
var i = Math.round(hz / length);
|
var i = Math.round(hz / length);
|
||||||
return frequencies[i] > FREQUENCY_THRESHOLD;
|
return frequencies[i] > AMPLITUDE_THRESHOLD;
|
||||||
}
|
}
|
||||||
function amplitude(hz, {frequencies, length}) {
|
function amplitude(hz, {frequencies, length}) {
|
||||||
var i = Math.round(hz / length);
|
var i = Math.round(hz / length);
|
||||||
@@ -773,15 +781,6 @@ function amplitude(hz, {frequencies, length}) {
|
|||||||
}
|
}
|
||||||
const sum = (total, value) => total + value;
|
const sum = (total, value) => total + value;
|
||||||
|
|
||||||
// function evaluateBit(highBits, lowBits) {
|
|
||||||
// let highCount = highBits.reduce(
|
|
||||||
// (count, highAmplitude, i) =>
|
|
||||||
// count += highAmplitude > lowBits[i] ? 1 : 0
|
|
||||||
// , 0
|
|
||||||
// );
|
|
||||||
// return highCount >= (highBits.length / 2) ? '1' : '0';
|
|
||||||
// }
|
|
||||||
|
|
||||||
function avgLabel(array) {
|
function avgLabel(array) {
|
||||||
const values = array.filter(v => v > 0);
|
const values = array.filter(v => v > 0);
|
||||||
if(values.length === 0) return 'N/A';
|
if(values.length === 0) return 'N/A';
|
||||||
@@ -790,7 +789,7 @@ function avgLabel(array) {
|
|||||||
function drawBitDurationLines(ctx, color) {
|
function drawBitDurationLines(ctx, color) {
|
||||||
const { width, height } = receivedGraph;
|
const { width, height } = receivedGraph;
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
|
|
||||||
const streamTimes = frequencyOverTime.filter(({
|
const streamTimes = frequencyOverTime.filter(({
|
||||||
streamStarted
|
streamStarted
|
||||||
@@ -808,7 +807,7 @@ function drawBitDurationLines(ctx, color) {
|
|||||||
|
|
||||||
ctx.strokeStyle = color;
|
ctx.strokeStyle = color;
|
||||||
streamTimes.forEach(({ streamStarted, streamEnded = newest}) => {
|
streamTimes.forEach(({ streamStarted, streamEnded = newest}) => {
|
||||||
for(let time = streamStarted; time < streamEnded; time += FREQUENCY_DURATION) {
|
for(let time = streamStarted; time < streamEnded; time += SEGMENT_DURATION) {
|
||||||
if(newest - time > duration) continue;
|
if(newest - time > duration) continue;
|
||||||
const x = ((newest - time) / duration) * width;
|
const x = ((newest - time) / duration) * width;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
@@ -828,7 +827,7 @@ function drawBitDurationLines(ctx, color) {
|
|||||||
function drawBitStart(ctx, color) {
|
function drawBitStart(ctx, color) {
|
||||||
const { width, height } = receivedGraph;
|
const { width, height } = receivedGraph;
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
ctx.strokeStyle = color;
|
ctx.strokeStyle = color;
|
||||||
for(let i = 0; i < bitStart.length; i++) {
|
for(let i = 0; i < bitStart.length; i++) {
|
||||||
if(!bitStart[i]) continue;
|
if(!bitStart[i]) continue;
|
||||||
@@ -849,11 +848,15 @@ function getPercentY(percent) {
|
|||||||
const { height } = receivedGraph;
|
const { height } = receivedGraph;
|
||||||
return (1 - percent) * height;
|
return (1 - percent) * height;
|
||||||
}
|
}
|
||||||
function drawFrequencyLineGraph(ctx, hz, color) {
|
function drawFrequencyLineGraph(ctx, hz, color, lineWidth, dashed) {
|
||||||
const { width, height } = receivedGraph;
|
const { width, height } = receivedGraph;
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
ctx.strokeStyle = color;
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
if(dashed) {
|
||||||
|
ctx.setLineDash([5, 5]);
|
||||||
|
}
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
for(let i = 0; i < frequencyOverTime.length; i++) {
|
for(let i = 0; i < frequencyOverTime.length; i++) {
|
||||||
const {frequencies, time, length} = frequencyOverTime[i];
|
const {frequencies, time, length} = frequencyOverTime[i];
|
||||||
@@ -864,6 +867,9 @@ function drawFrequencyLineGraph(ctx, hz, color) {
|
|||||||
if(i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
if(i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
|
||||||
}
|
}
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
if(dashed) {
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function drawFrequencyDots(ctx, hz, color) {
|
function drawFrequencyDots(ctx, hz, color) {
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
@@ -893,7 +899,7 @@ function getTimeX(time, newest) {
|
|||||||
return getTimePercent(time, newest) * receivedGraph.width;
|
return getTimePercent(time, newest) * receivedGraph.width;
|
||||||
}
|
}
|
||||||
function getTimePercent(time, newest) {
|
function getTimePercent(time, newest) {
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
if(newest - time > duration) return -1;
|
if(newest - time > duration) return -1;
|
||||||
return ((newest - time) / duration);
|
return ((newest - time) / duration);
|
||||||
}
|
}
|
||||||
@@ -905,7 +911,7 @@ function drawChannelData() {
|
|||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
const sampleRate = getAudioContext().sampleRate;
|
const sampleRate = getAudioContext().sampleRate;
|
||||||
const fftSize = 2 ** FFT_POWER;
|
const fftSize = 2 ** FFT_SIZE_POWER;
|
||||||
const frequencyResolution = sampleRate / fftSize;
|
const frequencyResolution = sampleRate / fftSize;
|
||||||
//const frequencyCount = (sampleRate/2) / frequencyResolution;
|
//const frequencyCount = (sampleRate/2) / frequencyResolution;
|
||||||
const channels = getChannels();
|
const channels = getChannels();
|
||||||
@@ -917,63 +923,103 @@ function drawChannelData() {
|
|||||||
const frequencySegments = Math.floor(nyquistFrequency / frequencyResolution);
|
const frequencySegments = Math.floor(nyquistFrequency / frequencyResolution);
|
||||||
|
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
const duration = SEGMENT_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH;
|
||||||
|
const overlays = [];
|
||||||
|
|
||||||
for(let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
for(let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
||||||
const [low, high] = channels[channelIndex];
|
const [low, high] = channels[channelIndex];
|
||||||
let top = channelHeight * channelIndex;
|
let top = channelHeight * channelIndex;
|
||||||
|
|
||||||
ctx.fillStyle = channelIndex % 2 === 0 ? 'black' : 'white';
|
// ctx.fillStyle = channelIndex % 2 === 0 ? 'black' : 'white';
|
||||||
// ctx.fillRect(0, top, width, channelHeight);
|
// ctx.fillRect(0, top, width, channelHeight);
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
ctx.strokeStyle = 'blue';
|
// ctx.strokeStyle = 'blue';
|
||||||
for(let i = 0; i < frequencyOverTime.length; i++) {
|
|
||||||
const {frequencies, time, length, hasSignal, segmentIndex, pairs
|
|
||||||
} = frequencyOverTime[i];
|
|
||||||
if(!hasSignal) continue;
|
|
||||||
|
|
||||||
const x1 = getTimePercent(time, newest) * width;
|
const segmentDurationS = SEGMENT_DURATION;
|
||||||
if(x1 === -1) continue;
|
|
||||||
const x2 = i < frequencyOverTime.length - 1 ? getTimePercent(frequencyOverTime[i + 1].time, newest) * width : width;
|
|
||||||
const sampleWidth = x2 - x1;
|
|
||||||
// const amplitude = hzAmplitude(hz, length, frequencies);
|
|
||||||
ctx.beginPath();
|
|
||||||
|
|
||||||
// what should the bit be for this channel?
|
// Segments
|
||||||
|
const segmentCount = Math.ceil(EXPECTED_ENCODED_BITS.length / channelCount);
|
||||||
|
const lastStream = frequencyOverTime.find(fot => fot.hasSignal);
|
||||||
|
const streamStarted = lastStream?.streamStarted ?? newest;
|
||||||
|
const lastSegmentIndex = lastStream?.segmentIndex ?? segmentCount;
|
||||||
|
const oldest = newest - (segmentDurationS * MAX_BITS_DISPLAYED_ON_GRAPH);
|
||||||
|
|
||||||
|
// Show segments with wrong bits
|
||||||
|
for(let segmentIndex = 0; segmentIndex <= lastSegmentIndex; segmentIndex++) {
|
||||||
|
const segmentBits = GET_SEGMENT_BITS(streamStarted, segmentIndex);
|
||||||
|
if(channelIndex >= segmentBits.length) continue; // past received/heard bits
|
||||||
const bitIndex = (segmentIndex * channelCount) + channelIndex;
|
const bitIndex = (segmentIndex * channelCount) + channelIndex;
|
||||||
const expectedBit = packetBits[bitIndex];
|
if(bitIndex >= EXPECTED_ENCODED_BITS.length) continue; // past data stream
|
||||||
|
const segmentStart = streamStarted + (segmentIndex * segmentDurationS);
|
||||||
|
if(segmentStart > newest) break; // too far in the future
|
||||||
|
const segmentEnd = segmentStart + segmentDurationS;
|
||||||
|
if(segmentEnd < oldest) continue; // to far in the past
|
||||||
|
const endPercent = getTimePercent(segmentEnd, newest);
|
||||||
|
const startPercent = getTimePercent(segmentStart, newest);
|
||||||
|
const endX = (endPercent) * width;
|
||||||
|
const startX = (startPercent) * width;
|
||||||
|
const segmentWidth = startX - endX;
|
||||||
|
|
||||||
// what is the bit?
|
// evaluate received bit
|
||||||
const {
|
const actualBit = segmentBits[channelIndex];
|
||||||
channel,
|
// identify expected bit
|
||||||
lowHz,
|
const expectedBit = EXPECTED_ENCODED_BITS[bitIndex];
|
||||||
highHz,
|
|
||||||
isMissing,
|
|
||||||
isHigh
|
|
||||||
} = pairs[channelIndex];
|
|
||||||
const actualBit = isHigh ? 1 : 0;
|
|
||||||
|
|
||||||
|
// color red if received bit does not match expected bit
|
||||||
ctx.fillStyle = actualBit === expectedBit ? 'green' : 'red';
|
ctx.fillStyle = actualBit === expectedBit ? 'green' : 'red';
|
||||||
ctx.fillRect(x1, top, sampleWidth,channelHeight);
|
ctx.fillRect(endX, top, segmentWidth, channelHeight);
|
||||||
|
|
||||||
ctx.lineWidth = 0.5;
|
ctx.lineWidth = 0.5;
|
||||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
|
||||||
ctx.strokeRect(x1, top, sampleWidth, channelHeight);
|
ctx.strokeRect(endX, top, segmentWidth, channelHeight);
|
||||||
ctx.stroke();
|
|
||||||
|
// show bad value
|
||||||
|
// if(actualBit !== expectedBit) {
|
||||||
|
ctx.font = `${channelHeight}px Arial`;
|
||||||
|
const size = ctx.measureText(actualBit.toString());
|
||||||
|
const textHeight = size.actualBoundingBoxAscent + size.actualBoundingBoxDescent;
|
||||||
|
const centerChannel = top + (channelHeight / 2);
|
||||||
|
const textTop = centerChannel + (size.actualBoundingBoxAscent / 2);
|
||||||
|
|
||||||
|
overlays.push(() => {
|
||||||
|
ctx.strokeStyle = actualBit !== expectedBit ? 'black' : 'black';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeText(actualBit.toString(), endX + (segmentWidth/2) - (size.width / 2), textTop);
|
||||||
|
ctx.fillStyle = actualBit !== expectedBit ? 'white' : 'white';
|
||||||
|
ctx.fillText(actualBit.toString(), endX + (segmentWidth/2) - (size.width / 2), textTop);
|
||||||
|
})
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for(let channelIndex = 0; channelIndex < channelCount; channelIndex++) {
|
||||||
|
// const [low, high] = channels[channelIndex];
|
||||||
|
// let top = channelHeight * channelIndex;
|
||||||
|
if(channelIndex % 8 === 0) {
|
||||||
|
ctx.strokeStyle = 'black';
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, top-1);
|
||||||
|
ctx.lineTo(width, top-1);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
// channel number
|
// channel number
|
||||||
ctx.font = `${channelHeight}px Arial`;
|
ctx.font = `${channelHeight}px Arial`;
|
||||||
const size = ctx.measureText(channelIndex);
|
const size = ctx.measureText(channelIndex);
|
||||||
const textHeight = size.fontBoundingBoxAscent + size.fontBoundingBoxDescent;
|
const textHeight = size.fontBoundingBoxAscent + size.fontBoundingBoxDescent;
|
||||||
const textTop = top;//(top + (channelHeight / 2)) - (textHeight/2);
|
const textTop = top + (channelHeight / 2) + (size.actualBoundingBoxAscent / 2);
|
||||||
ctx.fillStyle = 'red';
|
// const textTop = top;//(top + (channelHeight / 2)) - (textHeight/2);
|
||||||
|
const hue = channelHue(channelIndex, channelCount);
|
||||||
|
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
||||||
|
|
||||||
ctx.fillText(channelIndex, 5, textTop);
|
ctx.fillText(channelIndex, 5, textTop);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
overlays.forEach(fn => fn());
|
||||||
|
|
||||||
}
|
}
|
||||||
function drawFrequencyData() {
|
function drawFrequencyData() {
|
||||||
@@ -988,7 +1034,7 @@ function drawFrequencyData() {
|
|||||||
ctx.fillStyle = 'black';
|
ctx.fillStyle = 'black';
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
const thresholdY = (1 - (FREQUENCY_THRESHOLD/MAX_DATA)) * height;
|
const thresholdY = (1 - (AMPLITUDE_THRESHOLD/MAX_DATA)) * height;
|
||||||
ctx.strokeStyle = 'grey';
|
ctx.strokeStyle = 'grey';
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(0, thresholdY);
|
ctx.moveTo(0, thresholdY);
|
||||||
@@ -998,18 +1044,18 @@ function drawFrequencyData() {
|
|||||||
drawBitStart(ctx, 'green');
|
drawBitStart(ctx, 'green');
|
||||||
const frequencies = getChannels();
|
const frequencies = getChannels();
|
||||||
frequencies.forEach(([low, high], i) => {
|
frequencies.forEach(([low, high], i) => {
|
||||||
if(i >= frequencies.length - 1) {
|
const hue = channelHue(i, frequencies.length);
|
||||||
drawFrequencyLineGraph(ctx, high, 'pink');
|
drawFrequencyLineGraph(ctx, high, `hsl(${hue}, 100%, 50%)`, 2, false);
|
||||||
drawFrequencyLineGraph(ctx, low, 'cyan');
|
drawFrequencyLineGraph(ctx, low, `hsl(${hue}, 100%, 25%)`, 1, true);
|
||||||
} else {
|
|
||||||
drawFrequencyLineGraph(ctx, high, 'rgba(255, 0, 0, .5)');
|
|
||||||
drawFrequencyLineGraph(ctx, low, 'rgba(0, 0, 255, .5)');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
requestAnimationFrame(drawFrequencyData);
|
requestAnimationFrame(drawFrequencyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function channelHue(channelId, channelCount) {
|
||||||
|
return Math.floor((channelId / channelCount) * 360);
|
||||||
|
}
|
||||||
|
|
||||||
function drawReceivedData() {
|
function drawReceivedData() {
|
||||||
const ctx = receivedGraph.getContext('2d');
|
const ctx = receivedGraph.getContext('2d');
|
||||||
const { width, height } = receivedGraph;
|
const { width, height } = receivedGraph;
|
||||||
|
|||||||
Reference in New Issue
Block a user