//
//  Name:         ac.c
//
//
//  Revision History
//
//    04 Feb 2002  V1.00  Initial release.
//
//    30 Mar 2002  V1.01  Incremental release for web site.
//
//    21 Nov 2002  V1.02  Flight tested.  Incremental release for web site.
//
//    11 Dec 2002  V1.03  Incremental release for web site.
//
//    09 Feb 2003  V1.04  Payload E flight test, and
//                        added camera point/shoot control.
//
//    09 Apr 2003  V1.05  Fixed NMEA-0183 checksum calculation and converted to upper case,
//                        removed heading for magnetic corection field in $GPRMC message,
//                        removed support for electronic attitude indicator,
//                        inverted APRS/Com frequency selection, and
//                        removed support for camera.
//
//    25 Mar 2006  V1.06  Updates to work as APRS beacon.
//
//
//  Copyright (c) 2002-2006 Michael Gray, KD7LMO
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//

// Disable debug information because it is hard to run a debug cable 20 miles to the aircraft.
#nodebug
#memmap xmem

// PROGRAMMING NOTE: Since we don't have embedded C++ for this processor,
// we try to use C in a manner that emulates classes and methods.  Each method
// and variable is prefaced by the name of the class.  The classes
// have a method that acts as a constructor and sometimes a destructor.
// Even though variables are declared as global, they are only accessed
// through the appropriate class method.  The order of the classes and
// variables is not important in the application because we only access
// the class through a method.

// To avoid confusion in the sizes of short, int, and long on 8, 16, and 32 bit processors,
// we will be very explicit.  The typedefs MUST be checked when cross compiling or changing
// compilers.  NOTE: There are others, i.e. uint8, uint16_t, int16_t, etc. that are defined
// in a system library.
typedef char bool;
typedef char int8_t;
typedef unsigned char uint8_t;
typedef int16 int16_t;
typedef uint16 uint16_t;
typedef long int32_t;
typedef unsigned long uint32_t;

// Boolean flags.
#define TRUE 1
#define FALSE 0

// Allocate buffers for the serial ports.
#define AINBUFSIZE 15
#define AOUTBUFSIZE 15

#define CINBUFSIZE 255
#define COUTBUFSIZE 255

#define DINBUFSIZE 255
#define DOUTBUFSIZE 255


// Public methods and data structures for each class.
typedef struct {
    uint16_t faultCount, badHeaderCRCCount, invalidHeaderCount, badDataCRCCount, bufferOverflow;
    uint16_t carrierDetectCount, rxPacketCount;
    uint16_t txPacketCount;
    uint8_t lastRxSQE;
} COM_STATS;

typedef struct {
    bool reverseFlag;
    uint8_t min, max, nominal;
} CONFIG_SERVO_INFO;

typedef struct {
    bool updateFlag;
    uint8_t month, day, hours, minutes, seconds;
    uint16_t year;
    int32_t latitude, longitude, altitude, altitudeFeet;
    uint16_t vSpeed, hSpeed, heading, dop, status;
    uint8_t trackedSats, visibleSats;
    int32_t lastAltitude;
    int16_t vSpeedAccum;
} GPSPOSITION;

uint8_t analogRead();
void analogWrite (uint8_t value);

void configCalcCRC();
void configDefault();
uint8_t configInit();
uint8_t configGetComTxDelay();
int16_t configGetPitchOffset();
int16_t configGetRollOffset();
uint16_t configGetGyroOffset();
void configSetGyroOffset(uint16_t offset);
CONFIG_SERVO_INFO *configGetServoInfo();
void configSetPitchOffset(int16_t offset);
void configSetRollOffset(int16_t offset);

void flightInit();
void flightProcessCommand (uint8_t *message);
void flightProcessGPS();
void flightRun();
void flightSendLogPacket();

void gpsClearUpdateFlag();
GPSPOSITION *gpsGetData();
void gpsInit();
uint8_t gpsIsUpdated();
void gpsReadData();
void gpsSendMessage (uint8_t *message, uint8_t length);

void sysChan1();
void sysChan2();
uint16_t sysCRC16 (uint8_t *buffer, uint16_t length);
void sysDelay (uint32_t delayMS);
void sysInit();
void sysInterruptEnable();
bool sysIsCarrierDetect();
void sysLEDOff();
void sysLEDOn();
uint8_t sysNMEAChecksum (uint8_t *buffer, uint16_t length);
uint8_t sysParseHexDigit(uint8_t digit);
void sysPTTOff();
void sysPTTOn();

root void sysRuntimeErrHandler();

void sysSelfTest();
int16_t sysStringToScaledInt(char *buffer, char **newBuffer);
root interrupt void sysTimerISR();
void sysTimeTick();

void tempInit();
int16_t tempGetExtTemp();
uint8_t tempWriteAndGetAck(uint8_t data);
uint8_t tempReadWithAck (bool ack);
void tempMasterStart();
void tempMasterStop();

uint8_t tlmGetRate();
void tlmGPGGAPacket();
void tlmGPRMCPacket();
void tlmInit();
bool tlmIsAPRSActive();
bool tlmSetRate(uint8_t rate);
void tlmStatusPacket();
void tlmUpdateTick();

void tncSendPacket(uint8_t *message);

root interrupt void tncTimerISR();

/**
 *   Class to handle configuration items.
 */
typedef struct {
    uint16_t crc;
    uint8_t tncTxDelay, comTxDelay;
    uint8_t callSign[7], destCallSign[7], relay1CallSign[7], relay2CallSign[7];
    uint8_t callSignBalloonSSID, callSignAircraftSSID, relay1SSID, relay2SSID;
    uint16_t bootCount;


} CONFIG_STRUCT;

protected CONFIG_STRUCT config;

/**
 *    Set the default configuration parameters.
 */
void configDefault()
{
    uint8_t i;

    // Clear everything in case we don't initialize something.
    memset (&config, 0, sizeof(CONFIG_STRUCT));

    // Station ID, relay path, and destination call sign and SSID.
    strcpy (config.callSign, "KD7LMO");
    config.callSignBalloonSSID = 12;
    config.callSignAircraftSSID = 7;

    strcpy (config.relay1CallSign, "GATE  ");
    config.relay1SSID = 0;

    strcpy (config.relay2CallSign, "WIDE3 ");
    config.relay2SSID = 3;

    strcpy (config.destCallSign, "APRS  ");

    // Number of TNC flag bytes sent before data stream starts.  1 byte = 6.6mS
    config.tncTxDelay = 45;

    // Number of mS before data is sent over the high-speed com link.
    config.comTxDelay = 60;

    // Count the number of system boots.
    config.bootCount = 0;
}

