This is a prototype for a high altitude balloon project I worked on with the Purdue Orbital rocketry team. I designed this board to test the range and power draw of our comms system and threw on a relay and microcontroller so that it can be used for triggering things on a real flight. One of the FAA requirements is that balloons over a certain weight have four cutdown systems (two on the balloon envelope and two on the payload tether) and that these systems must be independent, meaning they are electrically disconnected from each other and have separate batteries. Many amateurs balloonists overlook this rule, but due to our close work with the FAA it is important that we follow the regulations.
APRS and Modem selection
To save on cost, I avoided the higher end radio specific devices on the market and stuck to hobby-tier components. First was the Baofeng DRA818V. This is cheap Chinese VHF transceiver that costs less than $20 on Amazon and has a decent amount of power. Some users claim that these devices bleed onto other channels and should have a lowpass filter on the output (mine is inline with the coax). There is also a UHF band available, but I chose VHF for potential future compatibility with the APRS system. APRS will be an essential part of the system, because most radios in this power level (0.5 - 1W transmission) can only achieve a few dozen miles tops transmitting from ground to balloon. The incorporation of APRS will greatly extend this range by taking advantage of amateur repeating stations called digipeaters. Since this radio module is for handheld units that transmit audio, I needed some sort of modem for converting data to an analog format and vice-versa. One of the methods of modulation I investigated was DTMF, which is used commonly in amateur radio and is built in to many consumer units. However, DTMF modems tend to be rather large pin-wise and aren't compatible with most digipeaters, so I decided to build one from scratch using my favorite microcontroller, the ATTiny85. For starters, I need to modulate and demodulate incoming data (known as symbols in communications) so they can be transmitted over the air. In this case, most digipeaters use a standard known as Bell-202 (also known as AFSK-1200), which is a simple modulation scheme where a logic 1 (mark) is represented by a 1200 Hz tone and a logic 0 (space) is represented by a 2200 Hz tone. I accomplished this by calculating an 8-point DTFT at 1200 Hz and 2200 Hz at a sample rate of 9600 Hz (determined through testing). I didn't use the FFT here because I'm only interested in the spectral power at two points. Once I generated my DTFT coefficients, I used the Q number format, which is a method of handling signed fractional numbers within fixed point datatypes. I wrote a bit of code to do the conversion.
#!/bin/octave## Evan Widloski - 2016-10-15## calculate DTFT coefficients and express as binary fractions## ----- Convert decimal numbers to n bit signed binary fractions -----functionout =dec2binfrac(x,n)## round input array to nearest 1/(2^n)x=round(x*2^n)/(2^n);k=[1:n-1];if(x<0)x=1+x;out=-2^(n-1);elseout=0;endiftwos_complement=mod(abs(x),.5.^(k-1))>=.5.^k;bin_values=2.^[n-2:-1:0];out+=sum(bin_values.*twos_complement);endfunction## ----- Create coefficients for implementation in C -----N=8## generate DFT coefficientsXd1=e.^(-i*2*pi*(1200/9600)*[0:N-1]);Xd2=e.^(-i*2*pi*(2200/9600)*[0:N-1]);## scale down coefficients by 1 bit, since `1` can't be expressed as binary fraction## e.g. scale 1 to 127/128Xd1=Xd1*((2^(N-1)-1)/2^(N-1));Xd2=Xd2*((2^(N-1)-1)/2^(N-1));## express coefficients as signed N-bit binary fractions (Q7)arrayfun(@(x)dec2binfrac(x,N),real(Xd1))arrayfun(@(x)dec2binfrac(x,N),imag(Xd1))arrayfun(@(x)dec2binfrac(x,N),real(Xd2))arrayfun(@(x)dec2binfrac(x,N),imag(Xd2))
This yielded 4 arrays which would calculate the DFT at 1200 and 2200 Hz with a 9600 Hz sample rate.
Before implementing my code on the ATTiny, I wanted to do some verification and evaluate performance. Fortunately, Octave has a C/C++ api which allowed me to test my code with the same bit-precision as I would have on the microcontroller. Below you can see the 4 arrays of DTFT coefficients (generated previously) in Q.7 number format (1 sign bit, 7 data bits).
Initially, I generated my test signals incoherently, meaning that the phase resets each time the signal transitions from a mark to a space and vice versa. This creates discontinuities and adds noise to the spectrum in the transition regions. From the results below, it was clear that my implementation really needed to be coherent.
At this point, the modem had no awareness of bits. The modem simply modulated at 1200 or 2200 Hz if it saw a logic 1 or 0 and vice versa for demodulation. If you look carefully at the thresholded output, you'll notice that the widths of the pulses are not exactly the same. This was problematic because UARTs operate asynchronously (without a clock line) and depend on the timing and width of bits to be precise. To solve this problem, I wrote some simple code that detects the phase of the thresholded output and samples it at regular intervals to ensure that the UART is receiving equally spaced bits.
The algorithm waits for a quiet period (no data being transmitted), then synchronizes on the first bit edge it sees. So long as there are breaks between transmitted packets, the algorithm works well enough to clean up the incoming bit stream.
However, there was one more problem. The ATTiny85 is a fairly underpowered device, and with all the multiplications I was doing, I was using up most of the sample period performing computations. With the sample period now cut in half, there wasn't enough time to finish the DFT calculation before the next sample came in. My first thought to a solution was to simply use an external 16 MHz crystal rather than the built in 8 MHz oscillator. However After some research, I learned I could use the ATTiny's PLL as a clock source to double the frequency of the built in oscillator and achieve the desired 16 MHz. This was just a new target in my makefile: