diff --git a/index.js b/index.js
index 4750b08..0d09465 100644
--- a/index.js
+++ b/index.js
@@ -15,7 +15,8 @@ var pauseTimeoutId;
var sampleIntervalId;
var TEXT_TO_SEND = "U";
-var MAX_BITS_DISPLAYED_ON_GRAPH = 115;
+var RANDOM_COUNT = 64;
+var MAX_BITS_DISPLAYED_ON_GRAPH = 58;
var SEGMENT_DURATION = 30;
var AMPLITUDE_THRESHOLD_PERCENT = .75;
var AMPLITUDE_THRESHOLD = 160;
@@ -27,6 +28,7 @@ var FREQUENCY_RESOLUTION_MULTIPLIER = 2;
var SMOOTHING_TIME_CONSTANT = 0;
var HAMMING_ERROR_CORRECTION = true;
+var LAST_STREAM_STARTED;
var SAMPLE_DELAY_MS = 1;
var frequencyOverTime = [];
var bitStart = [];
@@ -46,7 +48,7 @@ let packetDataByteCount = -1;
function handleWindowLoad() {
const printable = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>?";
- TEXT_TO_SEND = new Array(128).fill(0).map(() => printable[Math.floor(Math.random() * printable.length)]).join('');
+ TEXT_TO_SEND = new Array(RANDOM_COUNT).fill(0).map(() => printable[Math.floor(Math.random() * printable.length)]).join('');
// grab dom elements
sendButton = document.getElementById('send-button');
@@ -209,7 +211,7 @@ function drawChannels() {
const nyquistFrequency = audioContext.sampleRate / 2;
const frequencySegments = Math.floor(nyquistFrequency / frequencyResolution);
- for(let i = 0; i < channelCount; i++) {//xxx
+ for(let i = 0; i < channelCount; i++) {
const [low, high] = channels[i];
let top = channelHeight * i;
ctx.fillStyle = 'black';
@@ -365,10 +367,15 @@ function sendBits(bits) {
const channel = i % channelCount;
const segment = Math.floor(i / channelCount);
var offset = ((segment * SEGMENT_DURATION)/1000);
+ var offset2 = (((segment+1) * SEGMENT_DURATION)/1000) - (1/100000);
oscillators[channel].frequency.setValueAtTime(
channels[channel][isHigh ? 1 : 0],
currentTime + offset
);
+ oscillators[channel].frequency.setValueAtTime(
+ channels[channel][isHigh ? 1 : 0],
+ currentTime + offset2
+ );
}
// start sending our signal
@@ -379,6 +386,7 @@ function sendBits(bits) {
const channel = i % channelCount;
const segment = Math.floor(i / channelCount);
const offset = ((segment * SEGMENT_DURATION) / 1000);
+ oscillators[channel].frequency.setValueAtTime(0, currentTime + offset);
oscillators[channel].stop(currentTime + offset);
}
@@ -467,6 +475,7 @@ function collectSample() {
} else {
// new bit stream
data.streamStarted = time;
+ LAST_STREAM_STARTED = time;
// clear last packet
packetReceivedBits.length = 0;
packetDataByteCount = 0;
@@ -554,9 +563,6 @@ function processSegmentReceived(streamStarted, segmentIndex) {
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;
@@ -660,14 +666,21 @@ const textExpectorReducer = expected => (all, bit, i, bits) => {
const char = String.fromCharCode(ascii);
const charIndex = Math.floor((i - PACKET_SIZE_BITS) / 8);
if(char !== expected[charIndex]) {
- all += '' + char + '';
+ all += '' + htmlEncode(printable(char)) + '';
} else {
- all += char;
+ all += htmlEncode(printable(char));
}
}
return all;
}
-
+function printable(text) {
+ return text.replace(/[\x00-\x1f\x7f-\x9f]/g, '.');
+}
+function htmlEncode(text) {
+ const element = document.createElement('div');
+ element.textContent = text;
+ return element.innerHTML;
+}
function resetGraphData() {
frequencyOverTime.length = 0;
bitStart.length = 0;
@@ -786,6 +799,75 @@ function avgLabel(array) {
if(values.length === 0) return 'N/A';
return (values.reduce((t, v) => t + v, 0) / values.length).toFixed(2)
}
+function drawSegmentIndexes(ctx) {
+ if(!LAST_STREAM_STARTED) return;
+ const { width, height } = receivedGraph;
+ const fot = frequencyOverTime.find(fot => fot.streamStarted === LAST_STREAM_STARTED);
+ const newest = frequencyOverTime[0].time;
+ const channelCount = frequencyOverTime[0].pairs.length;
+ let {
+ streamStarted,
+ streamEnded = newest
+ } = fot ?? {
+ streamStarted: LAST_STREAM_STARTED,
+ streamEnded: newest
+ };
+ if(streamEnded === -1) streamEnded = newest;
+ let segmentIndex = 0;
+ ctx.fontSize = 24;
+
+ // determine max segments to prevent infinite loop later
+ let maxBits = ((1 << PACKET_SIZE_BITS) * 8) + PACKET_SIZE_BITS;
+ if(HAMMING_ERROR_CORRECTION) maxBits *= 7/4;
+ let maxSegments = Math.ceil(maxBits / channelCount);
+
+ // loop through each index
+ while(true) {
+ let segmentStart = streamStarted + (segmentIndex * SEGMENT_DURATION);
+ // if(segmentStart > streamEnded) break; // stream ended
+
+ let segmentEnd = segmentStart + SEGMENT_DURATION;
+ // find where the index is on the graph
+ const rightX = getTimeX(segmentStart, newest);
+ const leftX = getTimeX(segmentEnd, newest);
+ const segmentWidth = rightX - leftX;
+ if(leftX > width) continue; // too far in past
+ if(rightX < 0) break; // in the future
+
+ // Draw segment index
+ let text = segmentIndex.toString();
+ let size = ctx.measureText(text);
+ let textX = leftX + (segmentWidth / 2) - (size.width / 2);
+ ctx.strokeStyle = 'black';
+ ctx.lineWidth = 2;
+ ctx.textBaseline = 'bottom';
+ ctx.strokeText(text, textX, height);
+ ctx.fillStyle = segmentStart > streamEnded ? 'grey' : 'white';
+ ctx.fillText(text, textX, height);
+
+ // draw sample count
+ const sampleCount = frequencyOverTime
+ .filter(fot =>
+ fot.streamStarted === streamStarted &&
+ fot.segmentIndex === segmentIndex
+ )
+ .length;
+
+ text = sampleCount.toString();
+ size = ctx.measureText(text);
+ textX = leftX + (segmentWidth / 2) - (size.width / 2);
+ ctx.strokeStyle = 'black';
+ ctx.lineWidth = 2;
+ ctx.textBaseline = 'top';
+ ctx.strokeText(text, textX, 5);
+ ctx.fillStyle = 'white';
+ ctx.fillText(text, textX, 5);
+
+ segmentIndex++;
+ // break out of potential infinite loop
+ if(segmentIndex >= maxSegments) break;
+ }
+}
function drawBitDurationLines(ctx, color) {
const { width, height } = receivedGraph;
const newest = frequencyOverTime[0].time;
@@ -1048,6 +1130,7 @@ function drawFrequencyData() {
drawFrequencyLineGraph(ctx, high, `hsl(${hue}, 100%, 50%)`, 2, false);
drawFrequencyLineGraph(ctx, low, `hsl(${hue}, 100%, 25%)`, 1, true);
});
+ drawSegmentIndexes(ctx);
requestAnimationFrame(drawFrequencyData);
}