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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
class HistoryChart {
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
constructor(canvas, strokeStyle="black", duration=20*Second) {
|
||||
constructor(canvas, style=null, duration=20*Second) {
|
||||
this.canvas = canvas
|
||||
this.ctx = canvas.getContext("2d")
|
||||
this.duration = duration
|
||||
|
@ -23,13 +23,12 @@ class HistoryChart {
|
|||
|
||||
// One canvas pixel = 20ms
|
||||
canvas.width = duration / (20 * Millisecond)
|
||||
|
||||
|
||||
// Set origin to lower-left corner
|
||||
this.ctx.scale(1, -1)
|
||||
this.ctx.translate(0, -canvas.height)
|
||||
|
||||
this.ctx.strokeStyle = strokeStyle
|
||||
this.ctx.fillStyle = strokeStyle
|
||||
this.ctx.fillStyle = style || canvas.dataset.color || "black"
|
||||
this.ctx.lineWdith = 2
|
||||
|
||||
this.running=true
|
||||
|
@ -37,21 +36,34 @@ class HistoryChart {
|
|||
}
|
||||
|
||||
/**
|
||||
* Add an event point at a given time.
|
||||
*
|
||||
* These must always be added in time order.
|
||||
*
|
||||
* Set value to something at the current time.
|
||||
*
|
||||
* This also cleans up the event list,
|
||||
* purging anything that is too old to be displayed.
|
||||
*
|
||||
* @param {Number} when Time the event happened
|
||||
*
|
||||
* @param {Number} value Value for the event
|
||||
*/
|
||||
Add(when, value) {
|
||||
let now = Date.now()
|
||||
let earliest = now - this.duration
|
||||
Set(value) {
|
||||
this.SetAt(value)
|
||||
}
|
||||
|
||||
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
|
||||
while ((this.data.length > 1) && (this.data[1][0] < earliest)) {
|
||||
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>
|
||||
|
||||
<div id="charts">
|
||||
<canvas class="chart" id="txChart"></canvas>
|
||||
<canvas class="chart" id="ditChart"></canvas>
|
||||
<canvas class="chart" id="dahChart"></canvas>
|
||||
<canvas class="chart" id="rxChart" data-color="teal"></canvas>
|
||||
<canvas class="chart" id="txChart" data-color="maroon"></canvas>
|
||||
<canvas class="chart" id="ditChart" data-color="olive"></canvas>
|
||||
<canvas class="chart" id="dahChart" data-color="purple"></canvas>
|
||||
</div>
|
||||
|
||||
<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 */
|
||||
const PAUSE_WORD = -7
|
||||
/** Silent period between letters */
|
||||
|
@ -188,25 +186,6 @@ class Keyer {
|
|||
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
|
||||
*/
|
||||
|
@ -351,9 +330,6 @@ class Keyer {
|
|||
} else {
|
||||
this.endTxFunc()
|
||||
}
|
||||
if (this.straightChart) {
|
||||
this.straightChart.Add(Date.now(), down?1:0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -370,9 +346,6 @@ class Keyer {
|
|||
this.Enqueue(DIT)
|
||||
}
|
||||
}
|
||||
if (this.ditChart) {
|
||||
this.ditChart.Add(Date.now(), down?1:0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -389,9 +362,6 @@ class Keyer {
|
|||
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 Inputs from "./inputs.mjs"
|
||||
import * as Repeaters from "./repeaters.mjs"
|
||||
import * as Chart from "./chart.mjs"
|
||||
|
||||
const DefaultRepeater = "General"
|
||||
const Millisecond = 1
|
||||
|
@ -37,19 +38,11 @@ class VailClient {
|
|||
this.lamp = new Buzzer.Lamp()
|
||||
this.buzzer = new Buzzer.ToneBuzzer()
|
||||
this.keyer = new Keyer.Keyer(() => this.beginTx(), () => this.endTx())
|
||||
this.roboKeyer = new Keyer.Keyer(
|
||||
() => {
|
||||
this.buzzer.Buzz()
|
||||
this.lamp.Buzz()
|
||||
},
|
||||
() => {
|
||||
this.buzzer.Silence()
|
||||
this.lamp.Silence()
|
||||
}
|
||||
)
|
||||
this.roboKeyer = new Keyer.Keyer(() => this.Buzz(), () => this.Silence())
|
||||
|
||||
// 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
|
||||
for (let e of document.querySelectorAll("[data-singlekey]")) {
|
||||
|
@ -88,7 +81,6 @@ class VailClient {
|
|||
this.setTelegraphBuzzer(e.target.checked)
|
||||
})
|
||||
this.inputInit("#timing-chart", e => {
|
||||
console.log("moo")
|
||||
this.setTimingCharts(e.target.checked)
|
||||
})
|
||||
|
||||
|
@ -104,6 +96,88 @@ class VailClient {
|
|||
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.
|
||||
|
@ -116,17 +190,17 @@ class VailClient {
|
|||
let chartsContainer = document.querySelector("#charts")
|
||||
if (enable) {
|
||||
chartsContainer.classList.remove("hidden")
|
||||
this.keyer.SetCanvas(
|
||||
document.querySelector("#txChart"),
|
||||
document.querySelector("#straightChart"),
|
||||
document.querySelector("#ditChart"),
|
||||
document.querySelector("#dahChart"),
|
||||
)
|
||||
this.ditChart = Chart.FromSelector("#ditChart")
|
||||
this.dahChart = Chart.FromSelector("#dahChart")
|
||||
this.txChart = Chart.FromSelector("#txChart")
|
||||
this.rxChart = Chart.FromSelector("#rxChart")
|
||||
} else {
|
||||
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.
|
||||
* 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)
|
||||
}
|
||||
if (callback) {
|
||||
console.log("callback", selector)
|
||||
callback(e)
|
||||
}
|
||||
})
|
||||
|
@ -280,28 +353,6 @@ class VailClient {
|
|||
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.
|
||||
*
|
||||
|
@ -320,8 +371,7 @@ class VailClient {
|
|||
return
|
||||
}
|
||||
|
||||
this.buzzer.BuzzDuration(false, when, duration)
|
||||
this.lamp.BuzzDuration(false, when, duration)
|
||||
this.BuzzDuration(false, when, duration)
|
||||
|
||||
this.rxDurations.unshift(duration)
|
||||
this.rxDurations.splice(20, 2)
|
||||
|
|
Loading…
Reference in New Issue