So, for a long while now I’ve been a user of a Yaesu FT-857D. I bought it back in 2011 as shop-soiled stock (someone bought it before me thinking they could make it work on 27MHz!) and have used it on the bicycle pretty much ever since.
The FT-857D is a great rig. Capable of all common amateur bands from 160m through to 70cm, 100W on MF/HF/6m, 50W on 2m, 20W on 70cm, and able to work AM/FM/SSB/CW, in a nice small package. It’s ideal for the bike in that regard. The only thing I could wish for is an actually waterproof head unit, but the stock one has been good, until now.
Last time I rode the bike I had no issues with the head unit display, things were stable and working just fine. That was some months back. Today fired it up to check the battery voltage: it seems I’ve got the dreadded zebra stripe issue. The bike has been in the garage for the past few months, so under cover, not in the sun… anecdotal evidence is that this problem is caused by vibration/heat in vehicle installations, but some reports suggest this can happen for indoor fixed installations too.
The problem
Either way, the zebra has made its home in my radio’s head unit and the display is now pretty much unreadable. Reports suggest I can send it back to Yaesu, pay them $200 (I presume that’s USD, and does not include shipping), and they will replace the defective LCD. However… given they discontinued making these things a few years back, I think I’ve missed the boat on that one!
Time for replacement?
Buying a new replacement isn’t viable right now — Yaesu don’t make anything equivalent: the FT-991A is too big (same size as the FT-897D), the FT-891 doesn’t do 2m/70cm, the FT-818ND is only QRP. Icom’s IC-7100 is the nearest competitor, not out of the question, except it’s a pricey unit for something that will be out in the weather.
Also, a lot of these options are out-of-stock with a big lead time.
Most of the Chinese units only do FM, and are at best quad-banders. Not that I’m interested in buying one: I hear they’re not the longest-lived of transceivers and right now I wish to avoid buying from China anyway.
Kenwood are basically out of the market here in Australia, and they never had an offering like the Icom or Yaesu units; their TS-480SAT was the closest, but does not cover 2m/70cm. The TS-2000 is a monster.
Alinco don’t have anything in a mobile format that competes either. The DX-SR9T does not cover 2m/70cm and is rather big; none of their 2m/70cm sets do HF or SSB.
Keeping the old faithful going
The radio itself works fine. It looks like the wreck of the Hesperus… with paintwork rubbed off the body, screws missing, a DIY fix on the antenna ports, and miscellaneous fixes to other bits. It still works though.
DIY Repair
This could be tricky as I’m not entirely sure what the issue is. It could be just a need for re-flowing everything, or there’s talk of parts needing replacement. The information I have is pretty murky and I could wind up making my partially-working head unit completely non-working.
Replacement used head unit
If someone had a working head unit that they were willing to part with, that might be an option. That said, the used unit could have the same problems my existing unit has, so no guarantee it’ll fix the problem.
CAT port auxiliary display
There are projects that link to the CAT port and present a UI on a separate screen. I was planning on putting a Raspberry Pi 4 there for SDR work, so that’s an option.
Homebrew head unit
Another option is to make a new front head unit. It turns out this has been partially reverse-engineered, so might be a worthy avenue to consider. That would give me a head unit that I can purpose-build for the bike: an attractive option. The hardware interface is 5V TTL UART with a 62kbps baud rate and 8-bits, no parity, two stop bits.
I have a big LCD (128×64) that has been kicking around for a while as well as some TFT resistive touchscreen displays with STM32F103VEs.
The Raspberry Pi 4 scraping the data and presenting it via a remote UI is also an option, in fact may be the direction I wind up going simply because Python on an ARM CPU is much easier to use prototyping something than doing C on a MCU whilst I bed down the finer details of the protocol.
The attraction of this is that I can use what I have on-hand now. Possibly use my tablet as the front-end in the short term. Not good in the rain, but can’t argue with the price!
I’ll go ponder this some more… one thing I am short of though is time to work on this stuff. This week-end is through, and the next one I’ll already be tied up on the Saturday, so I guess I’ll have to squeeze something in.
So, for close to a decade now, I’ve had a bicycle-mobile station. Originally just restricted to 2m/70cm FM, it expanded to 2m SSB with the FT-290RII, then later all-band using a FT-857D.
It’s remained largely unchanged all this time. The station is able to receive MW/SW stations as well, and with some limitations, FM broadcast as well. My recent radio purchases will expand this a bit, freeing up the FT-857D’s general-coverage receiver to just focus on amateur bands. It’s been a long-term project though to move to SDR for reception.
What I have now
Already acquired is a Raspberry Pi 4 (8GB model) and a NWDR DRAWS interface board. I actually started out with a Raspberry Pi 3 + DRAWS and was waiting for the case for it to fit into. At that stage was the idea that the FT-897D would do much as it does now, no SDR involved, and I’d put a small hand-held with its own antenna as an APRS rig being driven by the second port on the DRAWS.
Since then; I bought the HackRF One for work (I needed something that could give me a view of the 2.4GHz ISM band for development of the WideSky Hub), the SDR bug firmly bit. Initially it was just DAB+ reception, I decided to get a RTL-SDR to do that so my radio listening wouldn’t be interrupted when a colleague needed to borrow the HackRF. That RTL-SDR saw some use receiving UHF CB traffic at horse endurance ride events at Imbil — I stated to consider whether maybe this might be a better option as a receiver for more than just commercial radio broadcasts.
Hence I purchased the Pi4: I figured that’d have enough CPU grunt that it’d still be able to decode a reasonable amount even if the CPU throttled itself for thermal management purposes. A pair of SDR interfaces would allow me to monitor a couple of bands simultaneously, such as 2m and 70cm together, or 2m/70cm and one of the HF bands.
Even the RTL-SDR v3 dongles are wide enough to watch the entire 2m band. With CAT control of the FT-857D, it’d be possible for the Pi4 to switch the FT-857D to the same frequency and possibly manage some antenna switching relays as well.
A rough design
This morning I came up with this:
A rough design of the SDR set-up
A critical design feature is that this must have a “pass-through” option so that in the event the computer crashes/fails, I can bypass all the fancy stuff and still use the FT-857D safely as I do now without all the fancy SDR stuff.
So while in SDR mode: the station pushbuttons on the handlebar go to a small sequencing MCU that can report events to the Pi4, on transmit the Pi4 can then instruct that MCU to connect the antennas into bypass mode, short-out the SDR inputs to protect them, then engage the PTT on the FT-857D, and transmit audio can either be delivered direct via the analogue inputs as they are now, or over USB/WiFi/Bluetooth through the MiniDIN6 DATA port.
The thinking is to have two SDRs, one of which is “agile” between HF/6m and 2m/70cm modes.
The front-end will be handled via the tablet: a Samsung Galaxy Active3 which can connect over WiFi or USB CDC-Ethernet.
I’ve shown gain-blocks between the antennas and the receivers, this is largely for impedance matching as well as to account for the losses involved in antenna sharing. Not sure what these will technically look like.
The two on the HF side should be ideally 0-60MHz devices. If I use the AirSpy HF+ as pictured, the VHF/UHF LNA connected to it only has to concentrate on the VHF band below 260MHz (really 144-148MHz, but let’s widen that to 87-230MHz for FM broadcast, air-band and DAB+) since that’s where the AirSpy stops.
The other, for now I’m looking at a RTL-SDR since I have one spare, but that could be any VHF/UHF capable SDR including the AirSpy Mini — the LNA on it, as well as the one feeding the FT-857D in receive mode will both need to handle 144-450MHz at a minimum.
It may be these frequency bands are “too wide” for a single device, and so I need to consider band-pass filters + separate band-specific LNAs and additional switching circuitry.
SDR selection
There are a couple of options I’ve considered:
KerberosSDR: a 4-channel RTL-SDR intended for direction-finding applications, these days replaced by the KrakenSDR which is 5-channel.
RTL-SDR or clone in direct sampling mode with an active filter + LNA
The thing I don’t like about the SDRPlay Duo is the non-free nature of its libraries which seem to be only available for i386 or AMD64. Otherwise on paper it looks like a nice option.
KerberosSDR/KrakenSDR seems like overkill. It’s basically four (or five) RTL-SDRs sharing a common oscillator which is essential for direction-finding, but let’s face it, I’ll never have enough antennas to make such an application feasible on the bicycle. It looks like an echidna now!
BladeRF looks nice, but is pricey and stops short of the HF band so would need an up-converter like the RTL-SDR — not a show-stopper. That said, it’s dual-channel and can transmit as well as receive, so cross-band repeater would be doable.
I should try this with the HackRF One some day, see if I can combine a conventional transceiver + RPi + DRAWS/UDRC + HackRF One to make a cross-band repeater.
The Airspy HF+ is available domestically, and isn’t too badly priced. It doesn’t transmit like the HackRF does, but then again I could stuff one of my Wouxun KG-UVD1Ps in there wired up to the second DRAWS port if I wanted a traditional cross-band set-up.
Next steps
It would seem the LNA / antenna sharing side of things needs consideration next. RF relays will need to be procured that can handle seeing 100W of RF. Where I’ve drawn a single switch, that’ll likely be multiple in reality — when the transmitter is connected to the antenna, the receivers should all be shorted to ground so they don’t get blown up by stray RF.
Maybe the LNAs feeding the FT-857D will need to be connected to a dummy-load to protect them, not sure. Perhaps LNAs aren’t strictly necessary, and I can “cheat” by just connecting receivers in parallel, but I’m not comfortable with this idea right now. So this is the area of research I’m focusing on right now.
Lately, I’ve been stuck at home with not much bicycle mobile operation happening, and it’s given me time to review where I’m going with the station and the onboard communications systems.
At home, I’ve been listening to a lot of commercial radio, whereas on the bicycle, in pre-COVID times I was basically restricted to recorded music unless I wanted to use the FT-897D for broadcast radio reception.
Now, the Yaesu rig actually isn’t a bad receiver for broadcast radio… but a few downsides:
Wideband FM sounds good, but is only received in mono
Medium wave and shortwave broadcast requires a rather bulky HF antenna to be deployed
The FT-897D is thirsty for power: about 1A on receive
When receiving broadcast radio, I obviously cannot monitor amateur frequencies
Some of the stations I like listening to are on DAB+, which the FT-897D will never receive
Long-term plan
Long term, the plan is to use SDR to augment the FT-897D, basically I rig up a Raspberry Pi 4 (already procured) with a SDR, and through some antenna switching, basically use the FT-897D as the transmitter with the Raspberry Pi 4 implementing an all-band scanning receiver. That would give me dual-watch (actually, I could watch entire bands) capability which I miss on the FT-897D.
Likely, the SDR chosen will either be a multi-channel one so I can watch a couple of bands: 2m + 70cm; or maybe I monitor 2m whilst listening to a radio broadcast on the other. SDR would also open up DAB+ to me.
This is a long-way off though. And also is rather fixed to the bike, I can’t take any of this stuff on a walk, which lately in COVID times has been my more likely form of exercise.
Current MW rig
For medium wave reception, I do have a small portable transistor radio, a Sanyo BC-088 which I was given years ago in non-working condition. The fault at first was broken PCB traces from the unit being thrown against a wall by its previous owner, which was fixed and allowed the radio to give many years of entertainment for over a decade until another incident on the bike smacked it against Waterworks Road, breaking a few connections to the internal loop-stick antenna.
I’ve repaired that, and the unit now works, but found it does not get along with any microprocessor-based device; picking up all manner of hash when placed near my handheld GPS (Garmin Rino 650) and squealing like a banshee next to my desktop PC. It also seems to be a tad deaf.
SDR is one possible option, but the SDRs I have in my possession: a couple of RTL-SDR v3 dongles and a HackRF One, none of them will tune down to 693kHz where I normally have the BC-088 tuned. The HackRF One gets close at 1MHz, but anything below 10MHz sounds terrible with noise and birdies galore. Even for shortwave, the HackRF One seems to suffer; trying it out on the HF antenna at home, I find myself picking up 4BH at 18883kHz — they normally broadcast at 882kHz.
Thus, I figured I’d try a couple of off-the-shelf options for the short-term and see how they go. Ideally I wanted a single radio that could do MW, FM and DAB+ bands… bonus points if it could do shortwave too.
New DAB+ rig: Digitech AR-1690
I bought this at a time when I noticed all the Australian Radio Network stations (4KQ, 97.3) suddenly go mute on the Brisbane channel 9A multiplex. I wasn’t sure if it was my end or the station, as other DAB+ stations seemed to be fine, and thought this little rig would both be a useful observer, and scratch that itch of portable listening.
The Digitech AR-1690
This is a basic entry-level DAB+/FM set. It’s a smallish unit, roughly 125mm×73mm×30mm. There’s no real special features of this unit. It has 40 station presets; 20 each for FM and DAB+, and there’s two alarm functions that can be set. The clock is set by the radio transmitter time broadcast. The front panel features the volume and channel buttons, along with a SELECT button. The rest of the controls are on the top.
Top controls
Info/Menu button:
Long-press → enters a configuration menu where you can configure the system time, set the two alarms, see the firmware version or do a factory reset
Short press → scrolls through different pieces of information on the LCD display:
Frequency
Current time
Current date
(DAB+): Signal strength?
(DAB+): Genre
(DAB+): DAB+ Multiplex name
(DAB+): Frequency and channel
(DAB+): Signal error rate
(DAB+): Bit rate and standard (DAB or DAB+)
(FM RDS): Station name
(FM RDS): Genre
Mode: Switches between FM and DAB+ mode
Scan: Initiates a scan on the currently selected mode (so, all FM broadcast, or all DAB+ channels)
Alarm: A shortcut button for setting the alarms (same as holding Info/Menu, then navigating to Alarms)
Preset: Used for accessing memory presets, short press recalls a preset, long press to store a station preset
Power: Switches the radio on, stand-by (short press) or off (long press)
For power, it can either run on 3 AAA cells, or you can buy separately a Nokia BL-5C Lithium battery.
The back of the radio, nothing much to see here.The battery compartment, with Nokia BL-5C installed
As for ports, there’s just the two on the right-hand side:
Connectors
The power jack is a small ~3mm barrel jack, the radio is supplied with a USB cable that interfaces to this connector. Looks like a dead ringer for the old Nokia phone connectors, I might dig up one of my old chargers and see if it works. (Update 2022-03-11: Found one, it doesn’t… the barrel is the same size but the tip in the radio is too big to fit in the bore of the connector.)
In operation
The set seems to do a reasonable job. I’m close to Mt. Coot-tha, so receiving DAB+ really isn’t that difficult. The sound is quite reasonable for the size, I thought the speaker would be a bit on the tinny side, but it’s perfectly listen-able. Certainly it’s a big improvement on the BC-088!
One gripe I do have with this set is that the volume steps are very coarse, and there’s no real “quiet” setting. Minimum volume is mute, one step up is comfortable listening level in a small room. I would have liked maybe 3 or four steps in between.
In both DAB+ mode, it can report the station dynamic labels.
DAB+ dynamic labels
It also can pull a similar stunt with RDS data on FM:
FM RDS reception
New Short wave rig: Tecsun PL-398MP
Now, when I bought the above DAB+ receiver, I ideally wanted something that would do MW broadcast as well, as one of the stations pictured on the DAB set is in fact, a MW station as well.
There is such a beast, Sangean make the DPR-45 which can do MW/FM and DAB+, but it’s enormous. Too big for my needs. Plus I found it after purchasing the little AR-1690 (not that it mattered, as size pretty much rules the DPR-45 out). I figured the next best thing was to get a portable set that had a line-in feature so it could provide the stereo speakers that the AR-1690 lacks.
Enter the Tecsun PL-398MP.
The Tecsun PL-398MP
As the text above the screen suggests, this is quad-band radio; supporting LW/MW/SW and FM bands, as well as a (primitive!) MP3 player. Unlike the Sanyo BC-088 it’s replacing, which boasted 8 transistors (wow!), this unit is a DSP-based receiver using the common Silicon Labs Si4734 radio receiver IC.
Most of the controls are on the front. The labels marked in red are activated when the radio is turned off; so holding 1 down allows you to switch the FM radio band from the default 88-108MHz to 64-108MHz or 76-108MHz. Holding 2 down switches the clock between 12-hour and 24-hour time, 3 will switch the MW band between using 9kHz steps and 10kHz steps, 0 turns keypad beep on/off and the ST button toggles the “intelligent backlight”.
Unlike the AR-1690, this thing runs on either standard disposable dry-cells, or you can install Ni-MH cells and by holding the M button whilst the radio is turned off, you can enable a built-in charger. Dry cells are not exactly my favourite way of powering a device, for no other reason than the reduced energy density and their nasty habit of leaking electrolyte.
Maybe a future project will be to hack a LiPo cell into this thing.
On the back, are the controls for the MP3 player.
Back of the PL-398MP
I’ll get to the MP3 player part in a moment, but in short, don’t bother!
For tuning and volume, there are two thumbwheels on the right-hand side. These are both rotary encoders driving a small microcontroller inside.
Volume and tuning controls
The “digital” volume control steps aren’t too bad for resolution, certainly nowhere near as coarse as the AR-1690! The tuning knob works well enough for small adjustments, and for moving between presets. Thankfully for moving between stations, there’s the keypad for entering frequencies directly.
On the left are all the ports:
FM/SW antenna, line-in, earphone output, and a 5V mini-USB input
The line-in feature is what set this apart from other MW and SW-capable sets. Being able to connect an external shortwave antenna is a welcome feature, and with this radio, I purchased a Sangean ANT-60 antenna for this purpose.
On top, there’s just the Light/Snooze button; pressing it momentarily turns the lighting on. I presume it’ll also silence the wake-up alarm if you have one set, but I haven’t tried this.
Light / Snooze button on top
In operation
FM Stereo reception
I’m close to the Mt Coot-tha transmitter site, so this isn’t much of a strain for the receiver, I guess I’ll know more when I take it out of town with me, but it seems to receive the local stations well, without getting overloaded from the strong ones (looking at you ABC Classic FM).
Being a dual-speaker device, this can provide stereo without additional hardware. Audio quality is actually decent for a radio this size. The speaker drivers are about 50mm in diameter, appear to be a low-profile mylar construction; not going to win audiophile magazine awards and are outperformed by many Bluetooth speakers, but are decent enough.
Short wave reception
The shortwave feature of this set seems good so far. There’s not as much to listen to on the shortwave bands as there used to be, but I’ve been able to receive China Radio International and Radio New Zealand both quite clearly, and one evening managed to pick up the BBC World service.
It performs decently with its built-in antenna, even without me telescoping it out. I haven’t had a chance to fully try the set with the ANT-60 — I did try it indoors in my room, but I suspect I haven’t really got enough wire “in the air” to make much difference. I’ll have to try it at a camp site some evening.
Medium wave reception
This blew me away actually. Okay, so maybe a late 60’s era transistor radio with leaky vintage germanium transistors that’s had a hard life and more than one ham-fisted repair attempt is not much of a contest, but it left the old Sanyo in the dust.
4KQ on 693kHz was a bit of a fiddle to get tuned on the Sanyo, and even then, I found I had to have the radio oriented right to receive it. 4QR on 612kHz of course, was loud and strong. Both stations are very clear on the PL-398MP. Ohh, and while this set’s no rich console radio, it’s nowhere near as tinny as what I was expecting to hear. For a portable rig, quite acceptable.
Out of the box, my unit used 9kHz frequency steps, which will also suit Europe. For those in the USA, you’ll want to hold that “3” button with the power off to switch the radio to a 10kHz spacing. This will also switch the temperature display to show °F instead of °C.
Long wave reception
Firstly, to even get at LW took a bit of fiddling. The handbook is a little inaccurate, telling you to press a non-existent MW/LW button. The correct procedure to enable LW is to turn the radio off, then long-press the AM button. The display will then show “LW” and “On” to indicate the feature is now enabled.
Turning on LW mode.
The same procedure turns the LW feature off too.
Having done so, when you turn the radio back on, pressing the AM button momentarily will now switch between MW and LW.
Now, ITU region 3 where I am, does not have any official LW stations. Nor does region 2 (Americas), this is a feature that’s more useful to those in Europe.
There used to be a LW weather beacon on 359kHz broadcasting out of Amberley Air base, and my Sony ST-2950F (my very first LW-capable receiver) could pick it up with its loop-stick antenna. Neither it, nor the PL398-MP do today. I guess I could drag out one of my amateur sets out to get a third opinion, but smart money is that the transmitter is now turned off.
Never mind, I’ll just turn LW back off and not worry about it.
Line-in feature
This works pretty simple, the radio is supplied with a 3.5mm male-male stereo cable. Plug one end into the line-in port on the radio, and the other into your audio device. On the LCD screen, a “>>” symbol appears on the right-hand side. Turn the radio on, and get your source playing, you’ll hear it through the radio speakers.
Nice and simple. I’ll be able to use it with the aforementioned AR-1690, my tablet, and the little portable media player I already use on the bike.
MP3 Player
Yes, I did mention it has one. The controls are on the back, and the device takes a SD card via a port hiding under the rear stand.
The SD card port
Plugging in a FAT32-formatted SD card with some MP3s on it (The Goons Show, what else?) and turning the radio on, I then tried getting it to play something. Hitting Pause/Play at first seemed to do nothing, but eventually I must’ve either waited long enough, or managed to coax it to play something, it started playing the first track it found.
I could navigate between the tracks — I have no idea whether it sorts the files by file name or not, the display is too primitive to support showing any track metadata, but it did work. There’s no playlist capability in this device, no random shuffle mode, as I say, it’s primitive.
So I think I’ll just ignore it and pretend it’s not there. A Bluetooth receiver would have had greater utility, but never mind. There is a sister-model to this one, the PL398-BT with exactly this feature… but good luck getting one unless you order direct from China.
Hidden function #1: A lithium charger?
So, fiddling with the radio, I noticed a few hidden features that are undocumented. With the radio off, holding the VM button triggers the display to show “Li On” and the “Ni-MH Battery” indicator starts flashing.
Is this a “Lithium battery” mode?
Exactly what this is doing I’m not sure. There are radios in Tecsun’s line-up that do support and include Lithium batteries, so maybe the project to add this feature isn’t out of the question. I guess a trip into the set with a screw driver will be my next move, but maybe some of that work is done for me.
Hidden feature #2: Self test?
Holding the BW button whilst the radio is turned off seems to perform a self-test of the display.
All segments on the LCD turned on
When the button is released, it switches back to showing the time, plus some 4-digit code (firmware version perhaps?):
“3985”, a code for the gurus to meditate over
Not sure what this is.
Final comments
All in all, both seem to be decent sets. The little DAB+ set is more-or-less a one-trick pony, it’ll be interesting to see if it does any better or worse than the Tecsun. I’m also yet to introduce these to the Garmin GPS that caused my Sanyo so much grief.
It’s nice to know that short wave sets are still being manufactured, and the performance of this set is quite remarkable. Tecsun themselves are based out of Hong Kong, and seem to have a decent reputation from what I’ve seen in reviews online.
While lately it’s been my policy to avoid buying stuff that’s made in China / by Chinese companies — in this case the feature set I wanted was practically a unicorn, no one else makes something like this, and this set seems to perform decently, so we’ll see how it looks after a year or two to see how it is long-term. After all, the little Sanyo has been in my possession since the early 90s, and it was an old radio then… it still goes. Will the Tecsun last as long? We’ll see.
As for the Digitech unit; well, DAB+ has a crazy amount of DSP going on to pick out one station out of a multiplex. I expect being more complicated, it’ll perhaps have a shorter longevity, but hopefully long enough for me to cobble up a replacement. Time will tell.
Lately, I’ve been socially distancing a home and so there’s been a few projects that have been considered that otherwise wouldn’t ordinarily get a look in on a count of lack-of-time.
One of these has been setting up a Raspberry Pi with DRAWS board for use on the bicycle as a radio interface. The DRAWS interface is basically a sound card, RTC, GPS and UART interface for radio interfacing applications. It is built around the TI TMS320AIC3204.
Right now, I’m still waiting for the case to put it in, even though the PCB itself arrived months ago. Consequently it has not seen action on the bike yet. It has gotten some use though at home, primarily as an OpenThread border router for 3 WideSky hubs.
My original idea was to interface it to Mumble, a VoIP server for in-game chat. The idea being that, on events like the Yarraman to Wulkuraka bike ride, I’d fire up the phone, connect it to an AP run by the Raspberry Pi on the bike, and plug my headset into the phone:144/430MHz→2.4GHz cross-band.
That’s still on the cards, but another use case came up: digital. It’d be real nice to interface this over WiFi to a stronger machine for digital modes. Sound card over network sharing. For this, Mumble would not do, I need a lossless audio transport.
Audio streaming options
For audio streaming, I know of 3 options:
PulseAudio network streaming
netjack
trx
PulseAudio I’ve found can be hit-and-miss on the Raspberry Pi, and IMO, is asking for trouble with digital modes. PulseAudio works fine for audio (speech, music, etc). It will make assumptions though about the nature of that audio. The problem is we’re not dealing with “audio” as such, we’re dealing with modem tones. Human ears cannot detect phase easily, data modems can and regularly do. So PA is likely to do things like re-sample the audio to synchronise the two stations, possibly use lossy codecs like OPUS or CELT, and make other changes which will mess with the signal in unpredictable ways.
netjack is another possibility, but like PulseAudio, is geared towards low-latency audio streaming. From what I’ve read, later versions use OPUS, which is a no-no for digital modes. Within a workstation, JACK sounds like a close fit, because although it is geared to audio, its use in professional audio means it’s less likely to make decisions that would incur loss, but it is a finicky beast to get working at times, so it’s a question mark there.
trx was a third option. It uses RTP to stream audio over a network, and just aims to do just that one thing. Digging into the code, present versions use OPUS, older versions use CELT. The use of RTP seemed promising though, it actually uses oRTP from the Linphone project, and last weekend I had a fiddle to see if I could swap out OPUS for linear PCM. oRTP is not that well documented, and I came away frustrated, wondering why the receiver was ignoring the messages being sent by the sender.
It’s worth noting that trx probably isn’t a good example of a streaming application using oRTP. It advertises the stream as G711u, but then sends OPUS data. What it should be doing is sending it as a dynamic content type (e.g. 96), and if this were a SIP session, there’d be a RTPMAP sent via Session Description Protocol to say content type 96 was OPUS.
I looked around for other RTP libraries to see if there was something “simpler” or better documented. I drew a blank. I then had a look at the RTP/RTCP specs themselves published by the IETF. I came to the conclusion that RTP was trying to solve a much more complicated use case than mine. My audio stream won’t traverse anything more sophisticated than a WiFi AP or an Ethernet switch. There’s potential for packet loss due to interference or weak signal propagation between WiFi nodes, but latency is likely to remain pretty consistent and out-of-order handling should be almost a non-issue.
Another gripe I had with RTP is its almost non-consideration of linear PCM. PCMA and PCMU exist, 16-bit linear PCM at 44.1kHz sampling exists (woohoo, CD quality), but how about 48kHz? Nope. You have to use SDP for that.
Custom protocol ideas
With this in mind, my own custom protocol looks like the simplest path forward. Some simple systems that used by GQRX just encapsulate raw audio in UDP messages, fire them at some destination and hope for the best. Some people use TCP, with reasonable results.
My concern with TCP is that if packets get dropped, it’ll try re-sending them, increasing latency and never quite catching up. Using UDP side-steps this, if a packet is lost, it is forgotten about, so things will break up, then recover. Probably a better strategy for what I’m after.
I also want some flexibility in audio streams, it’d be nice to be able to switch sample rates, bit depths, channels, etc. RTP gets close with its L16/44100/2 format (the Philips Red-book standard audio format). In some cases, 16kHz would be fine, or even 8kHz 16-bit linear PCM. 44.1k works, but is wasteful. So a header is needed on packets to at least describe what format is being sent. Since we’re adding a header, we might as well set aside a few bytes for a timestamp like RTP so we can maintain synchronisation.
So with that, we wind up with these fields:
Timestamp
Sample rate
Number of channels
Sample format
Timestamp
The timestamp field in RTP is basically measured in ticks of some clock of known frequency, e.g. for PCMU it is a 8kHz clock. It starts with some value, then increments up monotonically. Simple enough concept. If we make this frequency the sample rate of the audio stream, I think that will be good enough.
At 48kHz 16-bit stereo; data will be streaming at 192kbps. We can tolerate wrap-around, and at this data rate, we’d see a 16-bit counter overflow every ~341ms, which whilst not unworkable, is getting tight. Better to use a 32-bit counter for this, which would extend that overflow to over 6 hours.
Sample rate encoding
We can either support an integer field, or we can encode the rate somehow. An integer field would need a range up to 768k to support every rate ALSA supports. That’s another 32-bit integer. Or, we can be a bit clever: nearly every sample rate in common use is a harmonic of 8kHz or 11.025kHz, so we devise a scheme consisting of a “base” rate and multiplier. 48kHz? That’s 8kHz×6. 44.1kHz? That’s 11.025kHz×4.
If we restrict ourselves to those two base rates, we can support standard rates from 8kHz through to 1.4MHz by allocating a single bit to select 8kHz/11.025kHz and 7 bits for the multiplier: the selected sample rate is the base rate multiplied by the multipler incremented by one. We’re unlikely to use every single 8kHz step though. Wikipedia lists some common rates and as we go up, the steps get bigger, so let’s borrow 3 multiplier bits for a left-shift amount.
7 6 5 4 3 2 1 0
B S S S M M M M
B = Base rate: (0) 8000 Hz, (1) 11025 Hz
S = Shift amount
M = Multiplier - 1
Rate = (Base << S) * (M + 1)
Examples:
00000000b (0x00): 8kHz
00010000b (0x10): 16kHz
10100000b (0xa0): 44.1kHz
00100000b (0x20): 48kHz
01010010b (0x52): 768kHz (ALSA limit)
11111111b (0xff): 22.5792MHz (yes, insane)
Other settings
I primarily want to consider linear PCM types. Technically that includes unsigned PCM, but since that’s losslessly transcodable to signed PCM, we could ignore it. So we could just encode the number of bytes needed for a single channel sample, minus one. Thus 0 would be 8-bits; 1 would be 16-bits; 2 would be 32-bits and 3 would be 64-bits. That needs just two bits. For future-proofing, I’d probably earmark two extra bits; reserved for now, but might be used to indicate “compressed” (and possibly lossy) formats.
The remaining 4 bits could specify a number of channels, again minus 1 (mono would be 0, stereo 1, etc up to 16).
Packet type
For the sake of alignment, I might include a 16-bit identifier field so the packet can be recognised as being this custom audio format, and to allow multiplexing of in-band control messages, but I think the concept is there.
In my last post, I mentioned that I was playing around with SDR a bit more, having bought a couple. Now, my experiments to date were low-hanging fruit: use some off-the-shelf software to receive an existing signal.
One of those off-the-shelf packages was CubicSDR, which gives me AM/FM/SSB/WFM reception, the other is qt-dab which receives DAB+. The long-term goal though is to be able to use GNURadio to make my own tools. Notably, I’d like to set up a Raspberry Pi 3 with a DRAWS board and a RTL-SDR, to control the FT-857D and implement dual-watch for emergency comms exercises, or use the RTL-SDR for DAB+ reception.
In the latter case, while I could use qt-dab, it’ll be rather cumbersome in that use case. So I’ll probably implement my own tool atop GNURadio that can talk to a small microcontroller to drive a keypad and display. As a first step, I thought I’d try a DIY FM stereo receiver. This is a mildly complex receiver that builds on what I learned at university many moons ago.
FM Stereo is actually surprisingly complex. Not DAB+ levels of complex, but still complex. The system is designed to be backward-compatible with mono FM sets. FM itself actually does not provide stereo on its own — a stereo FM station operates by multiplexing a “mono” signal, a “differential” signal, and a pilot signal. The pilot is just a plain 19kHz carrier. Both left and right channels are low-pass filtered to a band-width of 15kHz. The mono signal is generated from the summation of the left and right channels, whilst the differential is produced from the subtraction of the right from the left channel.
The pilot signal is then doubled and used as the carrier for a double-sideband suppressed carrier signal which is modulated by the differential signal. This is summed with the pilot and mono signal, and that is then frequency-modulated.
For reception, older mono sets just low-pass the raw FM discriminator output (or rely on the fact that most speakers won’t reproduce >18kHz well), whilst a stereo set performs the necessary signal processing to extract the left and right channels.
Below, is a flow-graph in GNURadio companion that shows this:
Flow graph for FM stereo reception
The signal comes in at the top-left via a RTL-SDR. We first low-pass filter it to receive just the station we want (in this case I’m receiving Triple M Brisbane at 104.5MHz). We then pass it through the WBFM de-modulator. At this point I pass a copy of this signal to a waterfall plot. A second copy gets low-passed at 15kHz and down-sampled to a 32kHz sample rate (my sound card doesn’t do 500kHz sample rates!).
A third copy is passed through a band-pass filter to isolate the differential signal, and a fourth, is filtered to isolate the pilot at 19kHz.
The pilot in a real receiver would ordinarily be full-wave-bridge-rectified, or passed through a PLL frequency synthesizer to generate a 38kHz carrier. Here, I used the abs math function, then band-passed it again to get a nice clean 38kHz carrier. This is then mixed with the differential signal I isolated before, then the result low-pass filtered to shift that differential signal to base band.
I now have the necessary signals to construct the two channels: M + D gives us (L+R) + (L-R) = 2L, and M – D = (L+R) – (L – R) = 2R. We have our stereo channels.
Below are the three waterfall diagrams showing (from top to bottom) the de-modulated differential signal, the 38kHz carrier for the differential signal and the raw output from the WBFM discriminator.
The constituent components of a FM stereo radio station.
Not decoded here is the RDS carrier which can be seen just above the differential signal in the third waterfall diagram.
Well, lately I’ve been doing a bit of work hacking the firmware on the Rowetel SM1000 digital microphone. For those who don’t know it, this is a hardware (microcontroller) implementation of the FreeDV digital voice mode: it’s a modem that plugs into the microphone/headphone ports of any SSB-capable transceiver and converts FreeDV modem tones to analogue voice.
I plan to set this unit of mine up on the bicycle, but there’s a few nits that I had.
There’s no time-out timer
The unit is half-duplex
If there’s no timeout timer, I really need to hear the tones coming from the radio to tell me it has timed out. Others might find a VOX feature useful, and there’s active experimentation in the FreeDV 700B mode (the SM1000 currently only supports FreeDV 1600) which has been very promising to date.
Long story short, the unit needed a more capable UI, and importantly, it also needed to be able to remember settings across power cycles. There’s no EEPROM chip on these things, and while the STM32F405VG has a pin for providing backup-battery power, there’s no battery or supercapacitor, so the SM1000 forgets everything on shut down.
ST do have an application note on their website on precisely this topic. AN3969 (and its software sources) discuss a method for using a portion of the STM32’s flash for this task. However, I found their “license” confusing. So I decided to have a crack myself. How hard can it be, right?
There’s 5 things that a virtual EEPROM driver needs to bear in mind:
The flash is organised into sectors.
These sectors when erased contain nothing but ones.
We store data by programming zeros.
The only way to change a zero back to a one is to do an erase of the entire sector.
The sector may be erased a limited number of times.
So on this note, a virtual EEPROM should aim to do the following:
It should keep tabs on what parts of the sector are in use. For simplicity, we’ll divide this into fixed-size blocks.
When a block of data is to be changed, if the change can’t be done by changing ones to zeros, a copy of the entire block should be written to a new location, and a flag set (by writing zeros) on the old block to mark it as obsolete.
When a sector is full of obsolete blocks, we may erase it.
We try to put off doing the erase until such time as the space is needed.
Step 1: making room
The first step is to make room for the flash variables. They will be directly accessible in the same manner as variables in RAM, however from the application point of view, they will be constant. In many microcontroller projects, there’ll be several regions of memory, defined by memory address. This comes from the datasheet of your MCU.
The MCU here is the STM32F405VG, which has 1MB of flash starting at address 0x08000000. This 1MB is divided into (in order):
Sectors 0…3: 16kB starting at 0x08000000
Sector 4: 64kB starting at 0x0800c000
Sector 5 onwards: 128kB starting at 0x08010000
We need at least two sectors, as when one fills up, we will swap over to the other. Now it would have been nice if the arrangement were reversed, with the smaller sectors at the end of the device.
The Cortex M4 CPU is basically hard-wired to boot from address 0, the BOOT pins on the STM32F4 decide how that gets mapped. The very first few instructions are the interrupt vector table, and it MUST be the thing the CPU sees first. Unless told to boot from external memory, or system memory, then address 0 is aliased to 0x08000000. i.e. flash sector 0, thus if you are booting from internal flash, you have no choice, the vector table MUST reside in sector 0.
Normally code and interrupt vector table live together as one happy family. We could use a couple of 128k sectors, but 256k is rather a lot for just an EEPROM storing maybe 1kB of data tops. Two 16kB sectors is just dandy, in fact, we’ll throw in the third one for free since we’ve got plenty to go around.
However, the first one will have to be reserved for the interrupt vector table that will have the space to itself.
/* Specify the memory areas */
MEMORY
{
/* ISR vectors *must* be placed here as they get mapped to address 0 */
VECTOR (rx) : ORIGIN = 0x08000000, LENGTH = 16K
/* Virtual EEPROM area, we use the remaining 16kB blocks for this. */
EEPROM (rx) : ORIGIN = 0x08004000, LENGTH = 48K
/* The rest of flash is used for program data */
FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 960K
/* Memory area */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
/* Core Coupled Memory */
CCM (rwx) : ORIGIN = 0x10000000, LENGTH = 64K
}
This is only half the story, we also need to create the section that will be emitted in the ELF binary:
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} >FLASH
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
_exit = .;
} >FLASH…
There’s rather a lot here, and so I haven’t reproduced all of it, but this is the same file as before at revision 2389, but a little further down. You’ll note the .isr_vector is pointed at the region called FLASH which is most definitely NOT what we want. The image will not boot with the vectors down there. We need to change it to put the vectors in the VECTOR region.
Whilst we’re here, we’ll create a small region for the EEPROM.
THAT’s better! Things will boot now. However, there is still a subtle problem that initially caught me out here. Sure, the shiny new .eeprom section is unpopulated, BUT the linker has helpfully filled it with zeros. We cannot program zeroes back into ones! Either we have to erase it in the program, or we tell the linker to fill it with ones for us. Thankfully, the latter is easy (stm32_flash.ld at 2395):
.eeprom :
{
. = ALIGN(4);
KEEP(*(.eeprom)) /* special section for persistent data */
. = ORIGIN(EEPROM) + LENGTH(EEPROM) - 1;
BYTE(0xFF)
. = ALIGN(4);
} >EEPROM = 0xff
We have to do two things. One, is we need to tell it that we want the region filled with the pattern 0xff. Two, we need to make sure it gets filled with ones by telling the linker to write one as the very last byte. Otherwise, it’ll think, “Huh? There’s nothing here, I won’t bother!” and leave it as a string of zeros.
Step 2: Organising the space
Having made room, we now need to decide how to break this data up. We know the following:
We have 3 sectors, each 16kB
The sectors have an endurance of 10000 program-erase cycles
Give some thought as to what data you’ll be storing. This will decide how big to make the blocks. If you’re storing only tiny bits of data, more blocks makes more sense. If however you’ve got some fairly big lumps of data, you might want bigger blocks to reduce overheads.
I ended up dividing the sectors into 256-byte blocks. I figured that was a nice round (binary sense) figure to work with. At the moment, we have 16 bytes of configuration data, so I can do with a lot less, but I expect this to grow. The blocks will need a header to tell you whether or not the block is being used. Some checksumming is usually not a bad idea either, since that will clue you in to when the sector has worn out prematurely. So some data in each block will be header data for our virtual EEPROM.
If we don’t care about erase cycles, this is fine, we can just make all blocks data blocks, however it’d be wise to track this, and avoid erasing and attempting to use a depleted sector, so we need somewhere to track this. 256 bytes gives us enough space to stash an erase counter and a map of what blocks are in use within that sector.
So we’ll reserve the first block in the sector to act as this index for the entire sector. This gives us enough room to have 16-bits worth of flags for each block stored in the index. That gives us 63 blocks per sector for data use.
It’d be handy to be able to use this flash region for a few virtual EEPROMs, so we’ll allocate some space to give us a virtual ROM ID. It is prudent to do some checksumming, and the STM32F4 has a CRC32 module, so in that goes, and we might choose to not use all of a block, so we should throw in a size field (8 bits, since the size can’t be bigger than 255). If we pad this out a bit to give us a byte for reserved data, we get a header with the following structure:
15
14
13
12
11
10
19
8
7
6
5
4
3
2
1
0
+0
CRC32 Checksum
+2
+4
ROM ID
Block Index
+6
Block Size
Reserved
So that subtracts 8 bytes from the 256 bytes, leaving us 248 for actual program data. If we want to store 320 bytes, we use two blocks, block index 0 stores bytes 0…247 and has a size of 248, and block index 1 stores bytes 248…319 and has a size of 72.
I mentioned there being a sector header, it looks like this:
15
14
13
12
11
10
19
8
7
6
5
4
3
2
1
0
+0
Program Cycles Remaining
+2
+4
+6
+8
Block 0 flags
+10
Block 1 flags
+12
Block 2 flags
…
No checksums here, because it’s constantly changing. We can’t re-write a CRC without erasing the entire sector, we don’t want to do that unless we have to. The flags for each block are currently allocated accordingly:
15
14
13
12
11
10
19
8
7
6
5
4
3
2
1
0
+0
Reserved
In use
When the sector is erased, all blocks show up as having all flags set as ones, so the flags is considered “inverted”. When we come to use a block, we mark the “in use” bit with a zero, leaving the rest as ones. When we erase, we mark the entire flags block as zeros. We can set other bits here as we need for accounting purposes.
Thus we have now a format for our flash sector header, and for our block headers. We can move onto the algorithm.
Step 3: The Code
This is the implementation of the above ideas. Our code needs to worry about 3 basic operations:
reading
writing
erasing
This is good enough if the size of a ROM image doesn’t change (normal case). For flexibility, I made my code so that it works crudely like a file, you can seek to any point in the ROM image and start reading/writing, or you can blow the whole thing away.
Constants
It is bad taste to leave magic numbers everywhere, so constants should be used to represent some quantities:
VROM_SECT_SZ=16384:
The virtual ROM sector size in bytes. (Those watching Codec2 Subversion will note I cocked this one up at first.)
VROM_SECT_CNT=3:
The number of sectors.
VROM_BLOCK_SZ=256:
The size of a block
VROM_START_ADDR=0x08004000:
The address where the virtual ROM starts in Flash
VROM_START_SECT=1:
The base sector number where our ROM starts
VROM_MAX_CYCLES=10000:
Our maximum number of program-erase cycles
Our programming environment may also define some, for example UINTx_MAX.
Derived constants
From the above, we can determine:
VROM_DATA_SZ = VROM_BLOCK_SZ – sizeof(block_header):
The amount of data per block.
VROM_BLOCK_CNT = VROM_SECT_SZ / VROM_BLOCK_SZ:
The number of blocks per sector, including the index block
VROM_SECT_APP_BLOCK_CNT = VROM_BLOCK_CNT – 1
The number of application blocks per sector (i.e. total minus the index block)
CRC32 computation
I decided to use the STM32’s CRC module for this, which takes its data in 32-bit words. There’s also the complexity of checking the contents of a structure that includes its own CRC. I played around with Python’s crcmod module, but couldn’t find some arithmetic that would allow it to remain there.
So I copy the entire block, headers and all to a temporary copy (on the stack), set the CRC field to zero in the header, then compute the CRC. Since I need to read it in 32-bit words, I pack 4 bytes into a word, big-endian style. In cases where I have less than 4 bytes, the least-significant bits are left at zero.
Locating blocks
We identify each block in an image by the ROM ID and the block index. We need to search for these when requested, as they can be located literally anywhere in flash. There are probably cleverer ways to do this, but I chose the brute force method. We cycle through each sector and block, see if the block is allocated (in the index), see if the checksum is correct, see if it belongs to the ROM we’re looking for, then look and see if it’s the right index.
Reading data
To read from the above scheme, having been told a ROM ID (rom), start offset and a size, the latter two being in byte sand given a buffer we’ll call out, we first need to translate the start offset to a sector and block index and block offset. This is simple integer division and modulus.
The first and last blocks of our read, we’ll probably only read part of. The rest, we’ll read entire blocks in. The block offset is only relevant for this first block.
So we start at the block we calculate to have the start of our data range. If we can’t find it, or it’s too small, then we stop there, otherwise, we proceed to read out the data. Until we run out of data to read, we increment the block index, try to locate the block, and if found, copy its data out.
Writing and Erasing
Writing is a similar affair. We look for each block, if we find one, we overwrite it by copying the old data to a temporary buffer, copy our new data in over the top then mark the old block as obsolete before writing the new one out with a new checksum.
Trickery is in invoking the wear levelling algorithm on an as-needed basis. We mark a block obsolete by setting its header fields to zero, but when we run out of free blocks, then we go looking for sectors that are full of obsolete blocks waiting to be erased. When we encounter a sector that has been erased, we write a new header at the start and proceed to use its first data block.
In the case of erasing, we don’t bother writing anything out, we just mark the blocks as obsolete.
Implementation
The full C code is in the Codec2 Subversion repository. For those who prefer Git, I have a git-svn mirror (yes, I really should move it off that domain). The code is available under the Lesser GNU General Public License v2.1 and may be ported to run on any CPU you like, not just ST’s.
I’ve been running a station from the bicycle for some time now and I suppose I’ve tried a few different battery types on the station.
Originally I ran 9Ah 12V gel cells, which work fine for about 6 months, then the load of the radio gets a bit much and I find myself taking two with me on a journey to work because one no longer lasts the day. I replaced this with a 40Ah Thundersky LiFePO4 pack which I bought from EVWorks, which while good, weighed 8kg! This is a lot lighter than an equivalent lead acid, gel cell or AGM battery, but it’s still a hefty load for a bicycle.
At the time that was the smallest I could get. Eventually I found a mob that sold 10Ah packs. These particular cells were made by LiFeBatt, and while pricey, I’ve pretty much recouped my costs. (I’d have bought and disposed of about 16 gel cell batteries in this time at $50 each, versus $400 for one of these.) These are what I’ve been running now since about mid 2011, and they’ve been pretty good for my needs. They handle the load of the FT-857 okay on 2m FM which is what I use most of the time.
A week or two back though, I was using one of these packs outside with the home base in a “portable” set-up with my FT-897D. Tuned up on the 40m WICEN net on 7075kHz, a few stations reported that I had scratchy audio. Odd, the radio was known to be good, I’ve operated from the back deck before and not had problems, what changed?
The one and only thing different is I was using one of these 10Ah packs. I’ve had fun with RF problems on the bicycle too. On transmit, the battery was hovering around the 10.2V mark, perhaps a bit low. Could it be the radio is distorting on voice peaks due to input current starvation? I tried after the net swapping it for my 40Ah pack, which improved things. Not totally cleared up, but it was better, and the pack hadn’t been charged in a while so it was probably a little low too.
The idea
I thought about the problem for a bit. SSB requires full power on voice peaks. For a 100W radio, that’s a 20A load right now. Batteries don’t like this. Perhaps there was a bit of internal resistance from age and the nature of the cells? Could I do something to give it a little hand?
Supercapacitors are basically very high capacity electrolytic capacitors with a low breakdown voltage, normally in the order of a few volts and capacitances of over a farad. They are good for temporarily storing charge that needs to be dumped into a load in a hurry. Could this help?
My cells are in a series bank of 4: ~3.3V/cell with 4 cells gives me 13.2V. There’s a battery balancer already present. If a cell gets above 4V, that cell is toast, so the balancer is present to try to prevent that from happening. I could buy these 1F 5.5V capacitors for only a few dollars each, so I thought, “what the hell, give it a try”. I don’t have much information on them other that Elna Japan made them. The plan was to make some capacitor “modules” that would hook in parallel to each cell.
My 13.2V battery pack, out of its case
Supercapacitors
For my modules, the construction was simple, two reasonably heavy gauge wires tacked onto the terminals, the whole capacitor then encased in heatshrink tubing and ring lugs crimped to the leads. I was wondering whether I should solder a resistor and diode in parallel and put that in series with the supercap to prevent high in-rush current, but so far that hasn’t been necessary.
The re-assembled pack
I’ve put the pack back together and so far, it has charged up and is ready to face its first post-retrofit challenge. I guess I’ll be trying out the HF station tomorrow to see how it goes.
Assembled pack
The Verdict
Not a complete solution to the RF feedback, it seems to help in other ways. I did a quick test on the drive way first with the standard Yaesu handmic and with the headset. Headset still faces interference problems on HF, but I can wind it up to about 30W~40W now instead of 20.
More pondering to come but we’ll see what the other impacts are.
Well, it’s been a year and a half since I last posted details about the bicycle mobile station. Shortly after getting the Talon on the road, setting it up with the top box and lighting, and having gotten the bugs worked out of that set-up, I decided to get a second mounting plate and set my daily commuter up the same way, doing away with the flimsy rear basket in place of a mounting plate for the top box.
VK4MSL/BM today after the trip home from work.
That particular bike people might recognise from earlier posts, it’s my first real serious commuter bike. Now in her 5th year, has done over 10200km since 2012. (The Talon has done 5643km in that time.) You can add to that probably another 5000km or so done between 2010 and 2012. It’s had a new rear wheel (a custom one, after having a spate of spoke breakages) and the drive chain has been upgraded to 9-speed. The latter upgrade alone gave it a new lease on life.
Both upgrades being money well spent, as was the upgrade to hydraulic brakes early in the bike’s lifetime. I suppose I’ve spent in those upgrades alone close to $1400, but worth it as it has given me quite good service so far.
As for my time with the top box. Some look at it and quiz me about how much weight it adds. The box itself isn’t that heavy, it just looks it. I can carry a fair bit of luggage, and at present, with my gear and tools in it it weighs 12kg. Heavy, but not too heavy for me to manage.
Initially when I first got it, it was great, but then as things flexed over time, I found I was intermittently getting problems with the top box coming off. This cost me one HF antenna and today, the top box sports a number of battle-scars as a result.
The fix to this? Pick a spot inside the top box that’s clear of the pannier rack rails and the rear tyre, and drill a 5mm hole through. Then, when you mount the top box, insert an M5 bolt through the mounting plate and into the bottom of the top box and tighten with a 5mm wing nut. The box now will not come loose.
Still lit up like a Christmas tree from this morning’s ride.
The lights still work, and now there’s a small rear-view camera. On the TODO list is to rig up a 5V USB socket to power that camera with so that it isn’t draining the rather small internal battery (good for 4 hours apparently).
The station has had an upgrade, I bought a new LDG Z-100Plus automatic tuner which I’m yet to fully try out. This replaces the aging Yaesu FC-700 manual tuner which, although very good, is fiddly to use in a mobile set-up and doesn’t tune 6m easily.
One on-going problem now not so much on the Boulder but on the Talon is an issue with pannier racks failing. This started happening when I bought the pannier bags, essentially on the side I carry my battery pack (2kg), I repeatedly get the pannier rack failing. The racks I use are made by Topeak and have a built-in spacer to accomodate disc brake calipers. This seems to be a weak spot, I’ve now had two racks fail at about the same point.
Interestingly on the Boulder which also has disc brakes, I use the standard version of the same rack, and do not get the same failures. Both are supposedly rated to 25kg, and my total load would be under 16kg. Something is amiss.
A recurring flaw with the Topeak racks
I’m on the look-out for a more rugged rack that will tolerate this kind of usage, although having seen this, I am inspired to try a DIY solution. Then if a part fails, I can probably get replacement parts in any typical hardware store. A hack saw and small drill are not heavy items to carry, and I can therefore get myself out of trouble if problems arise.
I’ve been riding on the road now for some years, and while I normally try to avoid it, I do sometimes find myself riding on the road itself rather than on the footpath or bicycle path.
Most of the time, the traffic is fine. I’m mindful of where everyone is, and there aren’t any problems, but I have had a couple of close calls from time to time. Close calls that have me saying “ode for a horn”.
By law we’re required to have a bell on our bikes. No problem there, I have a mechanical one which is there purely for legal purposes. If I get pulled over by police, and they ask, I can point it out and demonstrate it. Requirement met? Tick to that.
It’s of minimal use with pedestrians, and utterly useless in traffic.
Early on with my riding I developed a lighting system which included indicators. Initially this was silent, I figured I’d see the lights flashing, but after a few occasions forgetting to turn indicators off, I fitted a piezo buzzer. This was an idea inspired by the motorcycles ridden by Australia Post contractors, which have a very audible buzzer. Jaycar sell a 85dB buzzer that’s waterproof, overkill in the audio department but fit for purpose. It lets me know I have indicators on and alerts people to my presence.
That is, if they equate the loud beep to a bicycle. Some do not. And of course, it’s still utterly useless on the road.
I figured a louder alert system was in order. Something that I could adjust the volume on, but loud enough to give a pedestrian a good 30 seconds warning. That way they’ve got plenty of time to take evasive action while I also start reducing speed. It’s not that I’m impatient, I’ll happily give way, but I don’t want to surprise people either. Drivers on the other hand, if they do something stupid it’d be nice to let them know you’re there!
My workplace looks after a number of defence bases in South-East Queensland, one of which has a railway crossing for driver training. This particular boom gate assembly copped a whack from a lightning strike, which damaged several items of equipment, including the electronic “bells” on the boom gate itself. These “bells” consisted of a horn speaker with a small potted PCB mounted to the back which contained an amplifier and bell sound generator. Apply +12V and the units would make a very loud dinging noise. That’s in theory; in practise, all that happened was a TO-220 transistor got hot. Either the board or the speaker (or both) was faulty.
It was decided these were a write-off, and after disassembly I soon discovered why: the voice coils in the horn speakers had been burnt out. A little investigation, and I figured I could replace the blown out compression drivers and get the speakers themselves working again, building my own horn.
A concept formed: the horn would have two modes, a “bell” mode with a sound similar to a bicycle bell, and a “horn” mode for use in traffic. I’d build the circuit in parts, the first being the power amplifier then interface to it the sound effect generator.
To make life easier testing, I also decided to add a line-in/microphone-in feature which would serve to debug construction issues in the power amplifier and add a megaphone function. (Who knows, might be handy with WICEN events.)
Replacing the compression drivers
Obviously it’d be ideal to replace it with the correct part, but looking around, I couldn’t see anything that would fit the housing. That, and what I did see, was more expensive than buying a whole new horn speaker.
There was a small aperture in the back about 40mm in diameter. The original drivers were 8ohms, and probably rated at 30W and had a convex diaphragm which matched the concave geometry in the back of the horn assembly.
Looking around, I saw these 2W mylar cone speakers. Not as good as a compression driver, but maybe good enough? It was cheap enough to experiment. I bought two to try it out.
I got them home, tacked some wires onto one of them and plugged it into a radio. On its own, not very loud, but when I held it against the back of a horn assembly, the amplification was quite apparent. Good enough to go further. I did some experiments with how to mount the speakers to the assembly, which required some modifications to be made.
I soon settled on mounting the assembly to an aluminium case with some tapped holes for clamping the speaker in place. There was ample room for a small amplifier which would be housed inside the new case, which would also serve as a means of mounting the whole lot to the bike.
Bell generator
I wasn’t sure what to use for this, I had two options: build an analogue circuit to make the effect, or program a microcontroller. I did some experiments with an ATMega8L, did manage to get some sound out of it using the PWM output, but 8kB of flash just wasn’t enough for decent audio.
A Freetronics LeoStick proved to be the ticket. 32kB flash, USB device support, small form factor, what’s not to like? I ignored the Arduino-compatible aspect and programmed the device directly. Behind the novice-friendly pin names, they’re an ATMega32U4 with a 16MHz crystal. I knocked up a quick prototype that just played a sound repeatedly. It sounded a bit like a crowbar being dropped, but who cares, it was sufficient.
Experimenting with low-pass filters I soon discovered that a buffer-amp would be needed, as any significant load on the filter would render it useless.
A 2W power amplifier
Initially I was thinking along the lines of a LM386, but after reading the datasheet I soon learned that this would not cut it. They are okay for 500mW, but not 2W. I didn’t have any transistors on hand that would do it and still fit in the case, then I stumbled on the TDA 1905. These ICs are actually capable of 5W into 4 ohms if you feed them with a 14V supply. With 9V they produce 2.5W, which is about what I’m after.
I bought a couple then set to work with the breadboard. A little tinkering and I soon had one of the horn speakers working with this new amplifier. Plugged into my laptop, I found the audio output to be quite acceptable, in fact turned up half-way, it was uncomfortable to sit in front of.
I re-built the circuit to try and make use of the muting feature. For whatever reason, I couldn’t get this to work, but the alternate circuit provided a volume control which was useful in itself.
The pre-amplifier
For the line-level audio, there’s no need for anything more fancy than a couple of resistors to act as a passive summation of the left and right channels, however for a microphone and for the LeoStick, I’d need a preamp. I grabbed a LM358 and plugged that into my breadboard alongside the TDA1905.
Before long, I had a working microphone preamp working using one half of the LM358, based on a circuit I found. I experimented with some resistor values and found I got reasonable amplification if I upped some of the resistor values to dial the gain back a little. Otherwise I got feedback.
For the LeoStick, it already puts out 5V TTL, so a unity-gain voltage follower was all that was needed. The second half of the LM358 provided this. A passive summation network consisting of two resistors and DC-blocking capacitor allowed me to combine these outputs for the TDA1905.
One thing I found necessary, the TDA1905 and LM358 misbehave badly unless there’s a decent size capacitor on the 9V rail. I found a 330uF electrolytic helped in addition to the datasheet-prescribed 100nF ceramics.
Power supply
Since I’m running on batteries with no means of generating power, it’s important that the circuit does not draw power when idle. Ideally, the circuit should power on when either I:
plug the USB cable in (for firmware update/USB audio)
toggle the external source switch
press the bell button
We also need two power rails: a 9V one for the analogue electronics, and a 5V one for the LeoStick. A LM7809 and LM7805 proved to be the easiest way to achieve this.
To allow software control of the power, a IRF9540N MOSFET was connected to the 12V input and supplies the LM7809. The gate pin is connected to a wired-OR bus. The bell button and external source switch connect to this bus with signal diodes that pull down on the gate.
Two BC547s also have collectors wired up to this bus, one driven from the USB +5V supply, and the other from a pin on the LeoStick. Pressing the Bell button would power the entire circuit up, at which point the LeoStick would assert its power on signal (turning on one of the BC547s) then sample the state of the bell button and start playing sound. When it detects the button has been released, it finishes its playback and turns itself off by releasing the power on signal.
Sound effect generator
Earlier I had prototyped a bell generator, however it wasn’t much use as it just repeatedly made a bell noise regardless of the inputs. To add insult to injury, I had lost the source code I used. I had a closer look at the MCU datasheet, deciding to start from a clean slate.
The LeoStick provides its audio on pin D11, which is wired up to Port B Pin 7. Within the chip, two possible timers hook up: Timer 0, which is an 8-bit timer, and Timer 1, which is 16-bits. Both are fed from the 16MHz system clock. The bit depth affects the PWM carrier frequency we can generate, the higher the resolution, the slower the PWM runs. You want the PWM frequency as high as possible, ideally well above 20kHz so that it’s not audible in the audio output, and obviously well above the audio sampling rate.
At 16MHz, a 16-bit timer would barely exceed 240Hz, which is utterly useless for audio. A 10-bit timer fares better, with 15kHz, older people may not hear it but I certainly can hear 15kHz. That leaves us with 8-bits which gets us up to 62kHz. So no point in using Timer 1 if we’re only going to be using 8-bits of it, we might as well use Timer 0.
Some of you familiar with this chip may know of Timer 4, which is a high-speed 10-bit timer fed by a separate 64MHz PLL. It’s possible to do better quality audio from here, either running at 10-bits with a 62kHz carrier, or dropping to 8-bits and ramping the frequency to 250kHz. Obviously it’d have been nice, but I had already wired things up by this stage, so it was too late to choose another pin.
Producing the output voltage is only half the equation though: once started, the PWM pin will just output a steady stream of pulses, which when low-passed, produces a DC offset. In order to play sound, we need to continually update the timer’s Capture Compare register with each new sample at a steady rate.
The most accurate way to do this, is to use another timer. Timer 3 is another 16-bit timer unit, with just one capture compare output available on Port C pin 3. It is an ideal candidate for a timer that has no external influence, so it gets the job of updating the PWM capture compare value with new samples.
Timer 1 is connected to pins that drive two of the three LEDs on the LeoStick, with Timer 4 driving the remaining one, so if I wanted, I could have LEDs fade in and out with it instead of just blinking. However, my needs are basic, and I need something to debounce switches and visibly blink LEDs. So I use that with a nice long period to give me a 10Hz timer.
Here is the source code. I’ll add schematics and other notes to it with time, but the particular bits of interest for those wanting to incorporate PWM-generated sound in their AVR projects are the interrupt routine and the sound control functions.
To permit gapless playback, I define two buffers which I alternate between, so while one is being played back, the other can be filled up with samples. I define these on line 139 with the functions starting at line 190. The interrupt routine that orchestrates the playback is at line 469.
When sound is to be played, the first thing that needs to happen is for the initial buffer to be loaded with samples using the write_audio function. This can either read from a separate buffer in RAM (e.g. from USB) or from program memory. One of the options permits looping of audio. Having loaded some initial data in, we can then call start_audio to set up the two timers and get audio playback rolling. start_audio needs the sample rate to configure the sample rate timer, and can accept any sample rate that is a factor of 16MHz (so 8kHz, 16kHz up to 32kHz).
The audio in this application is statically compiled in, taking the form of an array of uint8_t‘s in PROGMEM.
Creating the sounds
I initially had a look around to see if I could get a suitable sound effect. This proved futile, I was ideally looking around for a simple openly-licensed audio file. Lots of places offered something, but then wanted you to sign up or pay money. Fine, I can understand the need to make a quid, and if I were doing this a lot, I’d pay up, but this is a once-off.
Eventually, I found some recordings which were sort of what I was after, but not quite. So I downloaded these then fired up Audacity to have a closer look.
The bicycle bell
Bicycle bells have a very distinctive sound to them, and are surprisingly complicated. I initially tried to model it as an exponentially decaying sinusoid of different frequencies, but nothing sounded quite right.
The recording I had told me that the fundamental frequency was just over 2kHz. Moreover though, the envelope was amplitude-modulated by a second sinusoid: this one about 15Hz. Soon as I plugged this second term in, things sounded better. This script, was the end result. The resulting bell sounds like this:
So somewhat bell-like. To reduce the space, I use a sample rate of 6.4kHz. I did try a 4kHz sample rate but was momentarily miffed at the result until I realised what was going on: the bell was above the Nyquist frequency at 4kHz, 6.4kHz is the minimum practical rate that reproduces the audio.
I used Audacity to pick a point in the waveform for looping purposes, to make it sound like a bell being repeatedly struck.
The horn
I wanted something that sounded a little gutsy. Like an air-horn on a truck. Once again, I hit the web, and found a recording of a train horn. Close enough, but not long enough, and a bit noisy. However opening it up in Audacity and doing a spectrum analysis, I saw there were about 5 tones involved. I plugged these straight into a Python script and decided to generate those directly. Using a raised cosine filter to shape the envelope at the start and end, and I soon had my horn effect. This script generates the horn. The audio sounds like this:
Using other sound files
If you really wanted, you could use your own sound recordings. Just keep in mind the constraints of the ATMega32U4, namely, 32kB of flash has to hold both code and recordings. An ATMega64 would do better. The audio should be mono, 8-bits and unsigned with as lower sample rate as you can get away with. (6.4kHz proved to be sufficient for my needs.)
Your easiest bet would be to either figure out how to read WAV files (in Python: wave module), or alternatively, convert to raw headerless audio files, then code up a script that reads the file one byte at a time. The Python scripts I’ve provided might be a useful starting point for generating the C files.
Alternatively, you can try interfacing a SDCard and embedding a filesystem driver and audio file parser (not sure about WAVE but Sun Audio is easily parsed), this is left as an exercise for the adventurous.
Finishing up
I’ll put schematics and pictures up soonish. I’m yet to try mounting the whole set up, but so far the amplifier is performing fine on the bench.
Well, for a few weeks now I’ve been (metaphorically) tearing my hair out, trying to figure out why I had such bad antenna problems on VHF. HF, I still have work to do as right now, the RF just induces currents where it pleases, including in my microphone cabling, but that’s a different matter. VHF until recently had been rock solid.
I tried replacing coax, re-terminating leads, checking solder joints, replacing antennas. Yesterday, I re-wired the entire antenna system, doing away with the BNC connectors in the top box and hard-wiring the antenna mounts to the coax inside. Rode up to Ashgrove today thinking I had fixed the problem.
Nope.
Each bump in the road, I watched the S-meter graph move from S9 to S4 and back again.
What could it be? Why is it that it only occurs when I’m mobile, and not stationary? There’s a bad link somewhere, but where? No amount of jiggling cables was locating the problem. Finally today, I decided to take a peek inside the FT-857D.
Ohh, well that would do it!
I looked closely at the point where the N connector solders to the PCB. I noticed a small line around the wire where it met the solder blob. So I picked at it with pliers, and pulled it away from the blob: it was a dry joint!
Tomorrow, I shall know if this was the final problem. At least I don’t run full power on FM, my license only affords me 30W continuous, so the only time I do 50W is when I’m doing SSB at which point it’s only on voice peaks.
Update: It’s been a few days, the difference is like the difference between chalk and cheese. Prior to the fix my set was deaf as a post, and it’s not hard to see why!
Recent Comments