/**
 *    Calculate and set the configuration parameter block CRC.
 */
void configCalcCRC()
{
    config.crc = sysCRC16 ((uint8_t *) &config + 2, sizeof(CONFIG_STRUCT) - 2);
}

uint8_t configGetComTxDelay()
{
    return config.comTxDelay;
}

/**
 *   Get the station callsign.
 *
 *   @param pointer to callsign string
 */
uint8_t *configGetCallSign()
{
    return config.callSign;
}

/**
 *   Get the target callsign for the AX.25 packet.
 *
 *   @param pointer to callsign string
 */
uint8_t *configGetDestCallSign()
{
    return config.destCallSign;
}

uint8_t *configGetRelay1CallSign()
{
    return config.relay1CallSign;
}

uint8_t *configGetRelay2CallSign()
{
    return config.relay2CallSign;
}

uint8_t configGetRelay1SSID()
{
    return config.relay1SSID;
}

uint8_t configGetRelay2SSID()
{
    return config.relay2SSID;
}

uint8_t configGetBalloonSSID()
{
    return config.callSignBalloonSSID;
}

/**
 *  Get the TNC transmit delay period.
 *
 *  @return TNC transmit delay period in 8-bit time period
 */
uint8_t configGetTNCTxDelay()
{
    return config.tncTxDelay;
}

/**
 *    Initialize configuration subsystem.
 */
uint8_t configInit()
{
    // If the configuration CRC is not valid, then set default vaules.
    if (config.crc != sysCRC16((uint8_t *) &config + 2, sizeof(CONFIG_STRUCT) - 2)) {
        configDefault();
        configCalcCRC();
        return FALSE;
    }

    ++config.bootCount;
    configCalcCRC();

    return TRUE;
}

/**
 *   Class to handle the GPS receiver.
 */
#define GPS_START 0
#define GPS_START2 1
#define GPS_COMMAND 2
#define GPS_COMMAND_POSITION 3
#define GPS_COMMAND_RXID 4
#define GPS_READMESSAGE 5
#define GPS_READID 6
#define GPS_CHECKSUMMESSAGE 7
#define GPS_EOMCR 8
#define GPS_EOMLF 9

#define GPS_ID_LENGTH 288

// Structures that stores the GPS information.
GPSPOSITION gpsPosition;

// State machine used to parse GPS data stream.
uint8_t gpsMode;

// Pointer to buffer that holds GPS messages.
uint16_t gpsIndex;

// Verifies checksum of message from GPS.
uint8_t gpsChecksum;

// Buffers to hold GPS data.
uint8_t gpsBuffer[80], gpsID[GPS_ID_LENGTH];

// Flag to indicate the GPS ID is ready.
bool gpsIDFlag;

/**
 *   Clear the flag that indicates the GPS ID string has been received.
 */
void gpsClearIDFlag()
{
    gpsIDFlag = FALSE;
}

/**
 *   Clear the flag that indicates the GPS data has been processed.  When a new
 *   GPS message is received the flag is set.
 */
void gpsClearUpdateFlag()
{
    gpsPosition.updateFlag = FALSE;
}

/**
 *   Get a pointer to the data structure of last valid set of GPS data.  The structure
 *   contains all GPS information including position, time of day, and solution accuracy.
 *
 *   @return pointer to last valid set of GPS data
 */
GPSPOSITION *gpsGetData()
{
    return &gpsPosition;
}

char *gpsGetID()
{
    return gpsID;
}

/**
 *   Configure the GPS receiver.
 */
void gpsInit()
{
    uint8_t i;

    // Clear the structure that stores the position message and ID.
    for (i = 0; i < sizeof(GPSPOSITION); ++i)
        *((uint8_t *) &gpsPosition + i) = 0;

    // Clear the memory that stores the GPS ID string.
    strcpy (gpsID, "");
    gpsIDFlag = FALSE;

    // State machine used for parsing the binary GPS string.
    gpsMode = GPS_START;
}

/**
 *    Determine if the GPS ID has been updated since the <b>gpsClearIDFlag</b> method
 *    was last called.
 *
 *    @return true if GPS ID is ready; otherwise false
 */
uint8_t gpsIsIDReady()
{
    return gpsIDFlag;
}

/**
 *    Determine if the GPS data has been updated since the <b>gpsClearUpdateFlag</b> method
 *    was last called.
 *
 *    @return true if GPS data is updated; otherwise false
 */
uint8_t gpsIsUpdated()
{
    return gpsPosition.updateFlag;
}

/**
 *    Sends a binary message to the GPS receiver.  The message
 *    contains only the data between the start of message '@@' and
 *    the checksum end EOM.  THe rest is generated by this method.
 *
 *    @param message pointer to array of binary data
 *    @param length length of message in bytes
 */
void gpsSendMessage (uint8_t *message, uint8_t length)
{
    uint8_t i, checksum;

    // Send the message start characters.
    serCputc ('@');
    serCputc ('@');

    // Send each character while calculating the checksum.
    checksum = 0;

    for (i = 0; i < length; ++i) {
        checksum ^= message[i];
        serCputc (message[i]);
    } // END for

    // Now send the checksum, <CR>, and <LF>.
    serCputc (checksum);
    serCputc (13);
    serCputc (10);
}

/**
 *   Parse the @@Hb (Short position/message) report.
 *
 */
void gpsParsePositionMessage()
{
	char buffer[80];

    // 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 = (gpsBuffer[2] << 8) | gpsBuffer[3];
    gpsPosition.hours = gpsBuffer[4];
    gpsPosition.minutes = gpsBuffer[5];
    gpsPosition.seconds = gpsBuffer[6];
    gpsPosition.latitude = ((int32_t) gpsBuffer[11] << 24) | ((int32_t) gpsBuffer[12] << 16) | ((int32_t) gpsBuffer[13] << 8) | (int32_t) gpsBuffer[14];
    gpsPosition.longitude = ((int32_t) gpsBuffer[15] << 24) | ((int32_t) gpsBuffer[16] << 16) | ((int32_t) gpsBuffer[17] << 8) | (int32_t) gpsBuffer[18];
    gpsPosition.altitude = ((int32_t) gpsBuffer[19] << 24) | ((int32_t) gpsBuffer[20] << 16) | ((int32_t) gpsBuffer[21] << 8) | (int32_t) gpsBuffer[22];
    gpsPosition.altitudeFeet = gpsPosition.altitude * 100l / 3048l;
    gpsPosition.vSpeed = (gpsBuffer[27] << 8) | gpsBuffer[28];
    gpsPosition.hSpeed = (gpsBuffer[29] << 8) | gpsBuffer[30];
    gpsPosition.heading = (gpsBuffer[31] << 8) | gpsBuffer[32];
    gpsPosition.dop = (gpsBuffer[33] << 8) | gpsBuffer[34];
    gpsPosition.visibleSats = gpsBuffer[35];
    gpsPosition.trackedSats = gpsBuffer[36];
    gpsPosition.status = (gpsBuffer[37] << 8) | gpsBuffer[38];

    // Generate a VSI (Vertical Speed Indicator).
    gpsPosition.vSpeedAccum = (int16_t) (gpsPosition.altitude - gpsPosition.lastAltitude) + gpsPosition.vSpeedAccum - (gpsPosition.vSpeedAccum >> 2);
    gpsPosition.lastAltitude = gpsPosition.altitude;
}

/**
 *   Read and validate the GPS message.  When a valid message has been read, the structure
 *   <b>gpsMessage</b> is updated.
 */
void gpsReadData()
{
    uint16_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<CR><LF>
    while ((value = serCgetc()) != -1)
        switch (gpsMode) {
            // Wait for the first @
            case GPS_START:
                if (value == '@')
                    gpsMode = GPS_START2;
                break;

            case GPS_START2:
                if (value == '@')
                    gpsMode = GPS_COMMAND;
                else
                    gpsMode = GPS_START;
                break;

            case GPS_COMMAND:
                if (value == 'H')
                    gpsMode = GPS_COMMAND_POSITION;
                else if (value == 'C')
                    gpsMode = GPS_COMMAND_RXID;
                else
                    gpsMode = GPS_START;
                break;

            case GPS_COMMAND_POSITION:
                if (value == 'b') {
                    gpsMode = GPS_READMESSAGE;
                    gpsIndex = 0;
                    gpsChecksum = 0;
                    gpsChecksum ^= 'H';
                    gpsChecksum ^= 'b';
                } else
                    gpsMode = GPS_START;
                break;

            case GPS_COMMAND_RXID:
                if (value == 'j') {
                    gpsMode = GPS_READID;
                    gpsIndex = 0;
                } else
                    gpsMode = GPS_START;
                break;

            case GPS_READMESSAGE:
                gpsChecksum ^= value;
                gpsBuffer[gpsIndex++] = value;

                if (gpsIndex == 47)
                    gpsMode = GPS_CHECKSUMMESSAGE;

                break;

            case GPS_READID:
                // Save each byte of the string.
                gpsID[gpsIndex] = value;

                // Once all the data is read, set a flag to indicate it is ready.
                if (++gpsIndex == GPS_ID_LENGTH - 1) {
                    gpsID[gpsIndex] = 0;
                    gpsIDFlag = TRUE;
                    gpsMode = GPS_START;
                }
                break;

            case GPS_CHECKSUMMESSAGE:
                if (gpsChecksum == value)
                    gpsMode = GPS_EOMCR;
                else
                    gpsMode = GPS_START;
                break;

            case GPS_EOMCR:
                if (value == 13)
                    gpsMode = GPS_EOMLF;
                else
                    gpsMode = GPS_START;
                break;

            case GPS_EOMLF:
                // Once we have the last character, convert the binary message to something usable.
                if (value == 10)
                    gpsParsePositionMessage();

                gpsMode = GPS_START;
                break;
    } // END switch
}

// Define the I/O pins.
#define SYS_LED 2
#define SYS_CHAN 4
#define SYS_PTT 6
#define SYS_CARRIERDETECT 2

// CLK_RATE, Timer A5 value to generate 2mS interrupt
// 115 * (1 / 11.0592) * 192 = 2mS
// 38 * (1 / 3.6864) * 192 = 2mS
#define SYS_TIMER_A5_DELAY 115

// CLK_RATE, Timer A1 value used to feed timer A5 and B.
#define SYS_TIMER_A1_DELAY 191

// Heartbeat LED counter.
uint16_t sysLEDCounter;

// System operation time in seconds.
uint16_t sysUptime;

/**
 *    Wait <b>delayMS</b> before returning.
 *
 *    @param delayMS delay time in milliseconds
 */
void sysDelay(uint32_t delayMS)
{
    uint32_t timerTick;

    timerTick = MS_TIMER;
    while (MS_TIMER - timerTick < delayMS);
}

/**
 *    Return the number of seconds since the application was started.
 *
 *    @return uptime in seconds
 */
uint16_t sysGetUptime()
{
    return sysUptime;
}

/**
 *   Initialize the internal system controls.
 */
void sysInit()
{
    // ******  Configure PORT-A ******
    // Set all output pins with LED on to indicate startup.
    WrPortI(PADR, &PADRShadow, 0x04);
    WrPortI(SPCR, NULL, 0x84);


    // ******  Configure PORT-D ******
    // All outputs LOW.
    WrPortI(PDDR, &PDDRShadow, 0x00);

    // All ports are input.
    WrPortI(PDDDR, &PDDDRShadow, 0x00);

    // PORT D all open drain.
    WrPortI(PDDCR, NULL, 0xff);

    // Clock everything on PCLK/2
    WrPortI(PDCR, NULL, 0x00);

    //
    WrPortI(PDFR, NULL, 0x00);


    // ******  Configure PORT-E for COM radio and general I/O ******
    // Configure PE7 as CS for external I/O.
    WrPortI (IB7CR, NULL, 0x48);

    // Set PE2, PE4, PE5, and PE7 as outputs.
    WrPortI (PEDDR, NULL, 0xb4);

    // Configure PE7 as I/O strobe line.
    WrPortI (PEFR, NULL, 0x80);

    // Clock PE4-7 bits on timer B1 interrupt.
    WrPortI(PECR, NULL, 0x20);


    // Configure serial port for servo control, GPS, and debug port.
    serAopen (38400);
    serCopen (9600);
    serDopen (38400);


    // ****** Configure Timer A for heartbeat interrupt ******
    // Set the timer A ISR.
    SetVectIntern (0x0a, sysTimerISR);

    // Set Timer A1 value that feeds timer A5 and B.
    // rate = (timerValue + 1) * clockPeriod
    WrPortI (TAT1R, &TAT1RShadow, SYS_TIMER_A1_DELAY);

    // Set timer A5 value that provides 2mS heartbeat interrupt.
    WrPortI (TAT5R, &TAT5RShadow, SYS_TIMER_A5_DELAY);


    // ****** Configure Timer B for TNC 1200 baud interrupt ******
    // Set the timer B ISR.
    SetVectIntern (0x0b, tncTimerISR);

    // Setup our system variables.
    sysLEDCounter = 0;
    sysUptime = 0;

    serAputs ("System booted!\n\r");
}

