mirror of https://github.com/nealey/vail.git
Add rx chart, take UI code out of keyer
This commit is contained in:
parent
01ed64ad2d
commit
ce1579a6b8
|
@ -4,17 +4,17 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A chart of historical values.
|
* A chart of historical values.
|
||||||
*
|
*
|
||||||
* Curently this assumes all values are between 0 and 1,
|
* Curently this assumes all values are between 0 and 1,
|
||||||
* since that's all I need.
|
* since that's all I need.
|
||||||
*/
|
*/
|
||||||
class HistoryChart {
|
class HistoryChart {
|
||||||
/**
|
/**
|
||||||
* @param {Element} canvas Canvas element to draw on
|
* @param {Element} canvas Canvas element to draw on
|
||||||
* @param {string} strokeStyle strokeStyle to draw in
|
* @param {string} style style to draw in; falls back to the `data-style` attribute
|
||||||
* @param {Duration} duration Time to display history for
|
* @param {Duration} duration Time to display history for
|
||||||
*/
|
*/
|
||||||
constructor(canvas, strokeStyle="black", duration=20*Second) {
|
constructor(canvas, style=null, duration=20*Second) {
|
||||||
this.canvas = canvas
|
this.canvas = canvas
|
||||||
this.ctx = canvas.getContext("2d")
|
this.ctx = canvas.getContext("2d")
|
||||||
this.duration = duration
|
this.duration = duration
|
||||||
|
@ -23,13 +23,12 @@ class HistoryChart {
|
||||||
|
|
||||||
// One canvas pixel = 20ms
|
// One canvas pixel = 20ms
|
||||||
canvas.width = duration / (20 * Millisecond)
|
canvas.width = duration / (20 * Millisecond)
|
||||||
|
|
||||||
// Set origin to lower-left corner
|
// Set origin to lower-left corner
|
||||||
this.ctx.scale(1, -1)
|
this.ctx.scale(1, -1)
|
||||||
this.ctx.translate(0, -canvas.height)
|
this.ctx.translate(0, -canvas.height)
|
||||||
|
|
||||||
this.ctx.strokeStyle = strokeStyle
|
this.ctx.fillStyle = style || canvas.dataset.color || "black"
|
||||||
this.ctx.fillStyle = strokeStyle
|
|
||||||
this.ctx.lineWdith = 2
|
this.ctx.lineWdith = 2
|
||||||
|
|
||||||
this.running=true
|
this.running=true
|
||||||
|
@ -37,21 +36,34 @@ class HistoryChart {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an event point at a given time.
|
* Set value to something at the current time.
|
||||||
*
|
*
|
||||||
* These must always be added in time order.
|
|
||||||
*
|
|
||||||
* This also cleans up the event list,
|
* This also cleans up the event list,
|
||||||
* purging anything that is too old to be displayed.
|
* purging anything that is too old to be displayed.
|
||||||
*
|
*
|
||||||
* @param {Number} when Time the event happened
|
|
||||||
* @param {Number} value Value for the event
|
* @param {Number} value Value for the event
|
||||||
*/
|
*/
|
||||||
Add(when, value) {
|
Set(value) {
|
||||||
let now = Date.now()
|
this.SetAt(value)
|
||||||
let earliest = now - this.duration
|
}
|
||||||
|
|
||||||
this.data.push([when, value])
|
/**
|
||||||
|
* Set value to something at the provided time.
|
||||||
|
*
|
||||||
|
* This also cleans up the event list,
|
||||||
|
* purging anything that is too old to be displayed.
|
||||||
|
*
|
||||||
|
* @param {Number} value Value for the event
|
||||||
|
* @param {Number} when Time to set the value
|
||||||
|
*/
|
||||||
|
SetAt(value, when=null) {
|
||||||
|
let now = Date.now()
|
||||||
|
if (!when) when=now
|
||||||
|
|
||||||
|
this.data.push([now, value])
|
||||||
|
this.data.sort()
|
||||||
|
|
||||||
|
let earliest = now - this.duration
|
||||||
// Leave one old datapoint so we know the value when the window opens
|
// Leave one old datapoint so we know the value when the window opens
|
||||||
while ((this.data.length > 1) && (this.data[1][0] < earliest)) {
|
while ((this.data.length > 1) && (this.data[1][0] < earliest)) {
|
||||||
this.data.shift()
|
this.data.shift()
|
||||||
|
@ -93,4 +105,19 @@ class HistoryChart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {HistoryChart}
|
/**
|
||||||
|
* Return a new chart based on an HTML selector
|
||||||
|
*
|
||||||
|
* @param selector HTML selector
|
||||||
|
* @param style fill style
|
||||||
|
* @param duration duration of chart window
|
||||||
|
* @returns new chart, or null if it couldn't find a canvas
|
||||||
|
*/
|
||||||
|
function FromSelector(selector, style, duration=20*Second) {
|
||||||
|
let canvas = document.querySelector(selector)
|
||||||
|
if (canvas) {
|
||||||
|
return new HistoryChart(canvas, style, duration)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
export {HistoryChart, FromSelector}
|
||||||
|
|
|
@ -95,9 +95,10 @@
|
||||||
<output id="note"></output>
|
<output id="note"></output>
|
||||||
|
|
||||||
<div id="charts">
|
<div id="charts">
|
||||||
<canvas class="chart" id="txChart"></canvas>
|
<canvas class="chart" id="rxChart" data-color="teal"></canvas>
|
||||||
<canvas class="chart" id="ditChart"></canvas>
|
<canvas class="chart" id="txChart" data-color="maroon"></canvas>
|
||||||
<canvas class="chart" id="dahChart"></canvas>
|
<canvas class="chart" id="ditChart" data-color="olive"></canvas>
|
||||||
|
<canvas class="chart" id="dahChart" data-color="purple"></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
|
<div class="mdl-tabs mdl-js-tabs mdl-js-ripple-effect">
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import * as Chart from "./chart.mjs"
|
|
||||||
|
|
||||||
/** Silent period between words */
|
/** Silent period between words */
|
||||||
const PAUSE_WORD = -7
|
const PAUSE_WORD = -7
|
||||||
/** Silent period between letters */
|
/** Silent period between letters */
|
||||||
|
@ -188,25 +186,6 @@ class Keyer {
|
||||||
return this.last
|
return this.last
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up various charts by providing canvases for them.
|
|
||||||
*
|
|
||||||
* @param {Element} txCanvas
|
|
||||||
* @param {Element} straightCanvas
|
|
||||||
* @param {Element} ditCanvas
|
|
||||||
* @param {Element} dahCanvas
|
|
||||||
*/
|
|
||||||
SetCanvas(txCanvas=null, straightCanvas=null, ditCanvas=null, dahCanvas=null) {
|
|
||||||
for (let c of [this.txChart, this.straightChart, this.ditChart, this.dahChart]) {
|
|
||||||
if (c) c.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.txChart = txCanvas?new Chart.HistoryChart(txCanvas, "red"):null
|
|
||||||
this.straightChart =straightCanvas?new Chart.HistoryChart(straightCanvas, "teal"):null
|
|
||||||
this.ditChart =ditCanvas?new Chart.HistoryChart(ditCanvas, "olive"):null
|
|
||||||
this.dahChart =dahCanvas?new Chart.HistoryChart(dahCanvas, "purple"):null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if we are currently playing out something
|
* Return true if we are currently playing out something
|
||||||
*/
|
*/
|
||||||
|
@ -351,9 +330,6 @@ class Keyer {
|
||||||
} else {
|
} else {
|
||||||
this.endTxFunc()
|
this.endTxFunc()
|
||||||
}
|
}
|
||||||
if (this.straightChart) {
|
|
||||||
this.straightChart.Add(Date.now(), down?1:0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,9 +346,6 @@ class Keyer {
|
||||||
this.Enqueue(DIT)
|
this.Enqueue(DIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.ditChart) {
|
|
||||||
this.ditChart.Add(Date.now(), down?1:0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -389,9 +362,6 @@ class Keyer {
|
||||||
this.Enqueue(DAH)
|
this.Enqueue(DAH)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.dahChart) {
|
|
||||||
this.dahChart.Add(Date.now(), down?1:0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
142
static/vail.mjs
142
static/vail.mjs
|
@ -2,6 +2,7 @@ import * as Keyer from "./keyer.mjs"
|
||||||
import * as Buzzer from "./buzzer.mjs"
|
import * as Buzzer from "./buzzer.mjs"
|
||||||
import * as Inputs from "./inputs.mjs"
|
import * as Inputs from "./inputs.mjs"
|
||||||
import * as Repeaters from "./repeaters.mjs"
|
import * as Repeaters from "./repeaters.mjs"
|
||||||
|
import * as Chart from "./chart.mjs"
|
||||||
|
|
||||||
const DefaultRepeater = "General"
|
const DefaultRepeater = "General"
|
||||||
const Millisecond = 1
|
const Millisecond = 1
|
||||||
|
@ -37,19 +38,11 @@ class VailClient {
|
||||||
this.lamp = new Buzzer.Lamp()
|
this.lamp = new Buzzer.Lamp()
|
||||||
this.buzzer = new Buzzer.ToneBuzzer()
|
this.buzzer = new Buzzer.ToneBuzzer()
|
||||||
this.keyer = new Keyer.Keyer(() => this.beginTx(), () => this.endTx())
|
this.keyer = new Keyer.Keyer(() => this.beginTx(), () => this.endTx())
|
||||||
this.roboKeyer = new Keyer.Keyer(
|
this.roboKeyer = new Keyer.Keyer(() => this.Buzz(), () => this.Silence())
|
||||||
() => {
|
|
||||||
this.buzzer.Buzz()
|
|
||||||
this.lamp.Buzz()
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.buzzer.Silence()
|
|
||||||
this.lamp.Silence()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set up various input methods
|
// Set up various input methods
|
||||||
this.inputs = Inputs.SetupAll(this.keyer)
|
// Send this as the keyer so we can intercept dit and dah events for charts
|
||||||
|
this.inputs = Inputs.SetupAll(this)
|
||||||
|
|
||||||
// VBand: Keep track of how the user wants the single key to behave
|
// VBand: Keep track of how the user wants the single key to behave
|
||||||
for (let e of document.querySelectorAll("[data-singlekey]")) {
|
for (let e of document.querySelectorAll("[data-singlekey]")) {
|
||||||
|
@ -88,7 +81,6 @@ class VailClient {
|
||||||
this.setTelegraphBuzzer(e.target.checked)
|
this.setTelegraphBuzzer(e.target.checked)
|
||||||
})
|
})
|
||||||
this.inputInit("#timing-chart", e => {
|
this.inputInit("#timing-chart", e => {
|
||||||
console.log("moo")
|
|
||||||
this.setTimingCharts(e.target.checked)
|
this.setTimingCharts(e.target.checked)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -104,6 +96,88 @@ class VailClient {
|
||||||
document.querySelector("#muted").classList.add("hidden")
|
document.querySelector("#muted").classList.add("hidden")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Straight key change (keyer shim)
|
||||||
|
*
|
||||||
|
* @param down If key has been depressed
|
||||||
|
*/
|
||||||
|
Straight(down) {
|
||||||
|
this.keyer.Straight(down)
|
||||||
|
if (this.straightChart) this.straightChart.Set(down?1:0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dit key change (keyer shim)
|
||||||
|
*
|
||||||
|
* @param down If the key has been depressed
|
||||||
|
*/
|
||||||
|
Dit(down) {
|
||||||
|
this.keyer.Dit(down)
|
||||||
|
if (this.ditChart) this.ditChart.Set(down?1:0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dah key change (keyer shim)
|
||||||
|
*
|
||||||
|
* @param down If the key has been depressed
|
||||||
|
*/
|
||||||
|
Dah(down) {
|
||||||
|
this.keyer.Dah(down)
|
||||||
|
if (this.dahChart) this.dahChart.Set(down?1:0)
|
||||||
|
}
|
||||||
|
|
||||||
|
Buzz() {
|
||||||
|
this.buzzer.Buzz()
|
||||||
|
this.lamp.Buzz()
|
||||||
|
if (this.rxChart) this.rxChart.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
Silence() {
|
||||||
|
this.buzzer.Silence()
|
||||||
|
this.lamp.Silence()
|
||||||
|
if (this.rxChart) this.rxChart.Set(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuzzDuration(tx, when, duration) {
|
||||||
|
this.buzzer.BuzzDuration(tx, when, duration)
|
||||||
|
this.lamp.BuzzDuration(tx, when, duration)
|
||||||
|
|
||||||
|
let chart = tx?this.txChart:this.rxChart
|
||||||
|
if (chart) {
|
||||||
|
chart.SetAt(1, when)
|
||||||
|
chart.SetAt(0, when)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the side tone buzzer.
|
||||||
|
*
|
||||||
|
* Called from the keyer.
|
||||||
|
*/
|
||||||
|
beginTx() {
|
||||||
|
this.beginTxTime = Date.now()
|
||||||
|
this.buzzer.Buzz(true)
|
||||||
|
if (this.txChart) this.txChart.Set(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the side tone buzzer, and send out how long it was active.
|
||||||
|
*
|
||||||
|
* Called from the keyer
|
||||||
|
*/
|
||||||
|
endTx() {
|
||||||
|
if (!this.beginTxTime) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let endTxTime = Date.now()
|
||||||
|
let duration = endTxTime - this.beginTxTime
|
||||||
|
this.buzzer.Silence(true)
|
||||||
|
this.repeater.Transmit(this.beginTxTime, duration)
|
||||||
|
this.beginTxTime = null
|
||||||
|
if (this.txChart) this.txChart.Set(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle timing charts.
|
* Toggle timing charts.
|
||||||
|
@ -116,17 +190,17 @@ class VailClient {
|
||||||
let chartsContainer = document.querySelector("#charts")
|
let chartsContainer = document.querySelector("#charts")
|
||||||
if (enable) {
|
if (enable) {
|
||||||
chartsContainer.classList.remove("hidden")
|
chartsContainer.classList.remove("hidden")
|
||||||
this.keyer.SetCanvas(
|
this.ditChart = Chart.FromSelector("#ditChart")
|
||||||
document.querySelector("#txChart"),
|
this.dahChart = Chart.FromSelector("#dahChart")
|
||||||
document.querySelector("#straightChart"),
|
this.txChart = Chart.FromSelector("#txChart")
|
||||||
document.querySelector("#ditChart"),
|
this.rxChart = Chart.FromSelector("#rxChart")
|
||||||
document.querySelector("#dahChart"),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
chartsContainer.classList.add("hidden")
|
chartsContainer.classList.add("hidden")
|
||||||
this.keyer.SetCanvas()
|
this.ditChart = null
|
||||||
|
this.dahChart = null
|
||||||
|
this.txChart = null
|
||||||
|
this.rxChart = null
|
||||||
}
|
}
|
||||||
console.log("timing chart", enable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -226,7 +300,7 @@ class VailClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up an input.
|
* Set up an HTML input element.
|
||||||
*
|
*
|
||||||
* This reads any previously saved value and sets the input value to that.
|
* This reads any previously saved value and sets the input value to that.
|
||||||
* When the input is updated, it saves the value it's updated to,
|
* When the input is updated, it saves the value it's updated to,
|
||||||
|
@ -263,7 +337,6 @@ class VailClient {
|
||||||
outputWpmElement.value = (1200 / value).toFixed(1)
|
outputWpmElement.value = (1200 / value).toFixed(1)
|
||||||
}
|
}
|
||||||
if (callback) {
|
if (callback) {
|
||||||
console.log("callback", selector)
|
|
||||||
callback(e)
|
callback(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -280,28 +353,6 @@ class VailClient {
|
||||||
this.buzzer.Error()
|
this.buzzer.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the side tone buzzer.
|
|
||||||
*/
|
|
||||||
beginTx() {
|
|
||||||
this.beginTxTime = Date.now()
|
|
||||||
this.buzzer.Buzz(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the side tone buzzer, and send out how long it was active.
|
|
||||||
*/
|
|
||||||
endTx() {
|
|
||||||
if (!this.beginTxTime) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let endTxTime = Date.now()
|
|
||||||
let duration = endTxTime - this.beginTxTime
|
|
||||||
this.buzzer.Silence(true)
|
|
||||||
this.repeater.Transmit(this.beginTxTime, duration)
|
|
||||||
this.beginTxTime = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by a repeater class when there's something received.
|
* Called by a repeater class when there's something received.
|
||||||
*
|
*
|
||||||
|
@ -320,8 +371,7 @@ class VailClient {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buzzer.BuzzDuration(false, when, duration)
|
this.BuzzDuration(false, when, duration)
|
||||||
this.lamp.BuzzDuration(false, when, duration)
|
|
||||||
|
|
||||||
this.rxDurations.unshift(duration)
|
this.rxDurations.unshift(duration)
|
||||||
this.rxDurations.splice(20, 2)
|
this.rxDurations.splice(20, 2)
|
||||||
|
|
Loading…
Reference in New Issue