diff --git a/Panels/FrequencyGraphPanel.js b/Panels/FrequencyGraphPanel.js index a930b5d..cb6f220 100644 --- a/Panels/FrequencyGraphPanel.js +++ b/Panels/FrequencyGraphPanel.js @@ -18,7 +18,7 @@ class FrequencyGraphPanel extends BasePanel { }; setDurationMilliseconds = (millseconds) => { this.duration = millseconds; - this.draw(false); + if(!this.isRunning()) this.draw(); } setSignalStart = milliseconds => { this.signalStart = milliseconds; @@ -30,12 +30,14 @@ class FrequencyGraphPanel extends BasePanel { setAmplitudeThreshold = value => { this.amplitudeThreshold = value; + if(!this.isRunning()) this.draw(); } setSampleRate = (value) => { this.sampleRate = value; } setFskPairs = fskPairs => { this.fskPairs = fskPairs; + if(!this.isRunning()) this.draw(); } setAnalyser = (analyser) => { this.analyser = analyser; @@ -185,7 +187,7 @@ class FrequencyGraphPanel extends BasePanel { } // sample counts - this.drawSampleCount(ctx, width, height, time, end, leftX, samplePeriodWidth); + this.drawSampleCount(ctx, time, end, leftX, samplePeriodWidth); } this.drawSignalStart(ctx, width, height, now); @@ -195,7 +197,7 @@ class FrequencyGraphPanel extends BasePanel { this.animationFrameId = requestAnimationFrame(this.draw); } } - drawSampleCount = (ctx, width, height, start, end, leftX, samplePeriodWidth) => { + drawSampleCount = (ctx, start, end, leftX, samplePeriodWidth) => { const count = this.samples.filter(sample => { return sample.time >= start && sample.time < end; }).length; diff --git a/Panels/GraphConfigurationPanel.js b/Panels/GraphConfigurationPanel.js index 69a1dd7..888df17 100644 --- a/Panels/GraphConfigurationPanel.js +++ b/Panels/GraphConfigurationPanel.js @@ -7,7 +7,7 @@ class GraphConfigurationPanel extends BasePanel { {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.addInputNumber('duration', 1, {min: 0.03, max: 10, step: 0.001, eventName: 'durationChange'}); this.addText('s'); this.closeField(); }; diff --git a/index.html b/index.html index bab1d9e..1fa21de 100644 --- a/index.html +++ b/index.html @@ -11,14 +11,9 @@

Data Over Audio

-

Frequency Graph

+

Channel Graph

-
-
-
- Max Segments Displayed:
+
diff --git a/index.js b/index.js index 04f90ba..9d387ef 100644 --- a/index.js +++ b/index.js @@ -21,8 +21,6 @@ var microphoneStream; var microphoneNode; var analyser; var sentDataTextArea; -var receivedGraph; -var receivedData = []; const MAXIMUM_PACKETIZATION_SIZE_BITS = 16; const CRC_BIT_COUNT = 8; @@ -34,8 +32,6 @@ let SENT_TRANSFER_BITS = []; // bits sent in the transfer let EXCLUDED_CHANNELS = []; -var MAX_BITS_DISPLAYED_ON_GRAPH = 79; - const ERROR_CORRECTION_BLOCK_SIZE = 7; const ERROR_CORRECTION_DATA_SIZE = 4; let CHANNEL_OVER = -1; @@ -182,17 +178,12 @@ function handleWindowLoad() { StreamManager.addEventListener('change', handleStreamManagerChange); // grab dom elements - receivedGraph = document.getElementById('received-graph'); sentDataTextArea = document.getElementById('sent-data'); const receivedChannelGraph = document.getElementById('received-channel-graph'); receivedChannelGraph.addEventListener('mouseover', handleReceivedChannelGraphMouseover); receivedChannelGraph.addEventListener('mouseout', handleReceivedChannelGraphMouseout); receivedChannelGraph.addEventListener('mousemove', handleReceivedChannelGraphMousemove); receivedChannelGraph.addEventListener('click', handleReceivedChannelGraphClick); - 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); - }) document.getElementById('audio-context-sample-rate').innerText = getAudioContext().sampleRate.toLocaleString(); // wire up events configurationChanged(); @@ -910,206 +901,6 @@ function handleChangeListening({checked}) { } } } -function drawSegmentIndexes(ctx, width, height) { - // Do/did we have a stream? - if(!RECEIVED_STREAM_START_MS) return; - const latest = SAMPLES[0].time; - - // will any of the stream appear? - const segmentCount = PacketUtils.getPacketSegmentCount(); - const transferDuration = parseDataTransferDurationMilliseconds(); - const lastStreamEnded = RECEIVED_STREAM_START_MS + transferDuration; - - const graphDuration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; - const graphEarliest = latest - graphDuration; - // ended too long ago? - if(lastStreamEnded < graphEarliest) return; - - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; - - const latestSegmentEnded = Math.min(latest, lastStreamEnded); - - for(let time = latestSegmentEnded; time > graphEarliest; time -= signalPanel.getSegmentDuration()) { - // too far back? - if(time < RECEIVED_STREAM_START_MS) break; - - const transferSegmentIndex = PacketUtils.getTranserSegmentIndex(RECEIVED_STREAM_START_MS, time); - const packetIndex = PacketUtils.getPacketIndex(RECEIVED_STREAM_START_MS, time); - const packetSegmentIndex = PacketUtils.getPacketSegmentIndex(RECEIVED_STREAM_START_MS, time); - const packetStarted = PacketUtils.getPacketStartMilliseconds(RECEIVED_STREAM_START_MS, packetIndex); - const segmentStart = PacketUtils.getPacketSegmentStartMilliseconds(RECEIVED_STREAM_START_MS, packetIndex, packetSegmentIndex); - const segmentEnd = PacketUtils.getPacketSegmentEndMilliseconds(RECEIVED_STREAM_START_MS, packetIndex, packetSegmentIndex); - - // where is the segments left x coordinate? - const leftX = ((latest - segmentEnd) / graphDuration) * width; - - // Draw segment index - ctx.fontSize = '24px'; - if(segmentStart < lastStreamEnded) { - let text = packetSegmentIndex.toString(); - let size = ctx.measureText(text); - let textX = leftX + (segmentWidth / 2) - (size.width / 2); - ctx.strokeStyle = 'black'; - ctx.lineWidth = 2; - ctx.textBaseline = 'bottom'; - let textY = transferSegmentIndex % 2 === 0 ? height : height - 12; - ctx.strokeText(text, textX, textY); - ctx.fillStyle = 'white'; - ctx.fillText(text, textX, textY); - } - - // draw sample count - const sampleCount = SAMPLES - .filter(fot => - fot.streamStarted === packetStarted && - fot.segmentIndex === packetSegmentIndex && - fot.packetIndex === packetIndex - ) - .length; - // if(sampleCount === 0) { - // console.log({ - // packetStarted, - // packetSegmentIndex, - // packetIndex, - // startTimes: SAMPLES.reduce((all, fot) => all.includes(fot.streamStarted) ? all : [...all, fot.streamStarted], []) - // }) - // } - - let text = sampleCount.toString(); - let size = ctx.measureText(text); - let textX = leftX + (segmentWidth / 2) - (size.width / 2); - let textY = transferSegmentIndex % 2 === 0 ? 5 : 17; - ctx.strokeStyle = 'black'; - ctx.lineWidth = 2; - ctx.textBaseline = 'top'; - ctx.strokeText(text, textX, textY); - if(sampleCount === 0) ctx.fillStyle = 'red'; - else if(sampleCount < 3) ctx.fillStyle = 'yellow'; - else ctx.fillStyle = 'white'; - ctx.fillText(text, textX, textY); - } -} -function drawBitDurationLines(ctx, color) { - const { width, height } = receivedGraph; - const newest = SAMPLES[0].time; - const duration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; - - const streamTimes = SAMPLES.filter(({ - streamStarted - }) => { - return streamStarted !== -1 - }).reduce((unique, { - streamStarted, - streamEnded = newest - }) => { - if(unique.every(u => u.streamStarted != streamStarted)) { - unique.push({streamStarted, streamEnded}) - } - return unique; - }, []); - - ctx.strokeStyle = color; - streamTimes.forEach(({ streamStarted, streamEnded = newest}) => { - for(let time = streamStarted; time < streamEnded; time += signalPanel.getSegmentDuration()) { - if(newest - time > duration) continue; - const x = ((newest - time) / duration) * width; - ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, height); - ctx.stroke(); - } - // write end as well - const x = ((newest - streamEnded) / duration) * width; - ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, height); - ctx.stroke(); -}); -} - -function drawBitStart(ctx, color) { - const { width, height } = receivedGraph; - const newest = SAMPLES[0].time; - const duration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; - ctx.strokeStyle = color; - for(let i = 0; i < bitStart.length; i++) { - if(!bitStart[i]) continue; - const {time} = SAMPLES[i]; - if(newest - time > duration) continue; - const x = ((newest - time) / duration) * width; - ctx.beginPath(); - ctx.moveTo(x, 0); - ctx.lineTo(x, height); - ctx.stroke(); - } -} -function getPercentY(percent) { - const { height } = receivedGraph; - return (1 - percent) * height; -} -function drawFrequencyLineGraph(ctx, channel, highLowIndex, color, lineWidth, dashed) { - const { width, height } = receivedGraph; - const newest = SAMPLES[0].time; - const duration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; - const isSelected = channel === CHANNEL_SELECTED; - const isOver = channel === CHANNEL_OVER; - if(dashed) { - ctx.setLineDash([5, 5]); - } - ctx.beginPath(); - for(let i = 0; i < SAMPLES.length; i++) { - const {pairs, time} = SAMPLES[i]; - const x = getTimeX(time, newest); - if(x === -1) continue; - if(channel >= pairs.length) continue; - const amplitude = pairs[channel][highLowIndex]; - const y = getPercentY(amplitude / 300); - if(i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); - } - if(isSelected || isOver) { - ctx.lineWidth = lineWidth + 5; - ctx.strokeStyle = 'white'; - ctx.stroke(); - } - ctx.strokeStyle = color; - ctx.lineWidth = lineWidth; - ctx.stroke(); - if(dashed) { - ctx.setLineDash([]); - } -} -function drawFrequencyDots(ctx, channel, highLowIndex, color) { - const newest = SAMPLES[0].time; - const radius = 2; - const border = 0.5; - ctx.fillStyle = color; - ctx.strokeStyle = 'white'; - ctx.lineWidth = border; - const fullCircle = 2 * Math.PI; - for(let i = 0; i < SAMPLES.length; i++) { - const {pairs, time} = SAMPLES[i]; - const x = getTimeX(time, newest); - if(x === -1) continue; - const amplitude = pairs[channel][highLowIndex]; - const y = getPercentY(amplitude / 300); - - ctx.beginPath(); - ctx.arc(x, y, radius, 0, fullCircle); - ctx.fill(); - - ctx.beginPath(); - ctx.arc(x, y, radius + border, 0, fullCircle); - ctx.stroke(); - } -} -function getTimeX(time, newest) { - return getTimePercent(time, newest) * receivedGraph.width; -} -function getTimePercent(time, newest) { - const duration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; - if(newest - time > duration) return -1; - return ((newest - time) / duration); -} function drawChannelData() { // Do/did we have a stream? @@ -1122,7 +913,7 @@ function drawChannelData() { const packetDuration = PacketUtils.getPacketDurationMilliseconds(); const lastStreamEnded = RECEIVED_STREAM_START_MS + packetDuration; - const graphDuration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; + const graphDuration = graphConfigurationPanel.getDurationMilliseconds(); const graphEarliest = latest - graphDuration; // ended too long ago? if(lastStreamEnded < graphEarliest) return; @@ -1225,7 +1016,7 @@ function drawSegmentBackground( width, height ) { - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; + const segmentWidth = width / (graphConfigurationPanel.getDurationMilliseconds() / signalPanel.getSegmentDuration()); const hue = 120; let luminance = segmentIndex % 2 === 0 ? 30 : 25; @@ -1246,7 +1037,7 @@ function drawChannelSegmentForeground( expectedBit ) { const channelHeight = height / channelCount; - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; + const segmentWidth = width / (graphConfigurationPanel.getDurationMilliseconds() / signalPanel.getSegmentDuration()); let fontHeight = Math.min(24, channelHeight, segmentWidth); let top = channelHeight * channelIndex; ctx.font = `${fontHeight}px Arial`; @@ -1286,7 +1077,7 @@ function drawChannelSegmentBackground( if(isSelectedOrOver) luminance += 15; const channelHeight = height / channelCount; - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; + const segmentWidth = width / (graphConfigurationPanel.getDurationMilliseconds() / signalPanel.getSegmentDuration()); let top = channelHeight * channelIndex; ctx.fillStyle = `hsl(${hue}, 100%, ${luminance}%)`; ctx.fillRect(endX, top, segmentWidth, channelHeight); @@ -1323,7 +1114,7 @@ function drawChannelNumbers(ctx, channelCount, width, height) { const offset = 0; const channels = availableFskPairsPanel.getSelectedFskPairs(); const channelHeight = height / channelCount; - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; + const segmentWidth = width / (graphConfigurationPanel.getDurationMilliseconds() / signalPanel.getSegmentDuration()); let fontHeight = Math.min(24, channelHeight, segmentWidth); ctx.font = `${fontHeight}px Arial`; ctx.textBaseline = 'middle'; @@ -1356,80 +1147,13 @@ function drawFrequencyData(forcedDraw) { return; } drawChannelData(); - const ctx = receivedGraph.getContext('2d'); - const { width, height } = receivedGraph; - ctx.fillStyle = 'black'; - ctx.fillRect(0, 0, width, height); - - const thresholdY = (1 - ((signalPanel.getAmplitudeThreshold() * 255)/300)) * height; - ctx.strokeStyle = 'grey'; - ctx.beginPath(); - ctx.moveTo(0, thresholdY); - ctx.lineTo(width, thresholdY); - ctx.stroke(); - drawBitDurationLines(ctx, 'rgba(255, 255, 0, .25)'); - drawBitStart(ctx, 'green'); - const frequencies = availableFskPairsPanel.getSelectedFskPairs(); - const high = 1; - const low = 0 - const isSelectedOrOver = CHANNEL_OVER !== -1 || CHANNEL_SELECTED !== -1; - const highLuminance = isSelectedOrOver ? 25 : 50; - const lowLuminance = isSelectedOrOver ? 12 : 25; - frequencies.forEach((v, channel) => { - // const hue = channelHue(channel, frequencies.length); - const lowHue = hzHue(v[0]); - const highHue = hzHue(v[1]); - drawFrequencyLineGraph(ctx, channel, high, `hsl(${highHue}, 100%, ${highLuminance}%)`, 2, false); - drawFrequencyLineGraph(ctx, channel, low, `hsl(${lowHue}, 100%, ${lowLuminance}%)`, 1, true); - }); - if(CHANNEL_OVER !== -1) { - // const hue = channelHue(CHANNEL_OVER, frequencies.length); - const lowHue = hzHue(frequencies[CHANNEL_OVER][0]); - const highHue = hzHue(frequencies[CHANNEL_OVER][1]); - drawFrequencyLineGraph(ctx, CHANNEL_OVER, high, `hsl(${highHue}, 100%, 50%)`, 2, false); - drawFrequencyLineGraph(ctx, CHANNEL_OVER, low, `hsl(${lowHue}, 100%, 25%)`, 1, true); - } else if(CHANNEL_SELECTED !== -1) { - const lowHue = hzHue(frequencies[CHANNEL_SELECTED][0]); - const highHue = hzHue(frequencies[CHANNEL_SELECTED][1]); - // const hue = channelHue(CHANNEL_SELECTED, frequencies.length); - drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, high, `hsl(${highHue}, 100%, 50%)`, 2, false); - drawFrequencyLineGraph(ctx, CHANNEL_SELECTED, low, `hsl(${lowHue}, 100%, 25%)`, 1, true); - } - - drawSegmentIndexes(ctx, width, height); - requestAnimationFrame(drawFrequencyData); } -function channelHue(channelId, channelCount) { - return Math.floor((channelId / channelCount) * 360); -} function hzHue(hz) { return Math.floor((hz / 20000) * 360); } -function drawReceivedData() { - const ctx = receivedGraph.getContext('2d'); - const { width, height } = receivedGraph; - const segmentWidth = (1 / MAX_BITS_DISPLAYED_ON_GRAPH) * width; - ctx.clearRect(0, 0, width, height); - const sorted = receivedData.slice().sort((a, b) => a - b); - const min = sorted[0]; - const max = sorted[sorted.length - 1]; - const range = max - min; - ctx.beginPath(); - for(let i = 0; i < MAX_BITS_DISPLAYED_ON_GRAPH && i < receivedData.length; i++) { - const value = receivedData[i]; - const y = (1-(value / range)) * height; - if(i === 0) { - ctx.moveTo(0, y); - } else { - ctx.lineTo(segmentWidth * i, y) - } - } - ctx.stroke(); -} - function handleReceivedChannelGraphMouseover(e) { const {channelIndex, segmentIndex} = getChannelAndSegment(e); CHANNEL_OVER = channelIndex; @@ -1583,7 +1307,7 @@ function getChannelAndSegment(e) { // will any of the stream appear? const packetDuration = PacketUtils.getPacketDurationMilliseconds(); const lastStreamEnded = RECEIVED_STREAM_START_MS + packetDuration; - const graphDuration = signalPanel.getSegmentDuration() * MAX_BITS_DISPLAYED_ON_GRAPH; + const graphDuration = graphConfigurationPanel.getDurationMilliseconds(); const graphEarliest = latest - graphDuration; // ended too long ago? if(lastStreamEnded < graphEarliest) { @@ -1593,7 +1317,7 @@ function getChannelAndSegment(e) { }; } - const segmentWidth = width / MAX_BITS_DISPLAYED_ON_GRAPH; + const segmentWidth = width / (graphConfigurationPanel.getDurationMilliseconds() / signalPanel.getSegmentDuration()); const latestSegmentEnded = Math.min(latest, lastStreamEnded);