/**
 *   Enable the system interrupts.
 */
void sysInterruptEnable()
{
    // External interrupt priority 1 on INT0A, INT1A falling edge.
    WrPortI (I0CR, NULL, 0x05);
    WrPortI (I1CR, NULL, 0x05);

    // Clear the interrupt pending flag.
    RdPortI (TACSR);

    // Timer A5 clocked by timer A1, all other timers clocked by pclk/2, interrupt priority 2.
    WrPortI (TACR, &TACRShadow, 0x22);

    // Enable timer A1 and timer A5 interrupts.
    WrPortI (TACSR, &TACSRShadow, 0x21);

    // Timer B clocked by timer A1, interrupt priority 3.
    WrPortI (TBCR, NULL, 0x07);

    // Clear the timer B compare register to 0 for the next interrupt.
    WrPortI (TBM1R, NULL, 0);
    WrPortI (TBL1R, NULL, 0);

    // Clear the interrupt pending flag.
    RdPortI (TBCSR);

    // Enable Timer B and match register interrupts.
    WrPortI (TBCSR, NULL, 0x03);
}


/**
 *   Process the timer A5 interrupt that occurs every 2mS.  This method hits the watch dog timer,
 *   controls the heartbeat LED, and processes the background communication tasks.
 */
root interrupt void sysTimerISR()
{
    // Clear the timer A5 interrupt.
    RdPortI (TACSR);

    // Flash the heartbeat LED for 100mS every second.
    if (++sysLEDCounter == 500) {
        sysLEDCounter = 0;
        sysLEDOn();
    } else if (sysLEDCounter == 50)
        sysLEDOff();
}

/**
 *   This function should be called once a second by an accurate time base, i.e. GPS.
 */
void sysTimeTick()
{
    ++sysUptime;
}

/**
 *   Turn on the system board LED.
 */
void sysLEDOn()
{
    BitWrPortI (PADR, &PADRShadow, 1, SYS_LED);
}

/**
 *   Turn off the system board LED.
 */
void sysLEDOff()
{
    BitWrPortI (PADR, &PADRShadow, 0, SYS_LED);
}

/**
 *    Turn on the transmitter.
 */
void sysPTTOn()
{
    BitWrPortI (PADR, &PADRShadow, 1, SYS_PTT);
}

/**
 *    Turn off the transmitter.
 */
void sysPTTOff()
{
    BitWrPortI (PADR, &PADRShadow, 0, SYS_PTT);
}

/**
 *    Select radio channel 1.
 */
void sysChan1()
{
    BitWrPortI (PADR, &PADRShadow, 1, SYS_CHAN);
}

/**
 *    Select radio channel 2.
 */
void sysChan2()
{
    BitWrPortI (PADR, &PADRShadow, 0, SYS_CHAN);
}

/**
 *   Calculate NMEA-0183 message checksum of <b>buffer</b> that is <b>length</b> bytes long.
 *
 *   @param buffer Pointer to data buffer.
 *   @param length number of bytes in buffer.
 *
 *   @return checksum of buffer
 */
uint8_t sysNMEAChecksum (uint8_t *buffer, uint16_t length)
{
    uint16_t i;
    uint8_t checksum;

    checksum = 0;

    for (i = 0; i < length; ++i)
        checksum ^= buffer[i];

    return checksum;
}

/**
 *    Determine if carrier is present at radio.
 *
 *    @return true if carrier is present; otherwise false
 */
bool sysIsCarrierDetect()
{
    if (BitRdPortI (PBDR, SYS_CARRIERDETECT) == 0)
        return TRUE;

    return FALSE;
}

/**
 *   Handle run time errors.
 */
root void sysRuntimeErrHandler()
{
    static int error, xpc, addr;
    static char buffer[80];
    static uint32_t timerTick;

    // get all the relevant parameters off the stack
#ifndef WIN32
#asm
    ld      hl, (sp+@SP+2)
    ld      (error), hl     ; get the runtime error code
    ld      hl, (sp+@SP+6)
    ld      (xpc), hl       ; get the XPC where exception() was called
    ld      hl, (sp+@SP+8)
    ld      (addr), hl      ; get the address exception() was called
#endasm
#endif

    sprintf (buffer, "Run time error %d\n\r", error);
    serAputs (buffer);

    // Wait for message to be displayed.
    timerTick = MS_TIMER;
    while (MS_TIMER - timerTick < 500);

    // This is a simple error handler, just reboot.
    forceSoftReset();
}

void sysSelfTest()
{
    uint32_t timerTick;

    // Wait for the MAX-232 charge pumps to start and receiver data buffer to clear.
    timerTick = MS_TIMER;
    while (MS_TIMER - timerTick < 200);
}

/**
 *    Calculate the CRC-16 CCITT of <b>buffer</b> that is <b>length</b> bytes long.
 *
 *    @param buffer Pointer to data buffer.
 *    @param length number of bytes in data buffer
 *
 *    @return CRC-16 of buffer[0 .. length]
 */
uint16_t sysCRC16 (uint8_t *buffer, uint16_t length)
{
    uint16_t i, bit, crc, value;

    crc = 0xffff;

    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;
}

/**
 *    Convert the ASCII hex character <b>digit</b> to a binary value.
 *
 *    @param digit ASCII hex character
 *
 *    @return binary value of digit; 0 if character invalid
 */
uint8_t sysParseHexDigit(uint8_t digit)
{
    if (digit >= '0' && digit <= '9')
        return digit - '0';

    if (digit >= 'a' && digit <= 'f')
        return digit - 'a' + 10;

    if (digit >= 'A' && digit <= 'F')
        return digit - 'A' + 10;

    return 0;
}

