Car Time CW
Backstory
I am an avid amateur radio operator (often called a ham in the states) and for the longest time have only practiced AM radio communications with my Icom IC-7300 and my Xiegu G90. Both of these devices are not massive, but are not as portable as I would like. The part of the hobby I enjoy the most is operating in the field, namely Parks on the Air (POTA) which is an organization that gameifies portable operations in parks into a continuous contest where operators try to get as many contacts as possible while in a state or national park. In the past I have just lugged the G90 out which is a compact unit but only 20 Watts of usable power (which is less than 5 Watts when using AM) or the much larger IC-7300 if I am going to be close to a car or the bands are bad.
But the real action for POTA is with Continuous Wave (CW) or as it is more commonly known to non hams, Morse Code. Morse Code might seem like a bygone protocol, we have phones and digital data after all, but it is enjoying somewhat of a renaissance amongst hams who love operating portable because of how efficient it is. When you key up a transmitter for morse code, 100% of the radio's power goes into generating that tone. Since you are not modulating the amplitude at all like you are with AM, there are no dips in the power. It also allows for easier listening to weak signals since the bandwidth is so tight and you are only listening to the presence or absence of a tone. This allows operators to use a much lower power, and as a result, cheaper and smaller transmitters.
The Problem
So great, I want to learn CW, now what? There are a multitude of options for learning CW in the modern era, but most of them boil down to the Koch method. This method encourages operators to learn the letters at full speed right away so they do not count the dits and dahs and instead focus on the sound of the letters. This allows for faster decoding for slightly more work up front. There are several programs and websites that help with this, but I settled on Learn CW Online which plays blocks of letters and has the user type these letters into a web form to be graded. This was great when I was sitting at my desk, but I wanted to drill the letters while commuting. For that there were fewer options.
There is one pretty good option, Morse Code Ninja which has some great playlists on YouTube that play letters and then say the letter in english. This is great, but the audio files are pre-rendered and do not allow for customization to an operators preferences. They also teach the letters in a different order than lwco.net does.
The Solution
To rectify this, I build a small website that allows the user to set some preferences like tone, character speed, and inter character delay. I called the website Car Time CW and hosted it on GitHub pages.
Generating Morse Code
Car Time CW is built using Vue JS (big surprise there). For the generating the Morse Code, I used a library called Morse Player from Andrew Bunker.
I chose this library primarily due to its very interesting implementation of audio scheduling. I would urge the user to browse Andrew's repo for more information, but essentially it uses Look Ahead Audio Scheduling which enables client side timing very very accurately and does not rely on Java Script delays which are unreliable at best, especially on mobile where OS designers continually clamp down on how async processes work.
There were a few issues with this library, namely that user settings do not take effect until AFTER the first character has been played. There is an open PR that has been ignored that rectifies this, but I wanted to use the NPM package so instead I just play a single 'e' before each string. This is okay but sub optimal to be sure.
Generating the Narration
The next hurdle was synthesizing human speech. This seems like a difficult thing to do, however there is a Web Speech API built into essentially all modern browsers as a part of the Web Audio API.
There were also some issues with this though, mobile implementation of this api is pretty inconsistent. Ultimately I needed the site to say a single empty character before the morse code library plays anything to ensure that a speech synthesis context was attached to the window. This is also not ideal, but causes no performance impact and I do not know a great way around it.
Deployment
This project is hosted on GitHub Pages which largely has documentation for hosting a while Website on it. However I was able to find documentation for single project deployment, and since starting this project that documentation is now on the above link. I did fall into one pitfall, which is the structure of my repo. I initialized the Vue project with Vite AFTER I created the repo on GitHub and cloned it. This shifted everything by one layer. This meant that the playbook did not work out of the box and required some modifications:
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ['main']
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: 'pages'
cancel-in-progress: true
jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: './CarTimeCW'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'npm'
cache-dependency-path: './CarTimeCW/package-lock.json'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload dist folder
path: './CarTimeCW/dist'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4The great thing about pages is that it pulls in all of the auth and what not from the web configuration on GitHub so all of the mess is mostly taken care of for you. Pretty great tool for small static projects like this!
Conclusion
This was a very fun project to do that taught me a lot about building web apps and deploying them to the internet. It directly inspired the creation of this website and was a lot of fun. Most importantly, it has helped me to learn CW effectively while commuting. A great success!
Copyright
Copyright Ownership:prairielandelec
License under:Attribution-NonCommercial-NoDerivatives 4.0 International (CC-BY-NC-ND-4.0)