Having had some success getting Arduinos (and bare AVR mega chips) to function as KISS TNCs, I thought I would try to achieve the same functionality using a Parallax Propeller board.
I came across the QuickStart Propeller board from Parallax at my local RadioShack recently, $40 plus tax. The board has no jack for external DC, but does have a mini-USB jack for power and data, and a nice fat pin header with every Prop pin accessible. I had been curious about the Propeller for some time, and this seemed like an easy way to get started.
As a first attempt at AX.25 on the little board, I thought I would try out the "Bell 202 Modem" from the Object Exchange. I was MIGHTY disappointed to find that it is written not for the non-return-to-zero-inverted format used by AX.25 (and thus APRS), but for old school 1200b telephone modems (kinda like the title of the object). Undeterred, I found the APRS project in the OBEX. Unfortunately that one is TX-only, though the author did modify the Bell 202 code to speak genuine AX.25. I fiddled around with the assembler code unsuccessfully for a while before wandering off in dismay.
Back to square one, I decided to try porting the receive code from my previous Arduino TNC project (lucky version 13). I used Prop-GCC in order to stick as closely to the original C code as possible. The results of those adventures follow.
First, though, here are some...
Lessons learned in the course of Propeller development
Some critically useful links:
Anyway, let's get back to....
Progress creating a functional TNC
I still suspect that the 1-bit ADC is causing problems, so I took a side trip through the land of Propeller ADCs. I was mighty disappointed to find that the classic Sigma-Delta ADC described in their literature in a non-starter when you build your circuits with breadboards (as described above). I had given some thought to a very different ADC design, and gave it a whirl.
The Propeller has no mixed-signal functionality - it is a digital-only device. To make it work with analog signals, you can either add some extra chips (an external ADC, perhaps), or you can work within the context of the binary nature of the Prop's inputs. In my case, all of my work with the Propeller up to today has involved using an input pin (binary!) to read the analog signal. The behavior of the pins is that a signal above (approximately) half of the supply voltage (3.3v / 2 = 1.67ish volts) is considered to be a logic "high", or a binary 1. Any signal below Vdd / 2 is considered a logic "low", or binary 0. If you take an analog signal like the audio from a VHF receiver, de-couple it from DC (to get just the AC audio signal), then bias that to Vdd/2 with a voltage divider, you are left with a DC waveform that crosses over the Propeller pin's logic threshold twice per audio waveform. The Prop reads this as a series of 0s and 1s that corresponds to the original audio. This is the "one-bit ADC" that I have been referring to (and suspicious of). The Sigma-Delta design described by Parallax also makes use of this logic threshold at Vdd/2 as an integral part of their design, albeit in a far more sophisticated way.
In trying to re-imagine an ADC for the Prop, it was clear that I would need to make use of this same behavior. Instead of biasing the de-coupled audio to Vdd/2, I thought, I could instead ramp up a bias voltage from zero to Vdd in small steps, and use the logic state change to tell me when the audio signal plus my bias voltage had reached Vdd/2. I would then know what the incoming signal was at that moment by how far up this ramp I had to climb to hit Vdd/2.
Once of my primary design concerns was that the entire ADC operation had to function well below the VHF frequency of the official Sigma-Delta design (80 MHz). I knew from working on the TX section of the Arduino-based TNC that I could easily build a four-bit waveform generator using only digital output pins. That only gives 16 discreet output voltages, but it would be a familiar place to start. That would give me 16 values-per-sample * 13200 samples/sec, or an operational frequency of just 211.2 kHz - a tiny fraction of the unstable VHF of Parallax's design. In fact, that left room for me to double the ramp-up frequency to 422.4 kHz, so that I could perform all of the ramp-up during the first half of each sample time, so that the sample would be read at more precise intervals.
Here's what all of this looks like in practice:
So how does it play? I'm reading data bits over the air at a furious pace ("test4b.c" is attached below, but is very untidy). Even after all of this work, though, I still seem to be haunted by clock recovery issues.
Getting better (Test4a.c, below), but still having a lot of trouble with timing. I am not sure if the issue is one of having a crappy algorithm (always a possibility), bugs bugs bugs (I keep making dumb mistakes), or the 1-bit ADC. Since the ADC has only two phase states, I fear it may be more susceptible to large phase errors, which present themselves as jitter in the symbol detector. I spent a couple of days trying to get the standard ADC circuit working, only to discover (after much too much time) that the standard ADC cannot function reliably on a breadboard.
Some additional file attachments - a version of the test program (test4_pc.c) adapted to compile and run on a PC (I've been using TCC with great results), and a couple of WAV files with APRS traffic, down-sampled to 13.2KHz 16 bit mono. The "wav2dense.pl" script converts these to a file of ASCII ones and zeros, which are read by the PC version of the test program. All of this is to allow me to debug the DSP code without the timing and bandwidth limitations of trying to spit out serial data while keeping correct timing on the Prop at the same time.
The Prop code runs at about 20 MIPS, but actually seems a lot slower. I've had a lot of trouble with clock over-runs. I had to break the code into sections in order to get the ADC sampler routine down around 4000 clock ticks per sample. That piece runs in a separate COG. I'm using a digital input pin as a one-bit ADC - the audio input is biased to half of the regulated 3.3V rail, so downswings in the audio voltage make the pin read 0, and upswings read as 1. It works a lot better than I would have suspected. I'm clocking in bits furiously with open-squelch audio, though decode performance still has a ways to go. The code attached below (Test2.1.c) is maybe 25% functional as an RX-only KISS TNC as-is. I think my clock management code got screwed up in the transition to the multi-COG design, but that should be fixable.
I might also try re-writing this new version of the TNC code in Spin, just for kicks. No idea if it would be fast enough, but the COG management in Spin is a little more intuitive. I could probably spread the code across 3 or 4 COGS to mitigate slower program execution.