/**
 *   Convert the string <b>buffer</b> to a scaled integer.
 *
 *   @buffer pointer to string buffer
 *   @newBuffer pointer to end of convereted string
 *
 *   @return scaled integer
 */
int16_t sysStringToScaledInt(char *buffer, char **newBuffer)
{
    int16_t value;
    bool parseFlag, negativeFlag;

    value = 0;
    parseFlag = TRUE;
    negativeFlag = FALSE;

    // Parse the input string until we get to the end or run out of valid characters.
    while (*buffer != 0 && parseFlag == TRUE) {
        if (*buffer == '-')
            negativeFlag = TRUE;
        else if (*buffer >= '0' && *buffer <= '9')
            value = value * 10 + (*buffer - '0');
        else if (*buffer != '.')
            parseFlag = FALSE;

        ++buffer;
    } // END while

    // Set a pointer to the last parsed character.
    *newBuffer = buffer - 1;

    // Handle signed numbers and we are done.
    if (negativeFlag)
        return -value;
    else
        return value;
}

/**
 *   Class to handle digital temperature sensors.
 */

// Define the I2C clock/data lines.
#define TEMP_CLK 5
#define TEMP_DATA 3

#define tempClockHigh() BitWrPortI(PDDDR, &PDDDRShadow, 0, TEMP_CLK)
#define tempClockLow() BitWrPortI(PDDDR, &PDDDRShadow, 1, TEMP_CLK)
#define tempClock()   BitRdPortI(PDDR, TEMP_CLK)

#define tempDataHigh() BitWrPortI(PDDDR, &PDDDRShadow, 0, TEMP_DATA)
#define tempDataLow() BitWrPortI(PDDDR, &PDDDRShadow, 1, TEMP_DATA)
#define tempData()   BitRdPortI(PDDR, TEMP_DATA)

nodebug int16_t tempGetExtTemp()
{
	uint8_t returnCode, retryCount;
	int16_t temp;

	retryCount = 0;

	while (retryCount < 5) {
		tempMasterStart();
		returnCode = tempWriteAndGetAck (0x91);

		temp = tempReadWithAck (TRUE) << 8;
		temp = temp | tempReadWithAck (FALSE);

		tempMasterStop();

		if (returnCode == 0)
			return (temp * 9 / 64) + 320;

		++retryCount;
	}
}

/**
 *   Initialize the digital temperarture sensor.
 */
void tempInit()
{
	uint8_t i;

	tempDataHigh();
	tempClockLow();

	for (i = 0; i < 4; ++i)
		tempMasterStop();

}

void tempMasterStart()
{
	tempClockHigh();
	tempDataHigh();
	tempDataLow();
	tempClockLow();
	tempDataHigh();
}


void tempMasterStop()
{
	tempDataLow();
	tempClockHigh();
	tempDataHigh();
}

nodebug uint8_t tempWriteAndGetAck(uint8_t data)
{
    uint8_t i, returnState;

    // Bit bang out each bit, MSB first.
    for (i = 0; i < 8; ++i) {
        if ((data & 0x80) == 0x80)
            tempDataHigh();
        else
            tempDataLow();

        tempClockHigh();
        tempClockLow();


        data = data << 1;
    } // END for

    tempDataHigh();
    tempClockHigh();

    returnState = tempData();

    tempClockLow();

    return returnState;
}

nodebug uint8_t tempReadWithAck (bool ack)
{
    uint8_t i, value;

    value = 0;

    for (i = 0; i < 8; ++i) {
	    value = value << 1;

        tempClockHigh();
        value |= (tempData() & 0x01);
        tempClockLow();

    } // END for

	tempDataHigh();


	if (ack) {
	    tempDataLow();
        tempClockHigh();
        tempClockLow();
		tempDataHigh();
	} // END if

    return value;
}


/**
 *   Class to handle the telemetry data.
 */

/**
 *   Prepare and send a NMEA GPGGA packet via the 1200 baud TNC interface.
 */
