mirror of https://github.com/nealey/vail.git
I think it works? Maybe?
This commit is contained in:
parent
b202ab6968
commit
308131f901
|
@ -24,22 +24,21 @@
|
|||
<div class="mdl-layout-spacer"></div>
|
||||
<!-- Navigation -->
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="https://github.com/nealey/vail">Source Code</a>
|
||||
<a class="mdl-navigation__link" href="https://github.com/nealey/vail/issues/new">Bug Report</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="mdl-layout__drawer">
|
||||
<span class="mdl-layout-title">Title</span>
|
||||
<span class="mdl-layout-title">Repeaters</span>
|
||||
<nav class="mdl-navigation">
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="">Link</a>
|
||||
<a class="mdl-navigation__link" href="?repeater=">1-15 WPM</a>
|
||||
<a class="mdl-navigation__link" href="?repeater=int">16-20 WPM</a>
|
||||
<a class="mdl-navigation__link" href="?repeater=adv">21-99 WPM</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<main class="mdl-layout__content">
|
||||
<div class="flex">
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
|
@ -54,12 +53,23 @@
|
|||
<a href="#iambic" class="mdl-tabs__tab">Iambic</a>
|
||||
</div>
|
||||
<div class="mdl-tabs__panel is-active" id="straight">
|
||||
<button id="key" class="key mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
|
||||
Key
|
||||
</button>
|
||||
<table class="center wide">
|
||||
<tr>
|
||||
<td>
|
||||
<button id="key" class="key mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
|
||||
Key
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>⇧</code>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mdl-tabs__panel" id="iambic">
|
||||
<table style="width: 100%; text-align: center;">
|
||||
<table class="center wide">
|
||||
<tr>
|
||||
<td>
|
||||
<button id="dit" class="key mdl-button mdl-js-button mdl-button--raised mdl-button--colored">
|
||||
|
@ -74,28 +84,80 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>.</code> or <code>⇧</code>
|
||||
<code>.</code> or <code>z</code>
|
||||
</td>
|
||||
<td>
|
||||
<code>/</code> or <code>z</code>
|
||||
<code>/</code> or <code>x</code>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Dit length:
|
||||
<output id="iambic-duration-value"></output>ms
|
||||
<input
|
||||
id="iambic-duration"
|
||||
class="mdl-slider mdl-js-slider"
|
||||
type="range"
|
||||
min="40"
|
||||
max="255"
|
||||
value="80">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">
|
||||
Knobs
|
||||
</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
Average round-trip time:
|
||||
</td>
|
||||
<td>
|
||||
<output id="lag-value">0</output>ms
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Longest recent transmission:
|
||||
</td>
|
||||
<td>
|
||||
<output id="longest-rx-value">0</output>ms
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Suggested receive delay:
|
||||
</td>
|
||||
<td>
|
||||
<output id="suggested-delay-value">0</output>ms
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<p>
|
||||
Recieve delay:
|
||||
<output id="rx-delay-value"></output>ms
|
||||
<input
|
||||
id="rx-delay"
|
||||
class="mdl-slider mdl-js-slider"
|
||||
type="range"
|
||||
min="0"
|
||||
max="5000"
|
||||
value="300">
|
||||
</p>
|
||||
<p>
|
||||
Dit length (iambic):
|
||||
<output id="iambic-duration-value"></output>ms
|
||||
<input
|
||||
id="iambic-duration"
|
||||
class="mdl-slider mdl-js-slider"
|
||||
type="range"
|
||||
min="40"
|
||||
max="255"
|
||||
value="80">
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">
|
||||
|
@ -125,32 +187,39 @@
|
|||
Just like a radio repeater,
|
||||
anybody can connect and start transmitting stuff,
|
||||
and this will broadcast it to everyone connected.
|
||||
If there's enough interest,
|
||||
I'll add something like channels.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you need this to work on a cell phone,
|
||||
let me know and I'll come up with something for you.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="mdl-card__title-text">Why Does This Exist?</h3>
|
||||
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">
|
||||
Why Does This Exist?
|
||||
</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<p>
|
||||
I need a place to practice CW with actual human beings,
|
||||
and I want it to be as close as possible to what I'd experience on a radio.
|
||||
Also, I don't want to make people buy a bunch of radio hardware.
|
||||
Nothing else like this exists on the Internet, as far as I can tell.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>Who made it?</h3>
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">Future plans</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<ul>
|
||||
<li>Move to a more permanent URL</li>
|
||||
<li>Make this page less ugly</li>
|
||||
<li>Arduino program to let you hook up an iambic paddle over USB</li>
|
||||
<li>Document the protocol</li>
|
||||
<li>Support multiple channels/frequencies</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 class="mdl-card__title-text">How can I help?</h3>
|
||||
|
||||
<ul>
|
||||
<li>Improve the <a href="https://github.com/nealey/vail/">source code</a></li>
|
||||
<li>Email me and let me know you're using it</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<a href="mailto:neale@woozle.org">Neale Pickett</a> kd7oqi
|
||||
|
@ -158,31 +227,6 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mdl-card mdl-shadow--4dp">
|
||||
<div class="mdl-card__title">
|
||||
<h3 class="mdl-card__title-text">Future plans</h3>
|
||||
</div>
|
||||
<div class="mdl-card__supporting_text">
|
||||
<ul>
|
||||
<li>Move to a more permanent URL</li>
|
||||
<li>Make this page less ugly</li>
|
||||
<li>Arduino program to let you hook up an iambic paddle over USB</li>
|
||||
<li>Document the protocol</li>
|
||||
<li>Support multiple channels/frequencies</li>
|
||||
<li>Sensible way to make this work on a cell phone</li>
|
||||
<li>Make this page less ugly (I really hate it right now)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3>How can I help?</h3>
|
||||
|
||||
<ul>
|
||||
<li>Improve the <a href="https://github.com/nealey/vail/">source code</a></li>
|
||||
<li><a href="mailto:neale@woozle.org">Email me</a> and let me know you're using it</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
height: 6em;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
|
|
110
static/vail.js
110
static/vail.js
|
@ -2,6 +2,7 @@
|
|||
|
||||
const lowFreq = 660
|
||||
const highFreq = lowFreq * 6 / 5 // Perfect minor third
|
||||
const errorFreq = 30
|
||||
|
||||
const DIT = 1
|
||||
const DAH = 3
|
||||
|
@ -91,26 +92,26 @@ class Buzzer {
|
|||
// in order to avoid "pops" (square wave overtones)
|
||||
// that happen with instant changes in gain.
|
||||
|
||||
constructor(txGain=0.5) {
|
||||
constructor(txGain=0.3) {
|
||||
this.txGain = txGain
|
||||
|
||||
this.ac = new AudioContext()
|
||||
|
||||
this.lowGain = this.ac.createGain()
|
||||
this.lowGain.connect(this.ac.destination)
|
||||
this.lowGain.gain.value = 0
|
||||
this.lowOsc = this.ac.createOscillator()
|
||||
this.lowOsc.connect(this.lowGain)
|
||||
this.lowOsc.frequency.value = lowFreq
|
||||
this.lowOsc.start()
|
||||
this.lowGain = this.create(lowFreq)
|
||||
this.highGain = this.create(highFreq)
|
||||
this.errorGain = this.create(errorFreq, "square")
|
||||
}
|
||||
|
||||
this.highGain = this.ac.createGain()
|
||||
this.highGain.connect(this.ac.destination)
|
||||
this.highGain.gain.value = 0
|
||||
this.highOsc = this.ac.createOscillator()
|
||||
this.highOsc.connect(this.highGain)
|
||||
this.highOsc.frequency.value = highFreq
|
||||
this.highOsc.start()
|
||||
create(frequency, type="sine") {
|
||||
let gain = this.ac.createGain()
|
||||
gain.connect(this.ac.destination)
|
||||
gain.gain.value = 0
|
||||
let osc = this.ac.createOscillator()
|
||||
osc.type = type
|
||||
osc.connect(gain)
|
||||
osc.frequency.value = frequency
|
||||
osc.start()
|
||||
return gain
|
||||
}
|
||||
|
||||
gain(high) {
|
||||
|
@ -145,6 +146,14 @@ class Buzzer {
|
|||
this.txGain = gain
|
||||
}
|
||||
|
||||
/**
|
||||
* Play an error tone
|
||||
*/
|
||||
ErrorTone() {
|
||||
this.errorGain.gain.setTargetAtTime(this.txGain * 0.5, this.ac.currentTime, 0.001)
|
||||
this.errorGain.gain.setTargetAtTime(0, this.ac.currentTime + 0.2, 0.001)
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin buzzing at time
|
||||
*
|
||||
|
@ -187,6 +196,10 @@ class Buzzer {
|
|||
|
||||
class Vail {
|
||||
constructor() {
|
||||
this.sent = []
|
||||
this.lagTimes = [0]
|
||||
this.rxDurations = [0]
|
||||
this.rxDelay = 0 // Milliseconds to add to incoming timestamps
|
||||
this.beginTxTime = null // Time when we began transmitting
|
||||
|
||||
// Set up WebSocket
|
||||
|
@ -194,7 +207,7 @@ class Vail {
|
|||
wsUrl.protocol = "ws:"
|
||||
wsUrl.pathname += "chat"
|
||||
window.socket = new WebSocket(wsUrl)
|
||||
window.socket.addEventListener("message", this.wsMessage)
|
||||
window.socket.addEventListener("message", e => this.wsMessage(e))
|
||||
|
||||
// Listen to HTML buttons
|
||||
for (let e of document.querySelectorAll("button.key")) {
|
||||
|
@ -213,6 +226,7 @@ class Vail {
|
|||
|
||||
// Listen for slider values
|
||||
this.inputInit("#iambic-duration", e => this.iambic.SetInterval(e.target.value))
|
||||
this.inputInit("#rx-delay", e => {this.rxDelay = e.target.value})
|
||||
}
|
||||
|
||||
inputInit(selector, func) {
|
||||
|
@ -241,17 +255,69 @@ class Vail {
|
|||
let endTxTime = Date.now()
|
||||
let duration = endTxTime - this.beginTxTime
|
||||
this.buzzer.Silence(true)
|
||||
this.wsSend(this.beginTxTime, duration)
|
||||
this.beginTxTime = null
|
||||
}
|
||||
|
||||
let msg = JSON.stringify([this.beginTxTime, duration])
|
||||
window.socket.send(msg)
|
||||
updateReading(selector, value) {
|
||||
let e = document.querySelector(selector)
|
||||
if (e) {
|
||||
e.value = value
|
||||
}
|
||||
}
|
||||
|
||||
updateReadings() {
|
||||
let avgLag = this.lagTimes.reduce((a,b) => (a+b)) / this.lagTimes.length
|
||||
let longestRx = this.rxDurations.reduce(Math.max)
|
||||
let suggestedDelay = (avgLag + longestRx) * 1.2
|
||||
|
||||
this.updateReading("#lag-value", avgLag.toFixed())
|
||||
this.updateReading("#longest-rx-value", longestRx)
|
||||
this.updateReading("#suggested-delay-value", suggestedDelay.toFixed())
|
||||
}
|
||||
|
||||
addLagReading(duration) {
|
||||
this.lagTimes.push(duration)
|
||||
if (this.lagTimes.length > 20) {
|
||||
this.lagTimes.shift()
|
||||
}
|
||||
this.updateReadings()
|
||||
}
|
||||
|
||||
addRxDuration(duration) {
|
||||
this.rxDurations.push(duration)
|
||||
if (this.rxDurations.length > 20) {
|
||||
this.rxDurations.shift()
|
||||
}
|
||||
this.updateReadings()
|
||||
}
|
||||
|
||||
wsSend(time, duration) {
|
||||
let msg = [time, duration]
|
||||
let jmsg = JSON.stringify(msg)
|
||||
window.socket.send(jmsg)
|
||||
this.sent.push(jmsg)
|
||||
}
|
||||
|
||||
wsMessage(event) {
|
||||
let msg = JSON.parse(event.data)
|
||||
let jmsg = event.data
|
||||
let msg = JSON.parse(jmsg)
|
||||
let beginTxTime = msg[0]
|
||||
let duration = msg[1]
|
||||
|
||||
console.log(msg)
|
||||
let sent = this.sent.filter(e => e != jmsg)
|
||||
if (sent.length < this.sent.length) {
|
||||
// We're getting our own message back, which tells us our lag.
|
||||
// We shouldn't emit a tone, though.
|
||||
this.sent = sent
|
||||
this.addLagReading(Date.now() - beginTxTime - duration)
|
||||
return
|
||||
}
|
||||
|
||||
// Beep!
|
||||
this.buzzer.BuzzDuration(false, beginTxTime+this.rxDelay, duration)
|
||||
|
||||
this.addRxDuration(duration)
|
||||
}
|
||||
|
||||
key(event) {
|
||||
|
@ -262,11 +328,11 @@ class Vail {
|
|||
|
||||
let begin = event.type.endsWith("down")
|
||||
|
||||
if ((event.code == "Period") || (event.key == "KeyZ")) {
|
||||
if ((event.code == "KeyZ") || (event.code == "Period")) {
|
||||
event.preventDefault()
|
||||
this.iambic.Key(begin, DIT)
|
||||
}
|
||||
if ((event.code == "Slash") || (event.code == "KeyX")) {
|
||||
if ((event.code == "KeyX") || (event.code == "Slash")) {
|
||||
event.preventDefault()
|
||||
this.iambic.Key(begin, DAH)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue