Bell 202, 1200 baud Demodulator in an ATTiny10

I got my Radio Amateur's (HAM) license a few years back because I wanted to experiment with radio telemetry for my rocketry and high altitude balloon projects.  After running some tests using XBee modules, I decided that the Amateur Packet Reporting System (APRS) offered potentially longer range and would be more suitable for HA balloon projects.  There are already quite a few commercially available products that work with APRS but, being a do it myself kind of guy, I wanted to build my own.  Also, given that the loss of a rocket, or balloon is quite a high probability, the roll my own approach also offer the potential for lower costs in the case of lost, or destroyed telemetry equipment.  

My first project was to try and build an APRS decoding system I could hook to my handheld transceiver in order to monitor local APRS transmissions.  However, I quickly discovered that many of the designs I could find online were based on parts, such as the MX614, that were either obsolete, or hard to find (expensive.)  Modern systems, it seems, often use a software-only solution that uses DSP-based software running on a laptop in combination with a sound input, or sound card to decode the Bell 202-style 1200 baud AFSK signals that APRS most commonly uses for modulation and demodulation.  I was hoping to build something far more compact, so I began to wonder whether I could implement Bell 202 modulation and demodulation on a cheap micro controller chip.

Then, I remembered reading an article many years ago in Circuit Cellar magazine, about a guy that built his own digital answering machine.  One feature that stuck out in my memory was that his design implemented Caller ID using a software-base modem.  Caller ID is also based on the Bell 202, 1200 baud modulation scheme, so I reasoned this could be adapted to my purposes.  I tracked down a copy of the article, here, and discovered that he based his design on a German author's work (which you can find, here) that was originally intended for HAM Radio packet communication, so it seemed even more appropriate to adapt this design to my purposes.

The approach used is to sample the incoming FSK signal at 9600 samples/second (8 times the 1200 Hz frequency used in Bell 202 modulation) and pass this through two digital filters, one tuned to 1200 Hz and the other to 2200 Hz.  The secret of the demodulation scheme is to implement the digital filters using tables of Sine and Cosine values for each frequency.  The following bit of Java code shows the basic technique.  The data[] array passed to demodulate() contains audio FSK at 9600 Bps and the function is called for index = 0 through data.length - 8.  After stepping though at least 8 samples, the value returned from demodulate() will be >0 if it has demodulated a 2200 Hz tone, or < 0 for a 1200 Hz tone.

  private static byte[]   coeffloi = { 64,  45,   0, -45, -64, -45,   0,  45};
  private static byte[]   coeffloq = {  0,  45,  64,  45,   0, -45, -64, -45};
  private static byte[]   coeffhii = { 64,   8, -62, -24,  55,  39, -45, -51};
  private static byte[]   coeffhiq = {  0,  63,  17, -59, -32,  51,  45, -39};
  private static int demodulate (byte[] data, int idx) {
    short outloi = 0, outloq = 0, outhii = 0, outhiq = 0;
    for (int ii = 0; ii < 8; ii++) {
      byte sample = data[ii + idx];
      outloi += sample * coeffloi[ii];
      outloq += sample * coeffloq[ii];
      outhii += sample * coeffhii[ii];
      outhiq += sample * coeffhiq[ii];
    return (outhii >> 8) * (outhii >> 8) + (outhiq >> 8) * (outhiq >> 8) -
           (outloi >> 8) * (outloi >> 8) - (outloq >> 8) * (outloq >> 8);

To try out the approach, I wrote a Java application that used the code above to demodulate a test file of Bell 202-modulated data saved at 9600 samples/second.  This worked surprisingly well, so I decided to see if I could adapt this code to run on a cheap micro controller.  After some experimentation using my Java test application, I discovered that because the multiplications were all against constant values, they could easily be replaced with shifts and adds, which meant I did not need a micro controller with a hardware multiplier.  So, out of a perverse desire to see how far I could push this idea, I decided on the ATTiny10 as my target micro controller.  

By using careful register management, and by unrolling the central loop, I was able to avoid loading the sin/cos values from a table.  Instead, I wrote out the entire loop as one long series of sift, add/sub, move, etc. instructions.  To get an idea how this works, here's the first iteration of the central loop in unrolled form:

; Bell 202 Demodulation code
    ld  srcl,Z+         ; src = data[idx++];
    and zl, mask
    clr srch
    subi srcl,128
    sbci srch,0
    lsl srcl            ; src <<= 1;
    rol srch
    lsl srcl            ; src <<= 1;
    rol srch
    lsl srcl            ; src <<= 1;
    rol srch
    lsl srcl            ; src <<= 1;
    rol srch
    lsl srcl            ; src <<= 1;
    rol srch
    lsl srcl            ; src <<= 1;
    rol srch
    mov cof1l,srcl      ; cof1 = src;
    mov cof1h,srch
    clr cof2l           ; cof2 = 0;
    clr cof2h
    mov cof3l,srcl      ; cof3 = src;
    mov cof3h,srch
    clr cof4l           ; cof4 = 0;
    clr cof4h

This type of coding continues for the remaining 7 iterations of the loop, but different combinations of shifts, adds and subtracts are used to multiply by different coefficients.  Rather than repeat all this here, I refer you to complete source code, which you can download from the demod6.asm link at the bottom of this page.

Bell 202 Modulation

To complement my demodulation code, I wrote companion Bell 202 modulator code, also for the ATTiny10, which you can download from the modulate.asm link.  This code is simpler than the demodulation code in that it simply uses a sine table to generate an FSK signal, then outputs it using the ATTiny10's Pulse Width Modulation (PWM) circuitry to generate the output signal.  However, this output will need to be low pass filtered with a simple RC circuit before being fed into the demodulator.  Here's how to connect the two devices for testing purposes:

Where the values of R and C are chosen to cutoff frequencies above 2200 Hz.  For example, a value of .001 uFd for C, and 36K for R should be a suitable start.  Note: in the above circuit, the PWM output from the modulator will average out to a sine wave signal that varies up and down from an average value of 1/2 VCC, or 2.5 volts.  When connecting the demodulator to an AC signal, it's important to remember to bias this signal so it varies around this same 2.5 volt baseline.  Simply connecting an AC signal input to pin 6 of the demodulator through a capacitor will not work.

Wayne Holder,
Jul 7, 2012, 11:21 PM
Wayne Holder,
Jul 7, 2012, 11:24 PM