void tlmGPGGAPacket()
{
    uint32 coord, coordMin;
    uint8_t dirFlag, buffer[80], message[80];

    // Generate the GPGGA message.
    sprintf (message, "$GPGGA,");

    // UTC is replaced with flight time
    sprintf (buffer, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
    strcat (message, buffer);

    // Latitude value.
    coord = gpsPosition.latitude;

    if (gpsPosition.latitude < 0) {
        coord = gpsPosition.latitude * -1;
        dirFlag = 0;
    } else {
        dirFlag = 1;
        coord = gpsPosition.latitude;
    }

    coordMin = (coord % 3600000) / 6;
    sprintf (buffer, "%02ld%02ld.%04ld,", (uint32) (coord / 3600000), (uint32) (coordMin / 10000), (uint32) (coordMin % 10000));
    strcat (message, buffer);

    if (dirFlag == 1)
        strcat (message, "N,");
    else
        strcat (message, "S,");

    // Longitude value.
    if (gpsPosition.longitude < 0) {
        coord = gpsPosition.longitude * - 1;
        dirFlag = 0;
    } else {
        dirFlag = 1;
        coord = gpsPosition.longitude;
    }

    coordMin = (coord % 3600000) / 6;
    sprintf (buffer, "%03ld%02ld.%04ld,", (uint32) (coord / 3600000), (uint32) (coordMin / 10000), (uint32) (coordMin % 10000));
    strcat (message, buffer);

    if (dirFlag == 1)
        strcat (message, "E,");
    else
        strcat (message, "W,");

    // GPS status where 0: not available, 1: available
    if ((gpsPosition.status & 0x8000) == 0x8000)
        strcat (message, "1,");
    else
        strcat (message, "0,");

    // Number of visibile birds.
    sprintf (buffer, "%02d,", gpsPosition.visibleSats);
    strcat (message, buffer);

    // DOP
    sprintf (buffer, "%d.%01d,", gpsPosition.dop / 10, gpsPosition.dop % 10);
    strcat (message, buffer);

    // Altitude in meters.
    sprintf (buffer, "%ld.0,M,,M,,", (int32_t) (gpsPosition.altitude / 100l));
    strcat (message, buffer);

    // Checksum, we add 1 to skip over the $ character.
    sprintf (buffer, "*%02X", sysNMEAChecksum(message + 1, strlen(message) - 1));
    strcat (message, buffer);

    tncSendPacket (message);
}

/**
 *   Prepare and send a NMEA GPRMC packet via the 1200 baud TNC interface.
 */
void tlmGPRMCPacket()
{
    uint32 coord, coordMin, temp;
    uint8_t dirFlag, buffer[80], message[80];

    // Generate the GPRMC message.
    sprintf (message, "$GPRMC,");

    // UTC is replaced with flight time
    sprintf (buffer, "%02d%02d%02d,", gpsPosition.hours, gpsPosition.minutes, gpsPosition.seconds);
    strcat (message, buffer);

    // GPS status.
    if ((gpsPosition.status & 0x8000) == 0x8000)
        strcat (message, "A,");
    else
        strcat (message, "V,");

    // Latitude value.
    coord = gpsPosition.latitude;

    if (gpsPosition.latitude < 0) {
        coord = gpsPosition.latitude * -1;
        dirFlag = 0;
    } else {
        dirFlag = 1;
        coord = gpsPosition.latitude;
    }

    coordMin = (coord % 3600000) / 6;
    sprintf (buffer, "%02ld%02ld.%04ld,", (uint32) (coord / 3600000), (uint32) (coordMin / 10000), (uint32) (coordMin % 10000));
    strcat (message, buffer);

    if (dirFlag == 1)
        strcat (message, "N,");
    else
        strcat (message, "S,");

    // Longitude value.
    if (gpsPosition.longitude < 0) {
        coord = gpsPosition.longitude * - 1;
        dirFlag = 0;
    } else {
        dirFlag = 1;
        coord = gpsPosition.longitude;
    }

    coordMin = (coord % 3600000) / 6;
    sprintf (buffer, "%03ld%02ld.%04ld,", (uint32) (coord / 3600000), (uint32) (coordMin / 10000), (uint32) (coordMin % 10000));
    strcat (message, buffer);

    if (dirFlag == 1)
        strcat (message, "E,");
    else
        strcat (message, "W,");

    // Speed knots and heading.
    temp = (int32_t) gpsPosition.hSpeed * 75000 / 385826;
    sprintf (buffer, "%d.%d,%d.%d,", (int16) (temp / 10), (int16) (temp % 10), gpsPosition.heading / 10, gpsPosition.heading % 10);
    strcat (message, buffer);

    // Date
    sprintf (buffer, "%02d%02d%02d,,", gpsPosition.day, gpsPosition.month, gpsPosition.year % 100);
    strcat (message, buffer);

    // Checksum, skip over the $ character.
    sprintf (buffer, "*%02X", sysNMEAChecksum(message + 1, strlen(message) - 1));
    strcat (message, buffer);

    tncSendPacket (message);
}

uint8_t tlmIndex;

/**
 *   Initialize the telemetry class
 */
void tlmInit()
{
	tlmIndex = 0;
}

/**
 *   Prepare and send a status packet '>' via the 1200 baud TNC interface.
 */
void tlmStatusPacket()
{
    char buffer[80], message[80];
    uint16_t flightTime, value;
    GPSPOSITION *gpsPosition;

    // Get the data we need for the packet.
    gpsPosition = gpsGetData();

    // Status message header.
    sprintf (message, ">ANSR ");

    // Current flight time.
    flightTime = sysGetUptime();
    sprintf (buffer, "%02d:%02d:%02d ", flightTime / 3600, (flightTime / 60) % 60, flightTime % 60);
    strcat (message, buffer);

    // Show GPS information if it is report a 3D fix, otherwise show blank fields.
    if ((gpsPosition->status & 0xe000) == 0xe000) {
        sprintf (buffer, "%ld' %ld'/min ", (int32_t) (gpsPosition->altitude * 100l / 3048l), (int32_t) (gpsPosition->vSpeedAccum * 6000l / 12192l));
        strcat (message, buffer);
    } else
        strcat (message, "---' ---'/min ");

    // GPS status
    if ((gpsPosition->status & 0xe000) == 0xe000){
        sprintf (buffer, "%d.%dpdop ", gpsPosition->dop / 10, gpsPosition->dop % 10);
        strcat (message, buffer);
    } else
        if ((gpsPosition->status & 0xe000) == 0xc000) {
            sprintf (buffer, "%d.%dhdop ", gpsPosition->dop / 10, gpsPosition->dop % 10);
            strcat (message, buffer);
    } else
        strcat (message, "NoGPS ");

    // Aircraft computer bus voltage.
//    value = analogGetBusVoltage();  TODO
//    sprintf (buffer, "%d.%dv ", value / 10, value % 10);
//    strcat (message, buffer);

    // Internal temp.
//    value = analogGetIntTemp();  TODO
//    sprintf (buffer, "%d.%dF ", value / 10, abs(value % 10));
//    strcat (message, buffer);

    tncSendPacket (message);
}

/**
 *   This function is called every time we get a GPS message update (once a second).
 */
void tlmUpdateTick()
{
     switch (gpsGetData()->seconds)
     {
         case 7:
         case 25:
         case 37:
         case 55:
         	switch (tlmIndex)
         	{
            	case 0:
	             	tlmStatusPacket();
                  break;

               case 1:
             		tlmGPGGAPacket();
	               break;

					case 2:
		            tlmGPRMCPacket();
	               break;
            } // END swich

            tlmIndex = (tlmIndex + 1) % 3;

            break;
     } // END switch
}

/**
 *   Class to handle the 1200 baud TNC functions.
 */
#define TNC_M0 2
#define TNC_M1 4
#define TNC_DATA 5

#define TNC_TX_WAIT 0
#define TNC_TX_SYNC 1
#define TNC_TX_DATA 2
#define TNC_TX_END 3

#define TNC_MAX_MESSAGE 255

// CLK_RATE, Timer B delta between bit time interrupts.
// 48 * (1 / 11.0592) * 192 = 833.3 uS = 1200 bps
// 16 * (1 / 3.684) * 192 = 833.3uS = 1200 bps
#define TNC_BIT_CLOCK_RATE 48

uint16_t tncTimerCompare, tncIndex, tncLength, tncTxPacketCount;
uint8_t tncBitCount, tncShift, tncLastBit, tncMode, tncTransmit;
uint8_t tncBitStuff, tncBuffer[TNC_MAX_MESSAGE];

void tncInit()
{
    tncTimerCompare = 0;
    tncShift = 0;
    tncIndex = 0;
    tncMode = TNC_TX_WAIT;
    tncTransmit = FALSE;

    // Counter of all the packets we handle.
    tncTxPacketCount = 0;

    // Disable the CMX614 output.
    BitWrPortI (PEDR, &PEDRShadow, 0, TNC_DATA);
    BitWrPortI (PEDR, &PEDRShadow, 0, TNC_M0);
    BitWrPortI (PEDR, &PEDRShadow, 1, TNC_M1);

    // Turn off the transmitter and select channel 2.
    sysPTTOff();
    sysChan2();
}

root interrupt void tncTimerISR()
{
    // Clear the interrupt pending flag.
    RdPortI (TBCSR);

    // Interrupt again in 833uS (1 bit time).
    tncTimerCompare = (tncTimerCompare + TNC_BIT_CLOCK_RATE) & 0x3ff;
    WrPortI (TBM1R, NULL, (tncTimerCompare >> 2) & 0xc0);
    WrPortI (TBL1R, NULL, tncTimerCompare & 0xff);

    // Process based on the state machine.
    switch (tncMode) {
        case TNC_TX_WAIT:
            break;

        case TNC_TX_SYNC:
            // The variable tncShift contains the lastest data byte.
            // NRZI enocde the data stream.
            if ((tncShift & 0x01) == 0x00)
                if (tncLastBit == 0)
                    tncLastBit = 1;
                else
                    tncLastBit = 0;

            BitWrPortI (PEDR, &PEDRShadow, tncLastBit, TNC_DATA);

            // When the flag is done, determine if we need to send more or data.
            if (++tncBitCount == 8) {
                tncBitCount = 0;
                tncShift = 0x7e;

            // Once we transmit x mS of flags, send the data.
            // TNCTxDelay() bytes * 8 bits/byte * 833uS/bit = x mS
                if (++tncIndex == configGetTNCTxDelay()) {
                    tncIndex = 0;
                    tncShift = tncBuffer[0];
                    tncBitStuff = 0;
                    tncMode = TNC_TX_DATA;
                } // END if
            } else
                tncShift = tncShift >> 1;
            break;

        case TNC_TX_DATA:
            // Determine if we have sent 5 ones in a row, if we have send a zero.
            if (tncBitStuff == 0x1f) {
                if (tncLastBit == 0)
                    tncLastBit = 1;
                else
                    tncLastBit = 0;

                BitWrPortI (PEDR, &PEDRShadow, tncLastBit, TNC_DATA);
                tncBitStuff = 0x00;
                return;
            }    // END if

            // The variable tncShift contains the lastest data byte.
            // NRZI enocde the data stream.
            if ((tncShift & 0x01) == 0x00)
                if (tncLastBit == 0)
                    tncLastBit = 1;
                else
                    tncLastBit = 0;

            BitWrPortI (PEDR, &PEDRShadow, tncLastBit, TNC_DATA);

            // Save the data stream so we can determine if bit stuffing is
            // required on the next bit time.
            tncBitStuff = ((tncBitStuff << 1) | (tncShift & 0x01)) & 0x1f;

            // If all the bits were shifted, get the next byte.
            if (++tncBitCount == 8) {
                tncBitCount = 0;

            // If everything was sent, transmit closing flags.
                if (++tncIndex == tncLength) {
                    tncIndex = 0;
                    tncShift = 0x7e;
                    tncMode = TNC_TX_END;
                } else
                    tncShift = tncBuffer[tncIndex];

            } else
                tncShift = tncShift >> 1;

            break;

        case TNC_TX_END:
            // The variable tncShift contains the lastest data byte.
            // NRZI enocde the data stream.
            if ((tncShift & 0x01) == 0x00)
                if (tncLastBit == 0)
                    tncLastBit = 1;
                else
                    tncLastBit = 0;

            BitWrPortI (PEDR, &PEDRShadow, tncLastBit, TNC_DATA);

            // If all the bits were shifted, get the next one.
            if (++tncBitCount == 8) {
                tncBitCount = 0;
                tncShift = 0x7e;

                // Transmit two closing flags.
                if (++tncIndex == 2) {
                    // Keep track of how many packets we have sent.
                    ++tncTxPacketCount;

                    // Reset to the receive mode.
                    tncIndex = 0;
                    tncShift = 0;
                    tncMode = TNC_TX_WAIT;

                    // Change the MODEM to receive mode.
                    BitWrPortI (PEDR, &PEDRShadow, 0, TNC_M0);
                    BitWrPortI (PEDR, &PEDRShadow, 1, TNC_M1);
                    BitWrPortI (PEDR, &PEDRShadow, 0, TNC_DATA);

                    // Turn off the transmitter and go back to the primary channel.
                    sysPTTOff();
                    return;
                } // END if
            } else
                tncShift = tncShift >> 1;
            break;
    } // END switch
}


/**
 *   Send an AX.25 packet via the RF interface.  This function enables the transmitter,
 *   prepares the output buffer, and sets the interrupt mode.
 *
 *   @param message pointer to NULL terminated message string
 */
void tncSendPacket(uint8_t *message)
{
    uint16_t i, crc;
    uint8_t *outBuffer, *callSign;

//    serAputs ("sending '");
//    serAputs (message);
//    serAputs ("\n\r");

    // Turn on the MODEM TX mode.
    BitWrPortI (PEDR, &PEDRShadow, 1, TNC_DATA);
    BitWrPortI (PEDR, &PEDRShadow, 1, TNC_M0);
    BitWrPortI (PEDR, &PEDRShadow, 0, TNC_M1);

    // Key the transmitter.
    sysPTTOn();

    // Set a pointer to our output buffer.
    outBuffer = tncBuffer;

    // Includes source (7), dest (7), control field (1), protocol ID (1), and end of message (1).
    tncLength = 17;

    // Set the destination address.
    callSign = configGetDestCallSign();

    for (i = 0; i < 6; ++i)
        *outBuffer++ = *callSign++ << 1;

    // Set destination to SSID-0
    *outBuffer++ = 0x60;

    // Set the source address.
    callSign = configGetCallSign();

    for (i = 0; i < 6; ++i)
        *outBuffer++ = *callSign++ << 1;

    // Set the SSID.
    *outBuffer++ = 0x60 | (configGetBalloonSSID() << 1);

    // Add relay path 1.
    callSign = configGetRelay1CallSign();

    if (*callSign != NULL) {
        for (i = 0; i < 6; ++i)
            *outBuffer++ = *callSign++ << 1;

        *outBuffer++ = 0x60 | (configGetRelay1SSID() << 1);

        tncLength += 7;
    } // END if

    // Add relay path 2.
    callSign = configGetRelay2CallSign();

    if (*callSign != NULL) {
        for (i = 0; i < 6; ++i)
            *outBuffer++ = *callSign++ << 1;

        *outBuffer++ = 0x60 | (configGetRelay2SSID() << 1);

        tncLength += 7;
    } // END if

    // Set bit-0 of the last SSID.
    *(outBuffer - 1) |= 0x01;

    // Set the control field (UI) and protocol ID.
    *outBuffer++ = 0x03;
    *outBuffer++ = 0xf0;

    // Save the message.
    while (*message != 0) {
        *outBuffer++ = *message++;
        ++tncLength;
    }

    // Add the end of message character.
    *outBuffer++ = 0x0d;

    // Calculate and append the CRC.
    crc = sysCRC16(tncBuffer, tncLength);
    *outBuffer++ = crc & 0xff;
    *outBuffer = (crc >> 8) & 0xff;

    // Update the length to include the CRC bytes.
    tncLength += 2;

    // Prepare for the ISR.
    tncBitCount = 0;
    tncShift = 0x7e;
    tncLastBit = 0;
    tncIndex = 0;
    tncMode = TNC_TX_SYNC;
}

/**
 *   This class flies the aircraft.
 */
#define FLIGHT_WAIT_DATA 0
#define FLIGHT_WAIT_TX 1
#define FLIGHT_WAIT_ACK 2

#define FLIGHT_FAST_RETRY 800
#define FLIGHT_SLOW_RETRY 30000

// State machine used to determine if we are wait for flight data to send, waiting for the transmit to complete, or waiting for an ACK.
uint8_t flightDownlinkStatus;

// A flag to indicate the GPS has not provided us with an update.
uint8_t flightGPSFault;

// The maximum processing time of the main loop.
uint32_t flightMaxProcTime;

// Count the number of times we try to transmit a packet.
uint8_t flightRetryCount;

// Flag that is set when a flight data ack packet is received.
bool flightAckBlock;

// The time in mS the last flight data packet was sent.
uint32_t flightLastPacketTime;

// Most recent GPS update.
GPSPOSITION *flightGPS;

bool flightLogRunFlag;

/**
 *    Get everything read for flight.
 */
void flightInit()
{
    // Set a pointer to the GPS data set.
    flightGPS = gpsGetData();

    // Set the rest of the defaults.
    flightGPSFault = FALSE;

    flightAckBlock = FALSE;
    flightLastPacketTime = MS_TIMER;
    flightDownlinkStatus = FLIGHT_WAIT_DATA;
    flightMaxProcTime = 0;
    flightRetryCount = 0;
    flightLogRunFlag = TRUE;
}


void flightEnableLog()
{
    flightLogRunFlag = TRUE;
}

void flightDisableLog()
{
    flightLogRunFlag = FALSE;
}

bool flightIsLogEnabled()
{
    return flightLogRunFlag;
}

/**
 *    Process the new GPS message.
 */
void flightProcessGPS()
{
	char buffer[80];

  sprintf (buffer, "%02d:%02d:%02d 0x%04X %d %d %d.%01d ", flightGPS->hours, flightGPS->minutes, flightGPS->seconds, flightGPS->status , flightGPS->visibleSats, flightGPS->trackedSats, flightGPS->dop / 10, flightGPS->dop % 10);
//  serAputs (buffer);

  sprintf (buffer, "%ld,%ld,%ld\n\r", flightGPS->latitude, flightGPS->longitude, flightGPS->altitude);
//  serAputs (buffer);

    // Clear the update flag so we know when we get a new GPS message.
    gpsClearUpdateFlag();
}

void flightDataRecorder()
{


}

/**
 *   The main control loop that controls everything.
 */
void flightRun()
{
    char buffer[80];
    uint32_t startTime, procTime, gpsTime;

	gpsTime = MS_TIMER;

    // The main control loop.
    while (TRUE) {
        startTime = MS_TIMER;

        // Read and process the serial data from the GPS.
        gpsReadData();

        // Process new GPS messages, update the telemetry, and keep track of the last GPS messsage.
        if (gpsIsUpdated()) {
        	gpsTime = MS_TIMER;

            flightProcessGPS();
            tlmUpdateTick();
            sysTimeTick();

//            sprintf (buffer, "temp = %d\015\012", tempGetExtTemp());
//            serAputs (buffer);

            if (gpsIsIDReady()) {
                serAputs (gpsGetID());
                gpsClearIDFlag();
            } // END if

//            sprintf (buffer, "pos = %d, cmd = %d\n\r", servoPosition[0], servoCommand[0]);
//            serCputs(buffer);
        } // END if

        // Read and process the analog input board.
//        analogUpdate();

		// If the GPS hasn't updated in the last 1.5 seconds, send a message to request data.
		if (MS_TIMER - gpsTime > 1500) {
        	gpsTime = MS_TIMER;
			gpsSendMessage ("Hb\001", 3);
		} // END if


        // Process the flight data recorder.
        flightDataRecorder();

        // Record the maximum processing time through the main loop and display it.
        procTime = MS_TIMER - startTime;

        // Display the maximum processing time of the main loop.
        if (procTime > flightMaxProcTime) {
            flightMaxProcTime = procTime;
            sprintf (buffer, "time = %ldmS\015\012", flightMaxProcTime);
//            serAputs (buffer);
        } // END if
    } // END while

}


/**
 *   Time to fly!
 */
main()
{
    // Initialize the system and configuration classes.  These must be set for the other classes.
    sysInit();
    configInit();

    // Now initialize the rest of the classes in alphabetical order.
    flightInit();
    gpsInit();
    tlmInit();
    tncInit();

    // Turn off the LED to indicate init is complete and we are turning on interrupts.
    sysLEDOff();

    // Turn on the interrupts.
    sysInterruptEnable();

    // Make sure everything is alright.
    sysSelfTest();

    // Now execute the main routine
    flightRun();

    // We should never get to this point, but if we do reset.
    forceSoftReset();
}