altos: Get CC115L radio working.
[fw/altos] / src / drivers / ao_aprs.c
index 79cea49afbaa87028986e39e03a694a69f81964f..6ab61e6a3018f9d945bce57be25eb5176e251a3c 100644 (file)
  *
  */
 
-// Hardware specific configuration.
-#include <18f2525.h>
-#device ADC=10
+#ifndef AO_APRS_TEST
+#include <ao.h>
+#endif
 
-// NOTE: Even though we are using an external clock, we set the HS oscillator mode to
-//       make the PIC 18F252 work with our external clock which is a clipped 1V P-P sine wave.
-#fuses HS,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP
+#include <ao_aprs.h>
 
-// C runtime library definitions.
-#include 
-#include 
-
-// These compiler directives set the clock, SPI/I2C ports, and I/O configuration.
-
-// TCXO frequency
-#use delay(clock=19200000)
-
-// Engineering and data extracation port.
-#use rs232(baud=57600, xmit=PIN_B7, rcv=PIN_B6, STREAM=PC_HOST)
-
-// GPS engine 
-#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
-
-#use i2c (master, scl=PIN_C3, sda=PIN_C4)
-
-#use fast_io(A)
-#use fast_io(B)
-#use fast_io(C)
-
-// We define types that are used for all variables.  These are declared
-// because each processor has a different sizes for int and long.
-// The PIC compiler defines int8_t, int16_t, and int32_t.
-
-/// Boolean value { false, true }
-typedef boolean bool_t;
-
-/// Signed 8-bit number in the range -128 through 127.
-typedef signed int8 int8_t;
-
-/// Unsigned 8-bit number in the range 0 through 255.
-typedef unsigned int8 uint8_t;
-
-/// Signed 16-bit number in the range -32768 through 32767.
-typedef signed int16 int16_t;
-
-/// Unsigned 16-bit number in the range 0 through 65535.
-typedef unsigned int16 uint16_t;
-
-/// Signed 32-bit number in the range -2147483648 through 2147483647.
-typedef signed int32 int32_t;
-
-/// Unsigned 32-bit number in the range 0 through 4294967296.
-typedef unsigned int32 uint32_t;
-
-// Function and structure prototypes.  These are declared at the start of
-// the file much like a C++ header file.
-
-// Map I/O pin names to hardware pins.
-
-/// Heartbeat LED - Port A2
-#define IO_LED PIN_A2
-
-/// AD9954 DDS Profile Select 0 - Port A3
-#define IO_PS0 PIN_A3
-
-/// UHF amplifier and PA chain - Port A4
-#define IO_PTT PIN_A4
-
-/// AD9954 DDS Update - Port A5
-#define IO_UPDATE PIN_A5
-
-/// AD9954 CS (Chip Select) - Port B0
-#define IO_CS PIN_B0
-
-/// GPS Engine Power - Port B1
-#define IO_GPS_PWR PIN_B1
-
-/// AD9954 DDS Profile Select 1 - Port C0
-#define IO_PS1 PIN_C0
-
-/// AD9954 DDS OSK (Output Shift Key) - Port C2
-#define IO_OSK PIN_C2
-
-/// GPS engine serial transmit pin - Port C6
-#define IO_GPS_TXD PIN_C6
-
-// Public methods, constants, and data structures for each class.
-
-/// Operational modes of the AD9954 DDS for the ddsSetMode function.
-enum DDS_MODE
-{
-    /// Device has not been initialized.
-    DDS_MODE_NOT_INITIALIZED,
-
-    /// Device in lowest power down mode.
-    DDS_MODE_POWERDOWN,
-
-    /// Generate FM modulated audio tones.
-    DDS_MODE_AFSK,
-
-    /// Generate true FSK tones.
-    DDS_MODE_FSK
-};
-
-void ddsInit();
-void ddsSetAmplitude (uint8_t amplitude);
-void ddsSetOutputScale (uint16_t amplitude);
-void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1);
-void ddsSetFreq (uint32_t freq);
-void ddsSetFTW (uint32_t ftw);
-void ddsSetMode (DDS_MODE mode);
-
-void flashErase();
-uint8_t flashGetByte ();
-void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length);
-void flashSendByte(uint8_t value);
-void flashSendAddress(uint32_t address);
-void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length);
-
-/// Type of GPS fix.
-enum GPS_FIX_TYPE 
-{
-    /// No GPS FIX
-    GPS_NO_FIX,
-
-    /// 2D (Latitude/Longitude) fix.
-    GPS_2D_FIX,
-
-    /// 3D (Latitude/Longitude/Altitude) fix.
-    GPS_3D_FIX
-};
-
-/// GPS Position information.
-typedef struct 
-{
-    /// Flag that indicates the position information has been updated since it was last checked.
-    bool_t updateFlag;
-
-    /// Month in UTC time.
-    uint8_t month;
-
-    /// Day of month in UTC time.
-    uint8_t day;
-
-    /// Hours in UTC time.
-    uint8_t hours;
-
-    /// Minutes in UTC time.
-    uint8_t minutes;
-
-    /// Seconds in UTC time.
-    uint8_t seconds;
-
-    /// Year in UTC time.
-    uint16_t year;
-
-    /// Latitude in milli arc-seconds where + is North, - is South.
-    int32_t latitude;
-
-    /// Longitude in milli arc-seconds where + is East, - is West.
-    int32_t longitude;
-
-    /// Altitude in cm
-    int32_t altitudeCM;
-
-    /// Calculated altitude in feet
-    int32_t altitudeFeet;
-
-    /// 3D speed in cm/second.
-    uint16_t vSpeed;
-
-    /// 2D speed in cm/second.
-    uint16_t hSpeed;
-
-    /// Heading units of 0.1 degrees.
-    uint16_t heading;
-
-    /// DOP (Dilution of Precision)
-    uint16_t dop;
-
-    /// 16-bit number that represents status of GPS engine.
-    uint16_t status;
-
-    /// Number of tracked satellites used in the fix position.
-    uint8_t trackedSats;
-
-    /// Number of visible satellites.
-    uint8_t visibleSats;
-} GPSPOSITION_STRUCT;
-
-void gpsInit();
-bool_t gpsIsReady();
-GPS_FIX_TYPE gpsGetFixType();
-int32_t gpsGetPeakAltitude();
-void gpsPowerOn();
-bool_t gpsSetup();
-void gpsUpdate();
-
-int16_t lm92GetTemp();
-
-/// Define the log record types.
-enum LOG_TYPE 
-{
-    /// Time stamp the log was started.
-    LOG_BOOTED = 0xb4,
-
-    /// GPS coordinates.
-    LOG_COORD = 0xa5,
-
-    /// Temperature
-    LOG_TEMPERATURE = 0x96,
-
-    /// Bus voltage.
-    LOG_VOLTAGE = 0x87
-};
-
-void logInit();
-uint32_t logGetAddress();
-void logType (LOG_TYPE type);
-void logUint8 (uint8_t value);
-void logInt16 (int16_t value);
-
-bool_t serialHasData();
-void serialInit();
-uint8_t serialRead();
-void serialUpdate();
-
-uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc);
-void sysInit();
-void sysLogVoltage();
-
-/// 0% duty cycle (LED Off) constant for function timeSetDutyCycle
-#define TIME_DUTYCYCLE_0 0
-
-/// 10% duty cycle constant for function timeSetDutyCycle
-#define TIME_DUTYCYCLE_10 1
-
-/// 70% duty cycle constant for function timeSetDutyCycle
-#define TIME_DUTYCYCLE_70 7
-
-uint8_t timeGetTicks();
-void timeInit();
-void timeSetDutyCycle (uint8_t dutyCycle);
-void timeUpdate();
-
-/// Operational modes of the TNC for the tncSetMode function.
-enum TNC_DATA_MODE
-{
-    /// No operation waiting for setup and configuration.
-    TNC_MODE_STANDBY, 
-
-    /// 1200 bps using A-FSK (Audio FSK) tones.
-    TNC_MODE_1200_AFSK,
-
-    /// 9600 bps using true FSK tones.
-    TNC_MODE_9600_FSK
-};
-
-void tncInit();
-bool_t tncIsFree();
-void tncHighRate(bool_t state);
-void tncSetMode (TNC_DATA_MODE dataMode);
-void tnc1200TimerTick();
-void tnc9600TimerTick();
-void tncTxByte (uint8_t value);
-void tncTxPacket(TNC_DATA_MODE dataMode);
-
-/**
- *  @defgroup ADC Analog To Digital Converter
- *
- *  Control and manage the on board PIC A/D converter.
- *
- *  @{
- */
-
-/// Filtered voltages using a single pole, low pass filter.
-uint16_t adcMainBusVolt;
-
-/// PIC ADC Channel number of the reference voltage.
-#define ADC_REF 0
-
-/// PIC ADC Channel number of the main bus voltage.
-#define ADC_MAINBUS 1
-
-/// Input diode drop in units of 0.01 volts.
-#define MAIN_BUS_VOLT_OFFSET 20
-
-/**
- *  Intialize the ADC subsystem.
- */
-void adcInit()
-{
-    // Setup the ADC.
-    setup_adc_ports(AN0_TO_AN1);
-    setup_adc( ADC_CLOCK_DIV_32 );
-
-    // Zero the ADC filters.
-    adcMainBusVolt = 0;
-}
-
-/**
- *   Filtered main bus voltage in 10mV resolution.
- *
- *   @return voltage in 10mV steps
- */
-uint16_t adcGetMainBusVolt()
-{
-    uint32_t volts;
-
-    volts = (uint32_t) (adcMainBusVolt >> 3);
-
-    volts = (volts * 330l) / 1023l;
-
-    return (uint16_t) volts + MAIN_BUS_VOLT_OFFSET;
-}
-
-/** 
- *   Get the current ADC value for the main bus voltage.
- *
- *   @return ADC value in the range 0 to 1023
- */
-uint16_t adcRawBusVolt()
-{
-    set_adc_channel(ADC_MAINBUS);
-    delay_us(50);
-    return read_adc();
-}
-
-/** 
- *   Get the current ADC value for the reference source voltage.
- *
- *   @return ADC value in the range 0 to 1023
- */
-uint16_t adcRawRefVolt()
-{
-    set_adc_channel(ADC_REF);
-    delay_us(50);
-    return read_adc();
-}
-
-/**
- *   Read and filter the ADC channels for bus voltages.
- */
-void adcUpdate(void)
-{
-    // Filter the bus voltage using a single pole low pass filter.
-    set_adc_channel(ADC_MAINBUS);
-    delay_us(50);
-    adcMainBusVolt = read_adc() + adcMainBusVolt - (adcMainBusVolt >> 3);
-}
-
-/** @} */
-
-
-/**
- *  @defgroup diag Diagnostics and Control
- *
- *  Functions for diagnostics and control of the hardware and flight data recorder.
- *
- *  @{
- */
-
-/// Number of bytes per line to display when reading flight data recorder.
-#define DIAG_BYTES_PER_LINE 32
-
-/**
- *   Process the command to erase the data logger flash.
- */
-void diagEraseFlash()
-{
-    // Confirm we want to erase the flash with the key sequence 'yes' .
-    fprintf (PC_HOST, "Are you sure (yes)?  ");
-
-    if (fgetc(PC_HOST) != 'y')
-        return;
-
-    if (fgetc(PC_HOST) != 'e')
-        return;
-
-    if (fgetc(PC_HOST) != 's')
-        return;
-
-    if (fgetc(PC_HOST) != 13)
-        return;
-
-    // User feedback and erase the part.
-    fprintf (PC_HOST, "Erasing flash...");
-
-    flashErase();
-
-    fprintf (PC_HOST, "done.\n\r");
-}
-
-/**
- *   Display the engineering mode menu.
- */
-void diagMenu()
-{
-    // User interface.
-    fprintf (PC_HOST, "Options: (e)rase Flash, (r)ead Flash\n\r");
-    fprintf (PC_HOST, "         Toggle (L)ED\n\r");
-    fprintf (PC_HOST, "         (P)TT - Push To Transmit\n\r");
-    fprintf (PC_HOST, "         (f)requencey down, (F)requency up - 1KHz step\n\r");
-    fprintf (PC_HOST, "         (c)hannel down, (C)hannel up - 25KHz step\n\r");
-    fprintf (PC_HOST, "         (a)mplitude down, (A)mplitude up - 0.5 dB steps\n\r");
-    fprintf (PC_HOST, "         e(x)it engineering mode\n\r");
-}
-
-/**
- *   Process the command to dump the contents of the data logger flash.
- */
-void diagReadFlash()
-{
-    bool_t dataFoundFlag, userStopFlag;
-    uint8_t i, buffer[DIAG_BYTES_PER_LINE];
-    uint32_t address;
-
-    // Set the initial conditions to read the flash.
-    address = 0x0000;
-    userStopFlag = false;
-
-    do 
-    {
-        // Read each block from the flash device.
-        flashReadBlock (address, buffer, DIAG_BYTES_PER_LINE);
-
-        // This flag will get set if any data byte is not equal to 0xff (erase flash state)
-        dataFoundFlag = false;
-
-        // Display the address.
-        fprintf (PC_HOST, "%08lx ", address);
-
-        // Display each byte in the line.
-        for (i = 0; i < DIAG_BYTES_PER_LINE; ++i) 
-        {
-            fprintf (PC_HOST, "%02x", buffer[i]);
-
-            // Set this flag if the cell is not erased.
-            if (buffer[i] != 0xff)
-                dataFoundFlag = true;
-
-            // Any key will abort the transfer.
-            if (kbhit(PC_HOST))
-                userStopFlag = true;
-        } // END for
-
-        //  at the end of each line.
-        fprintf (PC_HOST, "\n\r");
-
-        // Advance to the next block of memory.
-        address += DIAG_BYTES_PER_LINE;
-    } while (dataFoundFlag && !userStopFlag);
-
-    // Feedback to let the user know why the transfer stopped.
-    if (userStopFlag)
-        fprintf (PC_HOST, "User aborted download!\n\r");
-}
-
-void diag1PPS()
-{
-    uint16_t timeStamp, lastTimeStamp;
-
-    lastTimeStamp = 0x0000;
-
-    gpsPowerOn();
-
-    for (;;)
-    {
-        timeStamp = CCP_2;
-
-        if (timeStamp != lastTimeStamp)
-        {
-            delay_ms (10);
-
-            timeStamp = CCP_2;
-
-            fprintf (PC_HOST, "%lu %lu\n\r", timeStamp, (timeStamp - lastTimeStamp));
-
-            lastTimeStamp = timeStamp;
-        }
-    }
-}
-
-/**
- *   Process diagnostic commands through the debug RS-232 port.
- */
-void diagPort()
-{
-    bool_t diagDoneFlag, ledFlag, paFlag, showSettingsFlag;
-    uint8_t command, amplitude;
-    uint32_t freqHz;
-
-    // If the input is low, we aren't connected to the RS-232 device so continue to boot.
-    if (!input(PIN_B6))
-        return;
-
-    fprintf (PC_HOST, "Engineering Mode\n\r");
-    fprintf (PC_HOST, "Application Built %s %s\n\r", __DATE__, __TIME__);
-
-    // Current state of the status LED.
-    ledFlag = false;
-    output_bit (IO_LED, ledFlag);
-
-    // This flag indicates we are ready to leave the diagnostics mode.
-    diagDoneFlag = false;
-
-    // Current state of the PA.
-    paFlag = false;
-
-    // Flag that indicate we should show the current carrier frequency.
-    showSettingsFlag = false;
-
-    // Set the initial carrier frequency and amplitude.
-    freqHz = 445950000;
-    amplitude = 0;
-
-    // Wait for the exit command.
-    while (!diagDoneFlag) 
-    {
-        // Wait for the user command.
-        command = fgetc(PC_HOST);
-
-        // Decode and process the key stroke.
-        switch (command) 
-        {
-            case 'e':
-                diagEraseFlash();
-                logInit();
-                break;
-
-            case 'l':
-            case 'L':
-                ledFlag = (ledFlag ? false : true);
-                output_bit (IO_LED, ledFlag);
-                break;
-
-            case 'h':
-            case 'H':
-            case '?':
-                diagMenu();
-                break;
-
-            case 'r':
-                diagReadFlash();
-                break;
-
-            case 't':
-                tncHighRate (true);
-                fprintf (PC_HOST, "Set high rate TNC.\n\r");    
-                break;
-
-            case 'f':
-                freqHz -= 1000;
-                ddsSetFreq (freqHz);
-
-                // Display the new frequency.
-                showSettingsFlag = true;
-                break;
-
-            case 'F':
-                freqHz += 1000;
-                ddsSetFreq (freqHz);
-
-                // Display the new frequency.
-                showSettingsFlag = true;
-                break;
-
-            case 'c':
-                freqHz -= 25000;
-                ddsSetFreq (freqHz);
-
-                // Display the new frequency.
-                showSettingsFlag = true;
-                break;
-
-            case 'C':
-                freqHz += 25000;
-                ddsSetFreq (freqHz);
-
-                // Display the new frequency.
-                showSettingsFlag = true;
-                break;
-
-            case 'p':
-            case 'P':
-                ddsSetFreq (freqHz);
-
-                paFlag = (paFlag ? false : true);
-                output_bit (IO_PTT, paFlag);
-                output_bit (IO_OSK, paFlag);
-
-                if (paFlag)
-                {
-                    ddsSetMode (DDS_MODE_AFSK);             
-                    ddsSetAmplitude (amplitude);
-                } else
-                    ddsSetMode (DDS_MODE_POWERDOWN);
-
-                break;
-
-            case 'a':
-                if (amplitude != 200)
-                {
-                    amplitude += 5;
-                    ddsSetAmplitude (amplitude);
-
-                    // Display the new amplitude.
-                    showSettingsFlag = true;
-                }
-                break;
-
-            case 'A':
-                if (amplitude != 0)
-                {
-                    amplitude -= 5;
-                    ddsSetAmplitude (amplitude);
-
-                    // Display the new amplitude.
-                    showSettingsFlag = true;
-                }
-                break;
-
-            case 'g':
-                diag1PPS();
-                break;
-
-            case 'x':
-                diagDoneFlag = true;
-                break;
-
-            default:
-                fprintf (PC_HOST, "Invalid command.  (H)elp for menu.\n\r");
-                break;
-        } // END switch
-
-        // Display the results of any user requests or commands.
-        if (showSettingsFlag) 
-        {
-            showSettingsFlag = false;
-
-            fprintf (PC_HOST, "%03ld.%03ld MHz  ", freqHz / 1000000, (freqHz / 1000) % 1000);
-            fprintf (PC_HOST, "%d.%01ddBc\n\r", amplitude / 10, amplitude % 10);
-
-        } // END if
-
-    } // END while
-
-    // Let the user know we are done with this mode.
-    fprintf (PC_HOST, "Exit diagnostic mode.\n\r");
-
-    return;
-}
-
-/** @} */
-
-
-/**
- *  @defgroup DDS AD9954 DDS (Direct Digital Synthesizer)
- *
- *  Functions to control the Analog Devices AD9954 DDS.
- *
- *  @{
- */
-
-/// AD9954 CFR1 - Control functions including RAM, profiles, OSK, sync, sweep, SPI, and power control settings.
-#define DDS_AD9954_CFR1 0x00
-
-/// AD9954 CFR2 - Control functions including sync, PLL multiplier, VCO range, and charge pump current.
-#define DDS_AD9954_CFR2 0x01
-
-/// AD9954 ASF - Auto ramp rate speed control and output scale factor (0x0000 to 0x3fff).
-#define DDS_AD9954_ASF 0x02
-
-/// AD9954 ARR - Amplitude ramp rate for OSK function.
-#define DDS_AD9954_ARR 0x03
-
-/// AD9954 FTW0 - Frequency tuning word 0.
-#define DDS_AD9954_FTW0 0x04
-
-/// AD9954 FTW1 - Frequency tuning word 1
-#define DDS_AD9954_FTW1 0x06
-
-/// AD9954 NLSCW - Negative Linear Sweep Control Word used for spectral shaping in FSK mode
-#define DDS_AD9954_NLSCW 0x07
-
-/// AD9954 PLSCW - Positive Linear Sweep Control Word used for spectral shaping in FSK mode
-#define DDS_AD9954_PLSCW 0x08
-
-/// AD9954 RSCW0 - RAM Segment Control Word 0
-#define DDS_AD9954_RWCW0 0x07
-
-/// AD9954 RSCW0 - RAM Segment Control Word 1
-#define DDS_AD9954_RWCW1 0x08
-
-/// AD9954 RAM segment
-#define DDS_RAM 0x0b
-
-/// Current operational mode.
-DDS_MODE ddsMode;
-
-/// Number of digits in DDS frequency to FTW conversion.
-#define DDS_FREQ_TO_FTW_DIGITS 9
-
-/// Array of multiplication factors used to convert frequency to the FTW.
-const uint32_t DDS_MULT[DDS_FREQ_TO_FTW_DIGITS] = { 11, 7, 7, 3, 4, 8, 4, 9, 1 };
-
-/// Array of divisors used to convert frequency to the FTW.
-const uint32_t DDS_DIVISOR[DDS_FREQ_TO_FTW_DIGITS - 1] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
-
-/// Lookup table to convert dB amplitude scale in 0.5 steps to a linear DDS scale factor.
-const uint16_t DDS_AMP_TO_SCALE[] = 
-{ 
-    16383, 15467, 14601, 13785, 13013, 12286, 11598, 10949, 10337, 9759, 9213, 8697, 
-    8211, 7752, 7318, 6909, 6522, 6157, 5813, 5488, 5181, 4891, 4617, 4359, 4115, 3885, 3668, 3463, 
-    3269, 3086, 2913, 2750, 2597, 2451, 2314, 2185, 2062, 1947, 1838, 1735, 1638 
-};
-
-
-/// Frequency Word List - 4.0KHz FM frequency deviation at 81.15MHz  (445.950MHz)
-const uint32_t freqTable[256] = 
-{
-    955418300, 955419456, 955420611, 955421765, 955422916, 955424065, 955425210, 955426351, 
-    955427488, 955428618, 955429743, 955430861, 955431971, 955433073, 955434166, 955435249, 
-    955436322, 955437385, 955438435, 955439474, 955440500, 955441513, 955442511, 955443495, 
-    955444464, 955445417, 955446354, 955447274, 955448176, 955449061, 955449926, 955450773, 
-    955451601, 955452408, 955453194, 955453960, 955454704, 955455426, 955456126, 955456803, 
-    955457457, 955458088, 955458694, 955459276, 955459833, 955460366, 955460873, 955461354, 
-    955461809, 955462238, 955462641, 955463017, 955463366, 955463688, 955463983, 955464250, 
-    955464489, 955464701, 955464884, 955465040, 955465167, 955465266, 955465337, 955465380, 
-    955465394, 955465380, 955465337, 955465266, 955465167, 955465040, 955464884, 955464701, 
-    955464489, 955464250, 955463983, 955463688, 955463366, 955463017, 955462641, 955462238, 
-    955461809, 955461354, 955460873, 955460366, 955459833, 955459276, 955458694, 955458088, 
-    955457457, 955456803, 955456126, 955455426, 955454704, 955453960, 955453194, 955452408, 
-    955451601, 955450773, 955449926, 955449061, 955448176, 955447274, 955446354, 955445417, 
-    955444464, 955443495, 955442511, 955441513, 955440500, 955439474, 955438435, 955437385, 
-    955436322, 955435249, 955434166, 955433073, 955431971, 955430861, 955429743, 955428618, 
-    955427488, 955426351, 955425210, 955424065, 955422916, 955421765, 955420611, 955419456, 
-    955418300, 955417144, 955415989, 955414836, 955413684, 955412535, 955411390, 955410249, 
-    955409113, 955407982, 955406857, 955405740, 955404629, 955403528, 955402435, 955401351, 
-    955400278, 955399216, 955398165, 955397126, 955396100, 955395088, 955394089, 955393105, 
-    955392136, 955391183, 955390246, 955389326, 955388424, 955387540, 955386674, 955385827, 
-    955385000, 955384192, 955383406, 955382640, 955381896, 955381174, 955380474, 955379797, 
-    955379143, 955378513, 955377906, 955377324, 955376767, 955376235, 955375728, 955375246, 
-    955374791, 955374362, 955373959, 955373583, 955373234, 955372912, 955372618, 955372350, 
-    955372111, 955371900, 955371716, 955371560, 955371433, 955371334, 955371263, 955371220, 
-    955371206, 955371220, 955371263, 955371334, 955371433, 955371560, 955371716, 955371900, 
-    955372111, 955372350, 955372618, 955372912, 955373234, 955373583, 955373959, 955374362, 
-    955374791, 955375246, 955375728, 955376235, 955376767, 955377324, 955377906, 955378513, 
-    955379143, 955379797, 955380474, 955381174, 955381896, 955382640, 955383406, 955384192, 
-    955385000, 955385827, 955386674, 955387540, 955388424, 955389326, 955390246, 955391183, 
-    955392136, 955393105, 955394089, 955395088, 955396100, 955397126, 955398165, 955399216, 
-    955400278, 955401351, 955402435, 955403528, 955404629, 955405740, 955406857, 955407982, 
-    955409113, 955410249, 955411390, 955412535, 955413684, 955414836, 955415989, 955417144
-};
-
-/**
- *   Initialize the DDS regsiters and RAM.
- */
-void ddsInit()
-{
-    // Setup the SPI port for the DDS interface.    
-    setup_spi( SPI_MASTER | SPI_L_TO_H | SPI_CLK_DIV_4 | SPI_XMIT_L_TO_H );    
-
-    // Set the initial DDS mode.  The ddsSetMode function uses this value to make the desired DDS selections. 
-    ddsMode = DDS_MODE_NOT_INITIALIZED;
-
-    // Set the DDS operational mode.
-    ddsSetMode (DDS_MODE_POWERDOWN);
-
-    // Set the output to full scale.
-    ddsSetOutputScale (0x3fff);
-
-    // CFR2 (Control Function Register No. 2)
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_CFR2);
-
-    spi_write (0x00);     // Unused register bits
-    spi_write (0x00);
-    spi_write (0x9c);     // 19x reference clock multipler, high VCO range, nominal charge pump current
-    output_high (IO_CS);
-
-    // ARR (Amplitude Ramp Rate) to 15mS for OSK
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_ARR);
-
-    spi_write (83);
-    output_high (IO_CS);
-
-    // Strobe the part so we apply the updates.
-    output_high (IO_UPDATE);
-    output_low (IO_UPDATE);
-}
-
-/**
- *  Set DDS amplitude value in the range 0 to 16383 where 16383 is full scale.  This value is a 
- *  linear multiplier and needs to be scale for RF output power in log scale.
- *
- *  @param scale in the range 0 to 16383
- */
-void ddsSetOutputScale (uint16_t scale)
-{
-    // Set ASF (Amplitude Scale Factor)
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_ASF);
-
-    spi_write ((scale >> 8) & 0xff);
-    spi_write (scale & 0xff);
-
-    output_high (IO_CS);
-    
-    // Strobe the DDS to set the amplitude.
-    output_high (IO_UPDATE);
-    output_low (IO_UPDATE);
-}
-
-/**
- *   Set the DDS amplitude in units of dBc of full scale where 1 is 0.1 dB.  For example, a value of 30 is 3dBc
- *   or a value of 85 is 8.5dBc.
- *
- *   @param amplitude in 0.1 dBc of full scale
- */ 
-void ddsSetAmplitude (uint8_t amplitude)
-{
-    // Range limit based on the lookup table size.
-    if (amplitude > 200)
-        return;
-
-    // Set the linear DDS ASF (Amplitude Scale Factor) based on the dB lookup table.
-    ddsSetOutputScale (DDS_AMP_TO_SCALE[amplitude / 5]);
-
-    // Toggle the DDS output low and then high to force it to ramp to the new output level setting.
-    output_low (IO_OSK);
-    delay_ms(25); 
-
-    output_high (IO_OSK);
-    delay_ms(25); 
-}
-
-/**
- *  Set DDS frequency tuning word.  The output frequency is equal to RefClock * (ftw / 2 ^ 32).
- *
- *  @param ftw Frequency Tuning Word
- */
-void ddsSetFTW (uint32_t ftw)
-{
-    // Set FTW0 (Frequency Tuning Word 0)
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_FTW0);
-
-    spi_write ((ftw >> 24) & 0xff);
-    spi_write ((ftw >> 16) & 0xff);
-    spi_write ((ftw >> 8) & 0xff);
-    spi_write (ftw & 0xff);
-
-    output_high (IO_CS);
-    
-    // Strobe the DDS to set the frequency.
-    output_high (IO_UPDATE);
-    output_low (IO_UPDATE);     
-}
-
-/**
- *   Convert frequency in hertz to 32-bit DDS FTW (Frequency Tune Word).
- *
- *   @param freq frequency in Hertz
- *
- */
-void ddsSetFreq(uint32_t freq)
-{
-    uint8_t i;
-    uint32_t ftw;
-    
-    // To avoid rounding errors with floating point math, we do a long multiply on the data.
-    ftw = freq * DDS_MULT[0];
-    
-    for (i = 0; i < DDS_FREQ_TO_FTW_DIGITS - 1; ++i)
-        ftw += (freq * DDS_MULT[i+1]) / DDS_DIVISOR[i];
-    
-    ddsSetFTW (ftw);
-}
-
-/**
- *  Set DDS frequency tuning word for the FSK 0 and 1 values.  The output frequency is equal 
- *  to RefClock * (ftw / 2 ^ 32).
- *
- *  @param ftw0 frequency tuning word for the FSK 0 value
- *  @param ftw1 frequency tuning word for the FSK 1 value
- */
-void ddsSetFSKFreq (uint32_t ftw0, uint32_t ftw1)
-{
-    // Set FTW0 (Frequency Tuning Word 0)
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_FTW0);
-
-    spi_write ((ftw0 >> 24) & 0xff);
-    spi_write ((ftw0 >> 16) & 0xff);
-    spi_write ((ftw0 >> 8) & 0xff);
-    spi_write (ftw0 & 0xff);
-
-    output_high (IO_CS);
-    
-    // Set FTW0 (Frequency Tuning Word 1)
-    output_low (IO_CS);
-    spi_write (DDS_AD9954_FTW1);
-
-    spi_write ((ftw1 >> 24) & 0xff);
-    spi_write ((ftw1 >> 16) & 0xff);
-    spi_write ((ftw1 >> 8) & 0xff);
-    spi_write (ftw1 & 0xff);
-
-    output_high (IO_CS);
-    
-    // Strobe the DDS to set the frequency.
-    output_high (IO_UPDATE);
-    output_low (IO_UPDATE);     
-}
-
-/** 
- *   Set the DDS to run in A-FSK, FSK, or PSK31 mode
- *
- *   @param mode DDS_MODE_APRS, DDS_MODE_PSK31, or DDS_MODE_HF_APRS constant
- */
-void ddsSetMode (DDS_MODE mode)
-{
-    // Save the current mode.
-    ddsMode = mode;
-
-    switch (mode) 
-    {
-        case DDS_MODE_POWERDOWN:
-            // CFR1 (Control Function Register No. 1)
-            output_low (IO_CS);
-            spi_write (DDS_AD9954_CFR1);
-        
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (0xf0);  // Power down all subsystems.
-            output_high (IO_CS);
-            break;
-
-        case DDS_MODE_AFSK:
-            // CFR1 (Control Function Register No. 1)
-            output_low (IO_CS);
-            spi_write (DDS_AD9954_CFR1);
-        
-            spi_write (0x03);  // OSK Enable and Auto OSK keying
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (0x40);  // Power down comparator circuit
-            output_high (IO_CS);
-            break;
-
-        case DDS_MODE_FSK:
-            // CFR1 (Control Function Register No. 1)
-            output_low (IO_CS);
-            spi_write (DDS_AD9954_CFR1);
-
-            spi_write (0x03);  // Clear RAM Enable, OSK Enable, Auto OSK keying
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (0x40);  // Power down comparator circuit
-            output_high (IO_CS);
-
-            // NOTE: The sweep rate requires 1/4 of a bit time (26uS) to transition.
-            // 6KHz delta = 70641 counts = (6KHz / 364.8MHz) * 2 ^ 32
-            // SYNC_CLK = 91.2MHz  1/91.2MHz * 70641 * 1/29 = 26.7uS
-
-            // NLSCW (Negative Linear Sweep Control Word)
-            output_low (IO_CS);
-            spi_write (DDS_AD9954_NLSCW);
-
-            spi_write (1);     // Falling sweep ramp rate word
-            spi_write (0x00);  // Delta frequency tuning word
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (250); 
-            output_high (IO_CS);
-
-            // PLSCW (Positive Linear Sweep Control Word)
-            output_low (IO_CS);
-            spi_write (DDS_AD9954_PLSCW);
-
-            spi_write (1);     // Rising sweep ramp rate word
-            spi_write (0x00);  // Delta frequency tuning word
-            spi_write (0x00);
-            spi_write (0x00);
-            spi_write (250); 
-            output_high (IO_CS);
-            break;
-    } // END switch
-    
-    // Strobe the DDS to change the mode.
-    output_high (IO_UPDATE);
-    output_low (IO_UPDATE);      
-}
-
-/** @} */
-
-/**
- *  @defgroup flash Flash Manager
- *
- *  Functions to control the ST MP25P80 serial flash device.
- *
- *  @{
- */
-
-/// Flash Chip Select - Port B3
-#define FLASH_CS PIN_B3
-
-/// Flash Clock - Port B5
-#define FLASH_CLK PIN_B5
-
-/// Flash Data Input - Port B4
-#define FLASH_D PIN_B4
-
-/// Flash Data Output - Port B2
-#define FLASH_Q PIN_B2
-
-/** 
- *   Determine if a flash write or erase operation is currently in progress.
- *
- *   @return true if write/erase in progress
- */
-bool_t flashIsWriteInProgress()
-{
-    uint8_t status;
-
-    output_low (FLASH_CS);
-
-    // Read Status Register (RDSR) flash command.
-    flashSendByte (0x05);
-
-    status = flashGetByte();
-
-    output_high (FLASH_CS);
-
-    return (((status & 0x01) == 0x01) ? true : false);
-}
-
-/**
- *   Read a block of memory from the flash device.
- *
- *   @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
- *   @param block pointer to locate of data block
- *   @param length number of bytes to read
- */
-void flashReadBlock(uint32_t address, uint8_t *block, uint16_t length)
-{
-    uint16_t i;
-    
-    output_low (FLASH_CS);
-
-    // Read Data Byte(s) (READ) flash command.
-    flashSendByte (0x03);
-    flashSendAddress (address);
-    
-    for (i = 0; i < length; ++i)
-        *block++ = flashGetByte();
-    
-    output_high (FLASH_CS);
-}
-
-/**
- *   Write a block of memory to the flash device.
- *
- *   @param address of desired location in the range 0x00000 to 0xFFFFF (1MB)
- *   @param block pointer data block to write
- *   @param length number of bytes to write
- */
-void flashWriteBlock(uint32_t address, uint8_t *block, uint8_t length)
-{
-    uint8_t i;
-
-    output_low (FLASH_CS);
-    // Write Enable (WREN) flash command.
-    flashSendByte (0x06);
-    output_high (FLASH_CS);
-    
-    output_low (FLASH_CS);
-    // Page Program (PP) flash command.
-    flashSendByte (0x02);
-    flashSendAddress (address);
-    
-    for (i = 0; i < length; ++i) 
-    {
-        // Send each byte in the data block.
-        flashSendByte (*block++);
-
-        // Track the address in the flash device.
-        ++address;
-
-        // If we cross a page boundary (a page is 256 bytes) we need to stop and send the address again.
-        if ((address & 0xff) == 0x00) 
-        {
-            output_high (FLASH_CS);
-
-            // Write this block of data.
-            while (flashIsWriteInProgress());
-
-            output_low (FLASH_CS);
-            // Write Enable (WREN) flash command.
-            flashSendByte (0x06);
-            output_high (FLASH_CS);
-
-            output_low (FLASH_CS);
-            // Page Program (PP) flash command.
-            flashSendByte (0x02);
-            flashSendAddress (address);
-        } // END if
-    } // END for    
-
-    output_high (FLASH_CS);
-
-    // Wait for the final write operation to complete.
-    while (flashIsWriteInProgress());
-}
-
-/** 
- *   Erase the entire flash device (all locations set to 0xff).
- */
-void flashErase()
-{
-    output_low (FLASH_CS);
-    // Write Enable (WREN) flash command.
-    flashSendByte (0x06);
-    output_high (FLASH_CS);
-    
-    output_low (FLASH_CS);
-    // Bulk Erase (BE) flash command.
-    flashSendByte (0xc7);
-    output_high (FLASH_CS);
-
-    while (flashIsWriteInProgress());
-}
-
-/**
- *   Read a single byte from the flash device through the serial interface.  This function
- *   only controls the clock line.  The chip select must be configured before calling
- *   this function.
- *
- *   @return byte read from device
- */
-uint8_t flashGetByte()
-{
-    uint8_t i, value;
-    
-    value = 0;      
-    
-    // Bit bang the 8-bits.
-    for (i = 0; i < 8; ++i) 
-    {
-        // Data is ready on the rising edge of the clock.
-        output_high (FLASH_CLK);
-
-        // MSB is first, so shift left.
-        value = value << 1;
-    
-        if (input (FLASH_Q))
-            value = value | 0x01;
-    
-        output_low (FLASH_CLK);
-    } // END for
-
-    return value;
-}
-
-/**
- *   Initialize the flash memory subsystem.
- */
-void flashInit()
-{
-    // I/O lines to control flash.
-    output_high (FLASH_CS);
-    output_low (FLASH_CLK);
-    output_low (FLASH_D);
-}
-
-/**
- *   Write a single byte to the flash device through the serial interface.  This function
- *   only controls the clock line.  The chip select must be configured before calling
- *   this function.
- *
- *   @param value byte to write to device
- */
-void flashSendByte(uint8_t value)
-{
-    uint8_t i;
-    
-    // Bit bang the 8-bits.
-    for (i = 0; i < 8; ++i) 
-    {
-        // Drive the data input pin.
-        if ((value & 0x80) == 0x80)
-            output_high (FLASH_D);
-        else
-            output_low (FLASH_D);
-        
-        // MSB is first, so shift leeft.
-        value = value << 1;
-        
-        // Data is accepted on the rising edge of the clock.
-        output_high (FLASH_CLK);
-        output_low (FLASH_CLK);
-    } // END for
-}
-
-/**
- *    Write the 24-bit address to the flash device through the serial interface.  This function
- *   only controls the clock line.  The chip select must be configured before calling
- *   this function.
- *
- *   @param address 24-bit flash device address
- */
-void flashSendAddress(uint32_t address)
-{
-    uint8_t i;
-    
-    // Bit bang the 24-bits.
-    for (i = 0; i < 24; ++i) 
-    {
-        // Drive the data input pin.
-        if ((address & 0x800000) == 0x800000)
-            output_high (FLASH_D);
-        else
-            output_low (FLASH_D);
-        
-        // MSB is first, so shift left.
-        address = address << 1;
-
-        // Data is accepted on the rising edge of the clock.
-        output_high (FLASH_CLK);
-        output_low (FLASH_CLK);
-    } // END for
-}
-
-/** @} */
-
-/**
- *  @defgroup GPS Motorola M12+ GPS Engine
- *
- *  Functions to control the Motorola M12+ GPS engine in native binary protocol mode.
- *
- *  @{
- */
-
-/// The maximum length of a binary GPS engine message.
-#define GPS_BUFFER_SIZE 50
-
-/// GPS parse engine state machine values.
-enum GPS_PARSE_STATE_MACHINE 
-{
-    /// 1st start character '@'
-    GPS_START1,
-
-    /// 2nd start character '@'
-    GPS_START2,
-
-    /// Upper case 'A' - 'Z' message type
-    GPS_COMMAND1,
-
-    /// Lower case 'a' - 'z' message type
-    GPS_COMMAND2,
-
-    /// 0 - xx bytes based on message type 'Aa'
-    GPS_READMESSAGE,
-
-    /// 8-bit checksum
-    GPS_CHECKSUMMESSAGE,
-
-    /// End of message - Carriage Return
-    GPS_EOMCR,
-
-    /// End of message - Line Feed
-    GPS_EOMLF
-};
-
-/// Index into gpsBuffer used to store message data.
-uint8_t gpsIndex;
-
-/// State machine used to parse the GPS message stream.
-GPS_PARSE_STATE_MACHINE gpsParseState;
-
-/// Buffer to store data as it is read from the GPS engine.
-uint8_t gpsBuffer[GPS_BUFFER_SIZE]; 
-
-/// Peak altitude detected while GPS is in 3D fix mode.
-int32_t gpsPeakAltitude;
-
-/// Checksum used to verify binary message from GPS engine.
-uint8_t gpsChecksum;
-
-/// Last verified GPS message received.
-GPSPOSITION_STRUCT gpsPosition;
-
-/**
- *   Get the type of fix.
- *
- *   @return gps fix type enumeration
- */
-GPS_FIX_TYPE gpsGetFixType()
-{
-    // The upper 3-bits determine the fix type.
-    switch (gpsPosition.status & 0xe000) 
-    {
-        case 0xe000:
-            return GPS_3D_FIX;
-
-        case 0xc000:
-            return GPS_2D_FIX;
-    
-        default:
-            return GPS_NO_FIX;
-    } // END switch
-}
-
-/**
- *  Peak altitude detected while GPS is in 3D fix mode since the system was booted.
- *
- *  @return altitude in feet
- */
-int32_t gpsGetPeakAltitude()
-{
-    return gpsPeakAltitude;
-}
-
-/** 
- *    Initialize the GPS subsystem.
- */
-void gpsInit()
-{
-    // Initial parse state.
-    gpsParseState = GPS_START1;
-
-    // Assume we start at sea level.
-    gpsPeakAltitude = 0;
-
-    // Clear the structure that stores the position message.
-    memset (&gpsPosition, 0, sizeof(GPSPOSITION_STRUCT));
-
-    // Setup the timers used to measure the 1-PPS time period.
-    setup_timer_3(T3_INTERNAL | T3_DIV_BY_1);
-    setup_ccp2 (CCP_CAPTURE_RE | CCP_USE_TIMER3);
-}
-
-/**
- *   Determine if new GPS message is ready to process.  This function is a one shot and
- *   typically returns true once a second for each GPS position fix.
- *
- *   @return true if new message available; otherwise false
- */
-bool_t gpsIsReady()
-{
-    if (gpsPosition.updateFlag) 
-    {
-        gpsPosition.updateFlag = false;
-        return true;
-    } // END if
-
-    return false;
-}
-
-/**
- *   Calculate NMEA-0183 message checksum of buffer that is length bytes long.
- *
- *   @param buffer pointer to data buffer.
- *   @param length number of bytes in buffer.
- *
- *   @return checksum of buffer
- */
-uint8_t gpsNMEAChecksum (uint8_t *buffer, uint8_t length)
-{
-    uint8_t i, checksum;
-
-    checksum = 0;
-
-    for (i = 0; i < length; ++i)
-        checksum ^= buffer[i];
-
-    return checksum;
-}
-
-/**
- *   Verify the GPS engine is sending the @@Hb position report message.  If not,
- *   configure the GPS engine to send the desired report.
- *
- *   @return true if GPS engine operation; otherwise false
- */
-bool_t gpsSetup()
-{
-    uint8_t startTime, retryCount;
-
-    // We wait 10 seconds for the GPS engine to respond to our message request.
-    startTime = timeGetTicks();
-    retryCount = 0;
-
-    while (++retryCount < 10) 
-    {
-        // Read the serial FIFO and process the GPS messages.
-        gpsUpdate();
-
-        // If a GPS data set is available, then GPS is operational.
-        if (gpsIsReady()) 
-        {
-            timeSetDutyCycle (TIME_DUTYCYCLE_10);
-            return true;
-        }
-
-        if (timeGetTicks() > startTime) 
-        {
-            puts ("@@Hb\001\053\015\012");
-            startTime += 10;
-        } // END if
-            
-    } // END while
-
-    return false;
-}
-
-/**
- *   Parse the Motorola @@Hb (Short position/message) report.
- */
-void gpsParsePositionMessage()
-{
-    // Convert the binary stream into data elements.  We will scale to the desired units
-    // as the values are used.
-    gpsPosition.updateFlag = true;
-
-    gpsPosition.month = gpsBuffer[0];
-    gpsPosition.day = gpsBuffer[1];
-    gpsPosition.year = ((uint16_t) gpsBuffer[2] << 8) | gpsBuffer[3];
-    gpsPosition.hours = gpsBuffer[4];
-    gpsPosition.minutes = gpsBuffer[5];
-    gpsPosition.seconds = gpsBuffer[6];
-    gpsPosition.latitude = ((int32) gpsBuffer[11] << 24) | ((int32) gpsBuffer[12] << 16) | ((int32) gpsBuffer[13] << 8) | (int32) gpsBuffer[14];
-    gpsPosition.longitude = ((int32) gpsBuffer[15] << 24) | ((int32) gpsBuffer[16] << 16) | ((int32) gpsBuffer[17] << 8) | gpsBuffer[18];
-    gpsPosition.altitudeCM = ((int32) gpsBuffer[19] << 24) | ((int32) gpsBuffer[20] << 16) | ((int32) gpsBuffer[21] << 8) | gpsBuffer[22];
-    gpsPosition.altitudeFeet = gpsPosition.altitudeCM * 100l / 3048l;
-    gpsPosition.vSpeed = ((uint16_t) gpsBuffer[27] << 8) | gpsBuffer[28];
-    gpsPosition.hSpeed = ((uint16_t) gpsBuffer[29] << 8) | gpsBuffer[30];
-    gpsPosition.heading = ((uint16_t) gpsBuffer[31] << 8) | gpsBuffer[32];
-    gpsPosition.dop = ((uint16_t) gpsBuffer[33] << 8) | gpsBuffer[34];
-    gpsPosition.visibleSats = gpsBuffer[35];
-    gpsPosition.trackedSats = gpsBuffer[36];
-    gpsPosition.status = ((uint16_t) gpsBuffer[37] << 8) | gpsBuffer[38];
-
-    // Update the peak altitude if we have a valid 3D fix.
-    if (gpsGetFixType() == GPS_3D_FIX)
-        if (gpsPosition.altitudeFeet > gpsPeakAltitude)
-            gpsPeakAltitude = gpsPosition.altitudeFeet;
-}
-
-/**
- *  Turn on the GPS engine power and serial interface.
- */
-void gpsPowerOn()
-{
-    // 3.0 VDC LDO control line.
-    output_high (IO_GPS_PWR);
-
-    // Enable the UART and the transmit line.
-#asm
-    bsf 0xFAB.7
-#endasm
-}
-
-/**
- *   Turn off the GPS engine power and serial interface.
- */
-void gpsPowerOff()
-{
-    // Disable the UART and the transmit line.
-#asm
-    bcf 0xFAB.7
-#endasm
-
-    // 3.0 VDC LDO control line.
-    output_low (IO_GPS_PWR);
-}
-
-/**
- *   Read the serial FIFO and process complete GPS messages.
- */
-void gpsUpdate() 
-{
-    uint8_t value;
-
-    // This state machine handles each characters as it is read from the GPS serial port.
-    // We are looking for the GPS mesage @@Hb ... C
-    while (serialHasData()) 
-    {
-        // Get the character value.
-        value = serialRead();
-
-        // Process based on the state machine.
-        switch (gpsParseState) 
-        {
-            case GPS_START1:
-                if (value == '@')
-                    gpsParseState = GPS_START2;
-                break;
-
-            case GPS_START2:
-                if (value == '@')
-                    gpsParseState = GPS_COMMAND1;
-                else
-                    gpsParseState = GPS_START1;
-                break;
-
-            case GPS_COMMAND1:
-                if (value == 'H')
-                    gpsParseState = GPS_COMMAND2;
-                else
-                    gpsParseState = GPS_START1;
-                break;
-
-            case GPS_COMMAND2:
-                if (value == 'b') 
-                {
-                    gpsParseState = GPS_READMESSAGE;
-                    gpsIndex = 0;
-                    gpsChecksum = 0;
-                    gpsChecksum ^= 'H';
-                    gpsChecksum ^= 'b';
-                } else
-                    gpsParseState = GPS_START1;
-                break;
-
-            case GPS_READMESSAGE:
-                gpsChecksum ^= value;
-                gpsBuffer[gpsIndex++] = value;
-
-                if (gpsIndex == 47)
-                    gpsParseState = GPS_CHECKSUMMESSAGE;
-
-                break;
-
-            case GPS_CHECKSUMMESSAGE:
-                if (gpsChecksum == value)
-                    gpsParseState = GPS_EOMCR;
-                else
-                    gpsParseState = GPS_START1;
-                break;
-
-            case GPS_EOMCR:
-                if (value == 13)
-                    gpsParseState = GPS_EOMLF;
-                else
-                    gpsParseState = GPS_START1;
-                break;
-
-            case GPS_EOMLF:
-                // Once we have the last character, convert the binary message to something usable.
-                if (value == 10)
-                    gpsParsePositionMessage();
-
-                gpsParseState = GPS_START1;
-                break;
-        } // END switch
-    } // END while
-}
-
-/** @} */
-
-
-/**
- *  @defgroup log Flight Data Recorder
- *
- *  Functions to manage and control the flight data recorder
- *
- *  @{
- */
-
-/// Number of bytes to buffer before writing to flash memory.
-#define LOG_WRITE_BUFFER_SIZE 40
-
-/// Last used address in flash memory.
-uint32_t logAddress;
-
-/// Temporary buffer that holds data before it is written to flash device.
-uint8_t logBuffer[LOG_WRITE_BUFFER_SIZE];
-
-/// Current index into log buffer.
-uint8_t logIndex;
-
-/** 
- *    Last used address in flash memory.  This location is where the next log data will
- *    be written.
- *
- *    @return 24-bit flash memory address
- */
-uint32_t logGetAddress()
-{
-    return logAddress;
-}
-
-/**
- *   Write the contents of the temporary log buffer to the flash device.  If the buffer
- *   is empty, nothing is done.
- */
-void logFlush()
-{
-    // We only need to write if there is data.
-    if (logIndex != 0) 
-    {
-        flashWriteBlock (logAddress, logBuffer, logIndex);
-        logAddress += logIndex;
-        logIndex = 0;
-    } // END if
-}
-
-/** 
- *   Prepare the flight data recorder for logging.
- */
-void logInit()
-{
-    uint8_t buffer[8];
-    bool_t endFound;
-
-    fprintf (PC_HOST, "Searching for end of flash log...");
-
-    logAddress = 0x0000;
-    endFound = false;
-
-    // Read each logged data block from flash to determine how long it is.
-    do 
-    {
-        // Read the data log entry type.
-        flashReadBlock (logAddress, buffer, 1);
-
-        // Based on the log entry type, we'll skip over the data contained in the entry.
-        switch (buffer[0]) 
-        {
-            case LOG_BOOTED:
-                logAddress += 7;
-                break;
-
-            case LOG_COORD:
-                logAddress += 26;
-                break;
-
-            case LOG_TEMPERATURE:
-                logAddress += 3;
-                break;
-
-            case LOG_VOLTAGE:
-                logAddress += 5;
-                break;
-
-            case 0xff:
-                endFound = true;
-                break;
-
-            default:
-                ++logAddress;
-        } // END switch
-    } while (logAddress < 0x100000 && !endFound);
-
-    fprintf (PC_HOST, "done.  Log contains %ld bytes.\n\r", logAddress);
-
-    logIndex = 0;
-}
-
-/**
- *   Start a entry in the data log.
- *
- *   @param type of log entry, i.e. LOG_BOOTED, LOG_COORD, etc.
- */
-void logType (LOG_TYPE type)
-{
-    // Only add the new entry if there is space.
-    if (logAddress >= 0x100000)
-        return;
-
-    // Write the old entry first.
-    logFlush();
-
-    // Save the type and set the log buffer pointer.
-    logBuffer[0] = type;
-    logIndex = 1;
-}
-
-/**
- *  Save an unsigned, 8-bit value in the log.
- *
- *  @param value unsigned, 8-bit value
- */
-void logUint8 (uint8_t value)
-{
-    logBuffer[logIndex++] = value;
-}
-
-/**
- *  Save a signed, 16-bit value in the log.
- *
- *  @param value signed, 16-bit value
- */
-void logInt16 (int16_t value)
-{
-    logBuffer[logIndex++] = (value >> 8) & 0xff;
-    logBuffer[logIndex++] = value & 0xff;
-}
-
-/**
- *  Save an unsigned, 16-bit value in the log.
- *
- *  @param value unsigned, 16-bit value
- */
-void logUint16 (uint16_t value)
-{
-    logBuffer[logIndex++] = (value >> 8) & 0xff;
-    logBuffer[logIndex++] = value & 0xff;
-}
-
-/**
- *  Save a signed, 32-bit value in the log.
- *
- *  @param value signed, 32-bit value
- */
-void logInt32 (int32_t value)
-{
-    logBuffer[logIndex++] = (value >> 24) & 0xff;
-    logBuffer[logIndex++] = (value >> 16) & 0xff;
-    logBuffer[logIndex++] = (value >> 8) & 0xff;
-    logBuffer[logIndex++] = value & 0xff;
-}
-
-/** @} */
-
-/**
- *  @defgroup LM92 LM92 temperature sensor
- *
- *  Read and control the National Semiconductor LM92 I2C temperature sensor
- *
- *  @{
- */
-
-/** 
- *   Read the LM92 temperature value in 0.1 degrees F.
- *
- *   @return 0.1 degrees F
- */
-int16_t lm92GetTemp()
-{
-    int16_t value;
-    int32_t temp;
-
-    // Set the SDA and SCL to input pins to control the LM92.
-    set_tris_c (0x9a);
-
-    // Read the temperature register value.
-    i2c_start();
-    i2c_write(0x97);
-    value = ((int16_t) i2c_read() << 8);
-    value = value | ((int16_t) i2c_read() & 0x00f8);
-    i2c_stop();
-
-    // Set the SDA and SCL back to outputs for use with the AD9954 because we share common clock pins.
-    set_tris_c (0x82);
-
-    //  LM92 register   0.0625degC/bit   9   10     9
-    //  ------------- * -------------- * - * -- =  -- + 320
-    //        8                          5         64
-
-    // Convert to degrees F.
-    temp = (int32_t) value;
-    temp = ((temp * 9l) / 64l) + 320;
-    
-    return (int16_t) temp;
-}
-
-/** @} */
-
-
-/**
- *  @defgroup serial Serial Port FIFO
- *
- *  FIFO for the built-in serial port.
- *
- *  @{
- */
-
-/// Size of serial port FIFO in bytes.  It must be a power of 2, i.e. 2, 4, 8, 16, etc.
-#define SERIAL_BUFFER_SIZE 64
-
-/// Mask to wrap around at end of circular buffer.  (SERIAL_BUFFER_SIZE - 1)
-#define SERIAL_BUFFER_MASK 0x3f
-
-/// Index to the next free location in the buffer.
-uint8_t serialHead;
-
-/// Index to the next oldest data in the buffer.
-uint8_t serialTail;
-
-/// Circular buffer (FIFO) to hold serial data.
-uint8_t serialBuffer[SERIAL_BUFFER_SIZE];
-
-/**
- *   Determine if the FIFO contains data.
- *
- *   @return true if data present; otherwise false
- */
-bool_t serialHasData()
-{
-    if (serialHead == serialTail)
-        return false;
-
-    return true;
-}
-
-/** 
- *   Initialize the serial processor.
- */
-void serialInit()
-{
-    serialHead = 0;
-    serialTail = 0;
-}
-
-/**
- *   Get the oldest character from the FIFO.
- *
- *   @return oldest character; 0 if FIFO is empty
- */
-uint8_t serialRead()
-{
-    uint8_t value;
-
-    // Make sure we have something to return.
-    if (serialHead == serialTail)
-        return 0;
-
-    // Save the value.
-    value = serialBuffer[serialTail];
-
-    // Update the pointer.
-    serialTail = (serialTail + 1) & SERIAL_BUFFER_MASK;
-
-    return value;
-}
+// Public methods, constants, and data structures for each class.
 
-/**
- *   Read and store any characters in the PIC serial port in a FIFO.
- */
-void serialUpdate()
-{
-    // If there isn't a character in the PIC buffer, just leave.
-    while (kbhit()) 
-    {
-        // Save the value in the FIFO.
-        serialBuffer[serialHead] = getc();
+static void timeInit(void);
 
-        // Move the pointer to the next open space.
-        serialHead = (serialHead + 1) & SERIAL_BUFFER_MASK;
-    }
-}
+static void tncInit(void);
+static void tnc1200TimerTick(void);
 
 /** @} */
 
@@ -2002,296 +158,62 @@ void serialUpdate()
  *  @defgroup sys System Library Functions
  *
  *  Generic system functions similiar to the run-time C library.
- *
- *  @{
- */
-
-/**
- *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
- *    The crc parameter allow the calculation on the CRC on multiple buffers.
- *
- *    @param buffer Pointer to data buffer.
- *    @param length number of bytes in data buffer
- *    @param crc starting value
- *
- *    @return CRC-16 of buffer[0 .. length]
- */
-uint16_t sysCRC16(uint8_t *buffer, uint8_t length, uint16_t crc)
-{
-    uint8_t i, bit, value;
-
-    for (i = 0; i < length; ++i) 
-    {
-        value = buffer[i];
-
-        for (bit = 0; bit < 8; ++bit) 
-        {
-            crc ^= (value & 0x01);
-            crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
-            value = value >> 1;
-        } // END for
-    } // END for
-
-    return crc ^ 0xffff;
-}
-
-/**
- *   Initialize the system library and global resources.
- */
-void sysInit()
-{
-    gpsPowerOff ();
-    output_high (IO_LED);
-
-    output_high (IO_CS);
-    output_low (IO_PS1);
-    output_low (IO_PS0);
-    output_low (IO_OSK);
-    output_low (IO_UPDATE);
-    output_low (IO_PTT);
-    output_low (IO_GPS_TXD);
-
-    // Configure the port direction (input/output).
-    set_tris_a (0xc3);
-    set_tris_b (0x44);
-    set_tris_c (0x82);
-
-    // Display a startup message during boot.
-    fprintf (PC_HOST, "System booted.\n\r");
-}
-
-/**
- *   Log the current GPS position.
- */
-void sysLogGPSData()
-{
-    // Log the data.
-    logType (LOG_COORD);
-    logUint8 (gpsPosition.hours);
-    logUint8 (gpsPosition.minutes);
-    logUint8 (gpsPosition.seconds);
-    logInt32 (gpsPosition.latitude);
-    logInt32 (gpsPosition.longitude);
-    logInt32 (gpsPosition.altitudeCM);
-
-    logUint16 (gpsPosition.vSpeed);
-    logUint16 (gpsPosition.hSpeed);
-    logUint16 (gpsPosition.heading);
-
-    logUint16 (gpsPosition.status);
-
-    logUint8 ((uint8_t) (gpsPosition.dop & 0xff));
-    logUint8 ((uint8_t) ((gpsPosition.visibleSats << 4) | gpsPosition.trackedSats));
-}
-
-/**
- *   Log the ADC values of the bus and reference voltage values.
- */
-void sysLogVoltage()
-{
-    logType (LOG_VOLTAGE);
-    logUint16 (adcRawBusVolt());
-    logUint16 (adcRawRefVolt());
-}
-
-/** @} */
-
-/**
- *  @defgroup rtc Real Time Interrupt tick
- *
- *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
- *
- *  @{
- */
-
-/// A counter that ticks every 100mS.
-uint8_t timeTicks;
-
-/// Counts the number of 104uS interrupts for a 100mS time period.
-uint16_t timeInterruptCount;
-
-/// Counts the number of 100mS time periods in 1 second.
-uint8_t time100ms;
-
-/// System time in seconds.
-uint8_t timeSeconds;
-
-/// System time in minutes.
-uint8_t timeMinutes;
-
-/// System time in hours.
-uint8_t timeHours;
-
-/// Desired LED duty cycle 0 to 9 where 0 = 0% and 9 = 90%.
-uint8_t timeDutyCycle;
-
-/// Current value of the timer 1 compare register used to generate 104uS interrupt rate (9600bps).
-uint16_t timeCompare;
-
-/// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
-uint16_t timeNCO;
-
-/// Audio tone NCO update step (phase).
-uint16_t timeNCOFreq;
-
-/// Counter used to deciminate down from the 104uS to 833uS interrupt rate.  (9600 to 1200 baud) 
-uint8_t timeLowRateCount;
-
-/// Current TNC mode (standby, 1200bps A-FSK, or 9600bps FSK)
-TNC_DATA_MODE tncDataMode;
-
-/// Flag set true once per second.
-bool_t timeUpdateFlag;
-
-/// Flag that indicate the flight time should run.
-bool_t timeRunFlag;
-
-/// The change in the CCP_1 register for each 104uS (9600bps) interrupt period.
-#define TIME_RATE 125
-
-/**
- *   Running 8-bit counter that ticks every 100mS.
- *
- *   @return 100mS time tick
- */
-uint8_t timeGetTicks()
-{
-    return timeTicks;
-}
-
-/**
- *   Initialize the real-time clock.
- */
-void timeInit()
-{
-    timeTicks = 0;
-    timeInterruptCount = 0;
-    time100mS = 0;
-    timeSeconds = 0;
-    timeMinutes = 0;
-    timeHours = 0;
-    timeDutyCycle = TIME_DUTYCYCLE_70;
-    timeCompare = TIME_RATE;
-    timeUpdateFlag = false;
-    timeNCO = 0x00;
-    timeLowRateCount = 0;
-    timeNCOFreq = 0x2000;
-    tncDataMode = TNC_MODE_STANDBY;  
-    timeRunFlag = false;
-    
-    // Configure CCP1 to interrupt at 1mS for PSK31 or 833uS for 1200 baud APRS
-    CCP_1 = TIME_RATE;
-    set_timer1(timeCompare);
-    setup_ccp1( CCP_COMPARE_INT );
-    setup_timer_1( T1_INTERNAL | T1_DIV_BY_4 );
-}
-
-/**
- *   Function return true once a second based on real-time clock.
- *
- *   @return true on one second tick; otherwise false
- */
-bool_t timeIsUpdate()
-{
-    if (timeUpdateFlag) 
-    {
-        timeUpdateFlag = false;
-        return true;
-    } // END if
-
-    return false;
-}
-
-/**
- *   Set the blink duty cycle of the heartbeat LED.  The LED blinks at a 1Hz rate.
- *
- *   @param dutyCycle TIME_DUTYCYCLE_xx constant
- */
-void timeSetDutyCycle (uint8_t dutyCycle)
-{
-    timeDutyCycle = dutyCycle;
-}
-
-/**
- *   Set a flag to indicate the flight time should run.  This flag is typically set when the payload
- *   lifts off.
+ *
+ *  @{
  */
-void timeSetRunFlag()
-{
-    timeRunFlag = true;
-}
 
-#INT_CCP1
 /**
- *   Timer interrupt handler called every 104uS (9600 times/second).
+ *    Calculate the CRC-16 CCITT of buffer that is length bytes long.
+ *    The crc parameter allow the calculation on the CRC on multiple buffers.
+ *
+ *    @param buffer Pointer to data buffer.
+ *    @param length number of bytes in data buffer
+ *    @param crc starting value
+ *
+ *    @return CRC-16 of buffer[0 .. length]
  */
-void timeUpdate()
+static uint16_t sysCRC16(const uint8_t *buffer, uint8_t length, uint16_t crc)
 {
-    // Setup the next interrupt for the operational mode.
-    timeCompare += TIME_RATE;
-    CCP_1 = timeCompare;
+    uint8_t i, bit, value;
 
-    switch (tncDataMode
+    for (i = 0; i < length; ++i
     {
-        case TNC_MODE_STANDBY:
-            break;
-
-        case TNC_MODE_1200_AFSK:
-            ddsSetFTW (freqTable[timeNCO >> 8]);
-
-            timeNCO += timeNCOFreq;
-
-            if (++timeLowRateCount == 8) 
-            {
-                timeLowRateCount = 0;
-                tnc1200TimerTick();
-            } // END if
-            break;
+        value = buffer[i];
 
-        case TNC_MODE_9600_FSK:
-            tnc9600TimerTick();
-            break;
-    } // END switch
+        for (bit = 0; bit < 8; ++bit) 
+        {
+            crc ^= (value & 0x01);
+            crc = ( crc & 0x01 ) ? ( crc >> 1 ) ^ 0x8408 : ( crc >> 1 );
+            value = value >> 1;
+        } // END for
+    } // END for
 
-    // Read the GPS serial port and save any incoming characters.
-    serialUpdate();
+    return crc ^ 0xffff;
+}
 
-    // Count the number of milliseconds required for the tenth second counter.
-    if (++timeInterruptCount == 960) 
-    {
-        timeInterruptCount = 0;
+/** @} */
 
-        // This timer just ticks every 100mS and is used for general timing.
-        ++timeTicks;
+/**
+ *  @defgroup rtc Real Time Interrupt tick
+ *
+ *  Manage the built-in real time interrupt.  The interrupt clock PRI is 104uS (9600 bps).
+ *
+ *  @{
+ */
 
-        // Roll the counter over every second.
-        if (++time100mS == 10) 
-        {
-            time100mS = 0;
+/// 16-bit NCO where the upper 8-bits are used to index into the frequency generation table.
+static uint16_t timeNCO;
 
-            // We set this flag true every second.
-            timeUpdateFlag = true;
+/// Audio tone NCO update step (phase).
+static uint16_t timeNCOFreq;
 
-            // Maintain a Real Time Clock.
-            if (timeRunFlag)
-                if (++timeSeconds == 60) 
-                {
-                    timeSeconds = 0;
-    
-                    if (++timeMinutes == 60) 
-                    {
-                        timeMinutes = 0;
-                        ++timeHours;
-                    } // END if timeMinutes
-                } // END if timeSeconds
-        } // END if time100mS
-
-        // Flash the status LED at timeDutyCycle % per second.  We use the duty cycle for mode feedback.
-        if (time100mS >= timeDutyCycle)
-            output_low (IO_LED);
-        else
-            output_high (IO_LED);
-    } // END if
+/**
+ *   Initialize the real-time clock.
+ */
+static void timeInit()
+{
+    timeNCO = 0x00;
+    timeNCOFreq = 0x2000;
 }
 
 /** @} */
@@ -2308,10 +230,10 @@ void timeUpdate()
 #define TNC_TX_DELAY 45
 
 /// The size of the TNC output buffer.
-#define TNC_BUFFER_SIZE 80
+#define TNC_BUFFER_SIZE 40
 
 /// States that define the current mode of the 1200 bps (A-FSK) state machine.
-enum TNC_TX_1200BPS_STATE 
+typedef enum
 {
     /// Stand by state ready to accept new message.
     TNC_TX_READY,
@@ -2327,156 +249,73 @@ enum TNC_TX_1200BPS_STATE
 
     /// Transmit the end flag sequence.
     TNC_TX_END
-};
-
-/// Enumeration of the messages we can transmit. 
-enum TNC_MESSAGE_TYPE 
-{
-    /// Startup message that contains software version information.
-    TNC_BOOT_MESSAGE,
-
-    /// Plain text status message.
-    TNC_STATUS,
-
-    /// Message that contains GPS NMEA-0183 $GPGGA message.
-    TNC_GGA,
-
-    /// Message that contains GPS NMEA-0183 $GPRMC message.
-    TNC_RMC
-};
+} TNC_TX_1200BPS_STATE;
 
 /// AX.25 compliant packet header that contains destination, station call sign, and path.
 /// 0x76 for SSID-11, 0x78 for SSID-12
-uint8_t TNC_AX25_HEADER[30] = { 
-    'A' << 1, 'P' << 1, 'R' << 1, 'S' << 1, ' ' << 1, ' ' << 1, 0x60, \
-    'K' << 1, 'D' << 1, '7' << 1, 'L' << 1, 'M' << 1, 'O' << 1, 0x76, \
-    'G' << 1, 'A' << 1, 'T' << 1, 'E' << 1, ' ' << 1, ' ' << 1, 0x60, \
-    'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '3' << 1, ' ' << 1, 0x67, \
+static uint8_t TNC_AX25_HEADER[] = { 
+    'A' << 1, 'P' << 1, 'A' << 1, 'M' << 1, ' ' << 1, ' ' << 1, 0x60, \
+    'N' << 1, '0' << 1, 'C' << 1, 'A' << 1, 'L' << 1, 'L' << 1, 0x78, \
+    'W' << 1, 'I' << 1, 'D' << 1, 'E' << 1, '2' << 1, ' ' << 1, 0x65, \
     0x03, 0xf0 };
 
+#define TNC_CALLSIGN_OFF       7
+#define TNC_CALLSIGN_LEN       6
+
+static void
+tncSetCallsign(void)
+{
+#ifndef AO_APRS_TEST
+       uint8_t i;
+
+       for (i = 0; i < TNC_CALLSIGN_LEN; i++) {
+               if (!ao_config.callsign[i])
+                       break;
+               TNC_AX25_HEADER[TNC_CALLSIGN_OFF + i] = ao_config.callsign[i] << 1;
+       }
+       for (; i < TNC_CALLSIGN_LEN; i++)
+               TNC_AX25_HEADER[TNC_CALLSIGN_OFF + i] = ' ' << 1;
+#endif
+}
 
 /// The next bit to transmit.
-uint8_t tncTxBit;
+static uint8_t tncTxBit;
 
 /// Current mode of the 1200 bps state machine.
-TNC_TX_1200BPS_STATE tncMode;
+static TNC_TX_1200BPS_STATE tncMode;
 
 /// Counter for each bit (0 - 7) that we are going to transmit.
-uint8_t tncBitCount;
+static uint8_t tncBitCount;
 
 /// A shift register that holds the data byte as we bit shift it for transmit.
-uint8_t tncShift;
+static uint8_t tncShift;
 
 /// Index into the APRS header and data array for each byte as we transmit it.
-uint8_t tncIndex;
+static uint8_t tncIndex;
 
 /// The number of bytes in the message portion of the AX.25 message.
-uint8_t tncLength;
+static uint8_t tncLength;
 
 /// A copy of the last 5 bits we've transmitted to determine if we need to bit stuff on the next bit.
-uint8_t tncBitStuff;
-
-/// Pointer to TNC buffer as we save each byte during message preparation.
-uint8_t *tncBufferPnt;
-
-/// The type of message to tranmit in the next packet.
-TNC_MESSAGE_TYPE tncPacketType;
+static uint8_t tncBitStuff;
 
 /// Buffer to hold the message portion of the AX.25 packet as we prepare it.
-uint8_t tncBuffer[TNC_BUFFER_SIZE];
-
-/// Flag that indicates we want to transmit every 5 seconds.
-bool_t tncHighRateFlag;
+static uint8_t tncBuffer[TNC_BUFFER_SIZE];
 
 /** 
  *   Initialize the TNC internal variables.
  */
-void tncInit()
+static void tncInit()
 {
     tncTxBit = 0;
     tncMode = TNC_TX_READY;
-    tncPacketType = TNC_BOOT_MESSAGE;
-    tncHighRateFlag = false;
-}
-
-/**
- *  Determine if the hardware if ready to transmit a 1200 baud packet.
- *
- *  @return true if ready; otherwise false
- */
-bool_t tncIsFree()
-{
-    if (tncMode == TNC_TX_READY)
-        return true;
-
-    return false;
-}
-
-void tncHighRate(bool_t state)
-{
-    tncHighRateFlag = state;
-}
-
-/**
- *   Configure the TNC for the desired data mode.
- *
- *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
- */ 
-void tncSetMode(TNC_DATA_MODE dataMode)
-{
-    switch (dataMode) 
-    {
-        case TNC_MODE_1200_AFSK:
-            ddsSetMode (DDS_MODE_AFSK);
-            break;
-
-        case TNC_MODE_9600_FSK:
-            ddsSetMode (DDS_MODE_FSK);
-
-            // FSK tones at 445.947 and 445.953 MHz
-            ddsSetFSKFreq (955382980, 955453621);
-            break;
-    } // END switch
-
-    tncDataMode = dataMode; 
-}
-
-/**
- *  Determine if the seconds value timeSeconds is a valid time slot to transmit
- *  a message.  Time seconds is in UTC.
- *
- *  @param timeSeconds UTC time in seconds
- *
- *  @return true if valid time slot; otherwise false
- */
-bool_t tncIsTimeSlot (uint8_t timeSeconds)
-{
-    if (tncHighRateFlag)
-    {
-        if ((timeSeconds % 5) == 0)
-            return true;
-
-        return false;
-    } // END if
-
-    switch (timeSeconds) 
-    {
-        case 0:
-        case 15:
-        case 30:
-        case 45:
-            return true;
-
-        default:
-            return false;
-    } // END switch
 }
 
 /**
  *   Method that is called every 833uS to transmit the 1200bps A-FSK data stream.
  *   The provides the pre and postamble as well as the bit stuffed data stream.
  */
-void tnc1200TimerTick()
+static void tnc1200TimerTick()
 {
     // Set the A-FSK frequency.
     if (tncTxBit == 0x00)
@@ -2494,11 +333,12 @@ void tnc1200TimerTick()
         case TNC_TX_SYNC:
             // The variable tncShift contains the lastest data byte.
             // NRZI enocde the data stream.
-            if ((tncShift & 0x01) == 0x00)
+            if ((tncShift & 0x01) == 0x00) {
                 if (tncTxBit == 0)
                     tncTxBit = 1;
                 else
                     tncTxBit = 0;
+           }
                     
             // When the flag is done, determine if we need to send more or data.
             if (++tncBitCount == 8) 
@@ -2534,11 +374,12 @@ void tnc1200TimerTick()
 
             // The variable tncShift contains the lastest data byte.
             // NRZI enocde the data stream.
-            if ((tncShift & 0x01) == 0x00)
+            if ((tncShift & 0x01) == 0x00) {
                 if (tncTxBit == 0)
                     tncTxBit = 1;
                 else
                     tncTxBit = 0;
+           }
 
             // Save the data stream so we can determine if bit stuffing is 
             // required on the next bit time.
@@ -2578,11 +419,12 @@ void tnc1200TimerTick()
 
             // The variable tncShift contains the lastest data byte.
             // NRZI enocde the data stream.
-            if ((tncShift & 0x01) == 0x00)
+            if ((tncShift & 0x01) == 0x00) {
                 if (tncTxBit == 0)
                     tncTxBit = 1;
                 else
                     tncTxBit = 0;
+           }
 
             // Save the data stream so we can determine if bit stuffing is 
             // required on the next bit time.
@@ -2610,11 +452,12 @@ void tnc1200TimerTick()
         case TNC_TX_END:
             // The variable tncShift contains the lastest data byte.
             // NRZI enocde the data stream. 
-            if ((tncShift & 0x01) == 0x00)
+            if ((tncShift & 0x01) == 0x00) {
                 if (tncTxBit == 0)
                     tncTxBit = 1;
                 else
                     tncTxBit = 0;
+           }
 
             // If all the bits were shifted, get the next one.
             if (++tncBitCount == 8) 
@@ -2627,14 +470,6 @@ void tnc1200TimerTick()
                 {
                     tncMode = TNC_TX_READY;
 
-                    // Tell the TNC time interrupt to stop generating the frequency words.
-                    tncDataMode = TNC_MODE_STANDBY;
-
-                    // Key off the DDS.
-                    output_low (IO_OSK);
-                    output_low (IO_PTT);
-                    ddsSetMode (DDS_MODE_POWERDOWN);
-
                     return;
                 } // END if
             } else
@@ -2645,277 +480,111 @@ void tnc1200TimerTick()
 }
 
 /**
- *   Method that is called every 104uS to transmit the 9600bps FSK data stream.
- */
-void tnc9600TimerTick()
-{
-
-}
-
-/**
- *    Write character to the TNC buffer.  Maintain the pointer
- *    and length to the buffer.  The pointer tncBufferPnt and tncLength
- *    must be set before calling this function for the first time.
- * 
- *    @param character to save to telemetry buffer
- */
-void tncTxByte (uint8_t character)
-{
-    *tncBufferPnt++ = character;
-    ++tncLength;
-}
-
-/**
- *   Generate the GPS NMEA standard UTC time stamp.  Data is written through the tncTxByte
- *   callback function.
+ *   Generate the plain text position packet.
  */
-void tncNMEATime()
+static int tncPositionPacket(void)
 {
-    // UTC of position fix.
-    printf (tncTxByte, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
-}
+    int32_t    latitude = ao_gps_data.latitude;
+    int32_t    longitude = ao_gps_data.longitude;
+    int32_t    altitude = ao_gps_data.altitude;
 
-/**
- *   Generate the GPS NMEA standard latitude/longitude fix.  Data is written through the tncTxByte
- *   callback function.
- */
-void tncNMEAFix()
-{
-    uint8_t dirChar;
-    uint32_t coord, coordMin;
+    uint16_t   lat_deg;
+    uint16_t   lon_deg;
+    uint16_t   lat_min;
+    uint16_t   lat_frac;
+    uint16_t   lon_min;
+    uint16_t   lon_frac;
 
-    // Latitude value.
-    coord = gpsPosition.latitude;
+    char       lat_sign = 'N', lon_sign = 'E';
 
-    if (gpsPosition.latitude < 0) 
-    {
-        coord = gpsPosition.latitude * -1;
-        dirChar = 'S';
-    } else {
-        coord = gpsPosition.latitude;
-        dirChar = 'N';
+    if (latitude < 0) {
+       lat_sign = 'S';
+       latitude = -latitude;
     }
 
-    coordMin = (coord % 3600000) / 6;
-    printf (tncTxByte, "%02ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
-
-
-    // Longitude value.
-    if (gpsPosition.longitude < 0) 
-    {
-        coord = gpsPosition.longitude * - 1;
-        dirChar = 'W';
-    } else {
-        coord = gpsPosition.longitude;
-        dirChar = 'E';
+    if (longitude < 0) {
+       lon_sign = 'W';
+       longitude = -longitude;
     }
 
-    coordMin = (coord % 3600000) / 6;
-    printf (tncTxByte, "%03ld%02ld.%04ld,%c,", (uint32_t) (coord / 3600000), (uint32_t) (coordMin / 10000), (uint32_t) (coordMin % 10000), dirChar);
-
-}
-
-/**
- *   Generate the GPS NMEA-0183 $GPGGA packet.  Data is written through the tncTxByte
- *   callback function.
- */
-void tncGPGGAPacket()
-{
-    // Generate the GPGGA message.
-    printf (tncTxByte, "$GPGGA,");
-
-    // Standard NMEA time.
-    tncNMEATime();
-
-    // Standard NMEA-0183 latitude/longitude.
-    tncNMEAFix();
-
-    // GPS status where 0: not available, 1: available
-    if (gpsGetFixType() != GPS_NO_FIX)
-        printf (tncTxByte, "1,");
-    else
-        printf (tncTxByte, "0,");
-
-    // Number of visible birds.
-    printf (tncTxByte, "%02d,", gpsPosition.trackedSats);
-
-    // DOP
-    printf (tncTxByte, "%ld.%01ld,", gpsPosition.dop / 10, gpsPosition.dop % 10);
-
-    // Altitude in meters.
-    printf (tncTxByte, "%ld.%02ld,M,,M,,", (int32_t) (gpsPosition.altitudeCM / 100l), (int32_t) (gpsPosition.altitudeCM % 100));
-
-    // Checksum, we add 1 to skip over the $ character.
-    printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
-}
-
-/**
- *   Generate the GPS NMEA-0183 $GPRMC packet.  Data is written through the tncTxByte
- *   callback function.
- */
-void tncGPRMCPacket()
-{
-    uint32_t temp;
-
-    // Generate the GPRMC message.
-    printf (tncTxByte, "$GPRMC,");
-
-    // Standard NMEA time.
-    tncNMEATime();
-
-    // GPS status.
-    if (gpsGetFixType() != GPS_NO_FIX)
-        printf (tncTxByte, "A,");
-    else
-        printf (tncTxByte, "V,");
-
-    // Standard NMEA-0183 latitude/longitude.
-    tncNMEAFix();
-
-    // Speed knots and heading.
-    temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826;
-    printf (tncTxByte, "%ld.%ld,%ld.%ld,", (int16_t) (temp / 10), (int16_t) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10);
-
-    // Date
-    printf (tncTxByte, "%02d%02d%02ld,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100);
-
-    // Checksum, skip over the $ character.
-    printf (tncTxByte, "*%02X", gpsNMEAChecksum(tncBuffer + 1, tncLength - 1));
+    /* Round latitude and longitude by 0.005 minutes */
+    latitude = latitude + 833;
+    if (latitude > 900000000)
+       latitude = 900000000;
+    longitude = longitude + 833;
+    if (longitude > 1800000000)
+           longitude = 1800000000;
+
+    lat_deg = latitude / 10000000;
+    latitude -= lat_deg * 10000000;
+    latitude *= 60;
+    lat_min = latitude / 10000000;
+    latitude -= lat_min * 10000000;
+    lat_frac = latitude / 100000;
+
+    lon_deg = longitude / 10000000;
+    longitude -= lon_deg * 10000000;
+    longitude *= 60;
+    lon_min = longitude / 10000000;
+    longitude -= lon_min * 10000000;
+    lon_frac = longitude / 100000;
+
+    if (altitude < 0)
+       altitude = 0;
+
+    altitude = (altitude * (int32_t) 10000 + (3048/2)) / (int32_t) 3048;
+    
+    return sprintf ((char *) tncBuffer, "=%02u%02u.%02u%c\\%03u%02u.%02u%cO /A=%06u\015",
+                   lat_deg, lat_min, lat_frac, lat_sign,
+                   lon_deg, lon_min, lon_frac, lon_sign,
+                   altitude);
+}
+
+static int16_t
+tncFill(uint8_t *buf, int16_t len)
+{
+    int16_t    l = 0;
+    uint8_t    b;
+    uint8_t    bit;
+
+    while (tncMode != TNC_TX_READY && l < len) {
+       b = 0;
+       for (bit = 0; bit < 8; bit++) {
+           b = b << 1 | (timeNCO >> 15);
+           timeNCO += timeNCOFreq;
+       }
+       *buf++ = b;
+       l++;
+       tnc1200TimerTick();
+    }
+    if (tncMode == TNC_TX_READY)
+       l = -l;
+    return l;
 }
 
-/**
- *   Generate the plain text status packet.  Data is written through the tncTxByte
- *   callback function.
- */
-void tncStatusPacket(int16_t temperature)
-{
-    uint16_t voltage;
-
-    // Plain text telemetry.
-    printf (tncTxByte, ">ANSR ");
-    
-    // Display the flight time.
-    printf (tncTxByte, "%02U:%02U:%02U ", timeHours, timeMinutes, timeSeconds);
-    
-    // Altitude in feet.
-    printf (tncTxByte, "%ld' ", gpsPosition.altitudeFeet);
-    
-    // Peak altitude in feet.
-    printf (tncTxByte, "%ld'pk ", gpsGetPeakAltitude());
-    
-    // GPS hdop or pdop
-    printf (tncTxByte, "%lu.%lu", gpsPosition.dop / 10, gpsPosition.dop % 10);
-
-    // The text 'pdop' for a 3D fix, 'hdop' for a 2D fix, and 'dop' for no fix.
-    switch (gpsGetFixType()) 
-    {
-        case GPS_NO_FIX:
-            printf (tncTxByte, "dop ");
-            break;
-
-        case GPS_2D_FIX:
-            printf (tncTxByte, "hdop ");
-            break;
-
-
-        case GPS_3D_FIX:
-            printf (tncTxByte, "pdop ");
-            break;
-    } // END switch
-
-    // Number of satellites in the solution.
-    printf (tncTxByte, "%utrk ", gpsPosition.trackedSats);
-    
-    // Display main bus voltage.
-    voltage = adcGetMainBusVolt();
-    printf (tncTxByte, "%lu.%02luvdc ", voltage / 100, voltage % 100);
-    
-    // Display internal temperature.
-    printf (tncTxByte, "%ld.%01ldF ", temperature / 10, abs(temperature % 10));
-    
-    // Print web address link.
-    printf (tncTxByte, "www.kd7lmo.net");
-}  
-
 /** 
  *    Prepare an AX.25 data packet.  Each time this method is called, it automatically
  *    rotates through 1 of 3 messages.
  *
  *    @param dataMode enumerated type that specifies 1200bps A-FSK or 9600bps FSK
  */
-void tncTxPacket(TNC_DATA_MODE dataMode)
+void ao_aprs_send(void)
 {
-    int16_t temperature;
     uint16_t crc;
 
-    // Only transmit if there is not another message in progress.
-    if (tncMode != TNC_TX_READY)
-        return;
-
-    // Log the battery and reference voltage before we start the RF chain.
-    sysLogVoltage();
-
-    // We need to read the temperature sensor before we setup the DDS since they share a common clock pin.
-    temperature = lm92GetTemp();
-
-    // Log the system temperature every time we transmit a packet.
-    logType (LOG_TEMPERATURE);
-    logInt16 (temperature);
-
-    // Configure the DDS for the desired operational.
-    tncSetMode (dataMode);
-
-    // Set a pointer to our TNC output buffer.
-    tncBufferPnt = tncBuffer;
-
-    // Set the message length counter.
-    tncLength = 0;
-
-    // Determine the contents of the packet.
-    switch (tncPacketType) 
-    {
-        case TNC_BOOT_MESSAGE:
-            printf (tncTxByte, ">ANSR Pico Beacon - V3.05");
-
-            // Select the next packet we will generate.
-            tncPacketType = TNC_STATUS;
-            break;
-
-        case TNC_STATUS:
-            tncStatusPacket(temperature);
-
-            // Select the next packet we will generate.
-            tncPacketType = TNC_GGA;
-            break;
-
-        case TNC_GGA:
-            tncGPGGAPacket();
-
-            // Select the next packet we will generate.
-            tncPacketType = TNC_RMC;
-            break;
-
-        case TNC_RMC:
-            tncGPRMCPacket();
-
-            // Select the next packet we will generate.
-            tncPacketType = TNC_STATUS;
-            break;
-    }
+    timeInit();
+    tncInit();
+    tncSetCallsign();
 
-    // Add the end of message character.
-    printf (tncTxByte, "\015");
+    tncLength = tncPositionPacket();
 
     // Calculate the CRC for the header and message.
     crc = sysCRC16(TNC_AX25_HEADER, sizeof(TNC_AX25_HEADER), 0xffff);
     crc = sysCRC16(tncBuffer, tncLength, crc ^ 0xffff);
 
     // Save the CRC in the message.
-    *tncBufferPnt++ = crc & 0xff;
-    *tncBufferPnt = (crc >> 8) & 0xff;
-
-    // Update the length to include the CRC bytes.
-    tncLength += 2;
+    tncBuffer[tncLength++] = crc & 0xff;
+    tncBuffer[tncLength++] = (crc >> 8) & 0xff;
 
     // Prepare the variables that are used in the real-time clock interrupt.
     tncBitCount = 0;
@@ -2924,179 +593,7 @@ void tncTxPacket(TNC_DATA_MODE dataMode)
     tncIndex = 0;
     tncMode = TNC_TX_SYNC;
 
-    // Turn on the PA chain.
-    output_high (IO_PTT);
-
-    // Wait for the PA chain to power up.
-    delay_ms (10);
-
-    // Key the DDS.
-    output_high (IO_OSK);
-
-    // Log the battery and reference voltage just after we key the transmitter.
-    sysLogVoltage();
+    ao_radio_send_aprs(tncFill);
 }
 
 /** @} */
-
-uint32_t counter;
-
-uint8_t bitIndex;
-uint8_t streamIndex;
-uint8_t value;
-
-uint8_t bitStream[] = { 0x10, 0x20, 0x30 };
-
-void init()
-{
-    counter = 0;
-    bitIndex = 0;
-    streamIndex = 0;
-    value = bitStream[0];
-}
-
-void test()
-{
-    counter += 0x10622d;
-
-    CCP_1 = (uint16_t) ((counter >> 16) & 0xffff);
-
-    if ((value & 0x80) == 0x80)
-        setup_ccp1 (CCP_COMPARE_SET_ON_MATCH);
-    else
-        setup_ccp1 (CCP_COMPARE_CLR_ON_MATCH);
-
-    if (++bitIndex == 8)
-    {
-        bitIndex = 0;
-        
-        if (++streamIndex == sizeof(bitStream))
-        {
-            streamIndex = 0;
-        }
-
-        value = bitStream[streamIndex];
-    } else
-        value = value << 1;
-}
-
-// This is where we go after reset.
-void main()
-{
-    uint8_t i, utcSeconds, lockLostCounter;
-
-test();
-
-    // Configure the basic systems.
-    sysInit();
-
-    // Wait for the power converter chains to stabilize.
-    delay_ms (100);
-
-    // Setup the subsystems.
-    adcInit();
-    flashInit();
-    gpsInit();
-    logInit();
-    timeInit();
-    serialInit();
-    tncInit();
-
-    // Program the DDS.
-    ddsInit();
-
-    // Turn off the LED after everything is configured.
-    output_low (IO_LED);
-
-    // Check for the diagnostics plug, otherwise we'll continue to boot.
-    diagPort();
-
-    // Setup our interrupts.
-    enable_interrupts(GLOBAL);
-    enable_interrupts(INT_CCP1);
-
-    // Turn on the GPS engine.
-    gpsPowerOn();
-
-    // Allow the GPS engine to boot.
-    delay_ms (250);
-
-    // Initialize the GPS engine.
-    while (!gpsSetup());
-
-    // Charge the ADC filters.
-    for (i = 0; i < 32; ++i)
-        adcUpdate();
-
-    // Log startup event.
-    logType (LOG_BOOTED);
-    logUint8 (gpsPosition.month);
-    logUint8 (gpsPosition.day);
-    logUint8 (gpsPosition.year & 0xff);
-
-    logUint8 (gpsPosition.hours);
-    logUint8 (gpsPosition.minutes);
-    logUint8 (gpsPosition.seconds);
-
-    // Transmit software version packet on start up.
-    tncTxPacket(TNC_MODE_1200_AFSK);
-
-    // Counters to send packets if the GPS time stamp is not available.
-    lockLostCounter = 5;
-    utcSeconds = 55;
-  
-    // This is the main loop that process GPS data and waits for the once per second timer tick.
-    for (;;) 
-    {
-        // Read the GPS engine serial port FIFO and process the GPS data.
-        gpsUpdate();
-
-        if (gpsIsReady()) 
-        {
-            // Start the flight timer when we get a valid 3D fix.
-            if (gpsGetFixType() == GPS_3D_FIX)
-                timeSetRunFlag();
-
-            // Generate our packets based on the GPS time.
-            if (tncIsTimeSlot(gpsPosition.seconds))
-                 tncTxPacket(TNC_MODE_1200_AFSK);
-
-            // Sync the internal clock to GPS UTC time.
-            utcSeconds = gpsPosition.seconds;
-
-            // This counter is reset every time we receive the GPS message.
-            lockLostCounter = 0;
-
-            // Log the data to flash.
-            sysLogGPSData();            
-        } // END if gpsIsReady   
-
-        // Processing that occurs once a second.
-        if (timeIsUpdate()) 
-        {
-            // We maintain the UTC time in seconds if we shut off the GPS engine or it fails.
-            if (++utcSeconds == 60)
-                utcSeconds = 0;
-
-            // If we loose information for more than 5 seconds, 
-            // we will determine when to send a packet based on internal time.
-            if (lockLostCounter == 5) 
-            {
-                if (tncIsTimeSlot(utcSeconds))
-                    tncTxPacket(TNC_MODE_1200_AFSK);
-            } else
-                ++lockLostCounter;
-
-            // Update the ADC filters.
-            adcUpdate();
-
-            if (timeHours == 5 && timeMinutes == 0 && timeSeconds == 0)
-                gpsPowerOff();
-
-        } // END if timeIsUpdate
-
-    } // END for
-}
-
-
-