QRSS Rx Network receiver. A receiver to sample a segment of RF spectrum and send this data to a server for further processing and display. NXP mbed Design Challenge entry (Honorable Mention). Published in Circuit Cellar, Feb 2012

Dependencies:   NetServices mbed DNSResolver

gps.cpp

Committer:
claytong
Date:
2012-01-25
Revision:
0:82ff15078322

File content as of revision 0:82ff15078322:

/*---------------------------------------------------------------------------

    QRSS Receiver Application
        
    by Clayton ZL3TKA/VK1TKA
    clayton@isnotcrazy.com

    GPS Module

---------------------------------------------------------------------------*/
// include files

#include "gps.h"
#include "comms.h"

// Definitions

#define GPS_DEBUG   0

// comments

// Macros

// Local Data

// Global Data
TGPSController GPSModule;


// Function Prototypes

//---------------------------------------------------------------------------
//  TGPSController Class Methods
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
//
//  Constructor
//
TGPSController::TGPSController() :
        PPSInput( p30 ),
        GPSPort( p28, p27 ),
        GPSUpLED( LED3 ),
        PPSEvent( p30 ),
        bPulsed( false ),
        uiPPSCapture( 0 ),
        uiLOscCapture( 0 )
{
    // clear current and last line
    szCurrentLine[0] = 0;
    szLastLine[0] = 0;
    bNewLine = false;
}

//---------------------------------------------------------------------------
//
//  Initialise routine
//
void TGPSController::Init()
{
    // Set up GPS port
    GPSPort.baud( 4800 );

    // capture 1PPS event    
    PPSEvent.rise( this, &TGPSController::PPSPulse );

    // Start timeout timer
    GPSMsgTimeout.start();
    GPSPulseTimeout.start();

    // Set up timer hardware
    LPC_PINCON->PINSEL0 |=  ( (0x3<<8) | (0x3<<10) );   // enable CAP2.0, CAP2.1
    LPC_SC->PCONP |= 1 << 22;   // power up TIMER2 (PCONP[22])
    LPC_SC->PCLKSEL1 &= ~(0x3<<12);
    LPC_SC->PCLKSEL1 |=  (0x1<<12); // Timer2 PCLK = CCLK
    LPC_TIM2->TCR = 0x2;        // reset timer
    LPC_TIM2->CTCR = 0x0;       // timer mode
    LPC_TIM2->PR = 0;           // no prescale
    LPC_TIM2->MCR = 0x0;        // no match
    LPC_TIM2->CCR = (1<<0) | (1<<3);    // Capture rising edges on both CAP2.0 & CAP2.1
    LPC_TIM2->TCR = 1;          // start the timer
}

//---------------------------------------------------------------------------
//
//  Poll routine
//
void TGPSController::Poll()
{
    // Test for a 1PPS pulse
    if ( bPulsed )
    {   // 1PPS from the GPS unit
        bPulsed = false;
        GPSPulseTimeout.reset();

        // flush out any old serial data
        while ( GPSPort.readable() )
            GPSPort.getc();
        #if GPS_DEBUG>2
         printf( "***1PPS***\r\n" );
        #endif
        // Record capture data to comms module
        Comms.RecordPPSTimestamps( uiPPSCapture, uiLOscCapture, GPSRecord );
    }

    // Test for GPS serial port data
    while ( GPSPort.readable() )
    {
        char cNextChar = GPSPort.getc();
        #if GPS_DEBUG>3
          printf( "%c", cNextChar );
        #endif                            
        if ( ParseData(cNextChar) )
        {   // have a completed GPS sentence
            GPSMsgTimeout.reset();
            // analysis the sentence data
            int iStatus = ProcessGPSData();
            if ( iStatus==0 )
            {   // good sentence
                #if GPS_DEBUG>0
                 printf( "Time: %d\r\n",   GPSRecord.iGPSTimeSeconds );
                 printf( "Lat: %d\r\n",    GPSRecord.iGPSLatMicroDegrees );
                 printf( "Long: %d\r\n",   GPSRecord.iGPSLongMicroDegrees );
                 printf( "Alt: %d\r\n",    GPSRecord.iGPSAltitudeMeters );
                 printf( "Sats: %d\r\n",   GPSRecord.iGPSSatellites );
                 printf( "Qual: %d\r\n",   GPSRecord.iGPSQuality );
                #endif
            }
        }
    }

    // Test for GPS timeout (no data)
    if ( GPSMsgTimeout.read_ms()>GPS_MSGTIMEOUT )
    {   // No GPS messages for a period
        #if GPS_DEBUG>1
          printf( "GPS Timeout - setting to offline\r\n" );
        #endif                            
        GPSRecord.iGPSQuality = 0;      // set quality to 0 - invalid data
        GPSMsgTimeout.reset();
        // Record invalid GPS status to comms module
        Comms.RecordPPSTimestamps( 0, 0, GPSRecord );
    }

    // Test for GPS timeout (no 1PPS)
    if ( GPSPulseTimeout.read_ms()>GPS_PULSETIMEOUT )
    {   // No GPS pulse for a period
        // we just forward data to Comms module with invalid capture data
        Comms.RecordPPSTimestamps( 0, 0, GPSRecord );
        GPSPulseTimeout.reset();
    }

}

//---------------------------------------------------------------------------
//
//  Parse data from the GPS
//      Return true if a completed valid sentence is received
//
#define EXPECT_CHAR(CC)     if (cChr==CC)iParseState++; else iParseState=0;ResetNumber()
#define PARSE_NUMBER(NN)    if(cChr==','){NN=ParseNum;ResetNumber();iParseState++;break;}if (!ParseNumber(cChr))iParseState=0
#define PARSE_CHARACTER(CH) if(cChr==','){CH=cCharacter;ResetNumber();iParseState++;break;}cCharacter=cChr
//
bool TGPSController::ParseData( char cChr )
{
    ucChecksum ^= cChr;
    switch ( iParseState )
    {
      case 0:
        if ( cChr=='$' )
            iParseState++;
        ucChecksum = 0;            
        break;
      case 1:       EXPECT_CHAR('G');               break;
      case 2:       EXPECT_CHAR('P');               break;
      case 3:       EXPECT_CHAR('G');               break;
      case 4:       EXPECT_CHAR('G');               break;
      case 5:       EXPECT_CHAR('A');               break;
      case 6:       EXPECT_CHAR(',');               break;
      case 7:       PARSE_NUMBER( GPSTime );        break;
      case 8:       PARSE_NUMBER( GPSLatitude );    break;
      case 9:       PARSE_CHARACTER( GPSLatNS );    break;
      case 10:      PARSE_NUMBER( GPSLongitude );   break;
      case 11:      PARSE_CHARACTER( GPSLongEW );   break;
      case 12:      PARSE_NUMBER( GPSQuality );     break;
      case 13:      PARSE_NUMBER( GPSSatellites );  break;
      case 14:      PARSE_NUMBER( GPSDOP );         break;
      case 15:      PARSE_NUMBER( GPSAltitude );    break;
      case 16:      PARSE_CHARACTER( GPSAltType );  break;
      case 17:      PARSE_NUMBER( GPSHeight );      break;
      case 18:      PARSE_CHARACTER( GPSHeightType ); break;
      case 19:      EXPECT_CHAR(',');   
                    ucFinalChecksum = ucChecksum; 
                    break;
      case 20:      EXPECT_CHAR('*');               break;
      case 21:      iParseState++;
                    if ( (cChr>='0') && (cChr<='9') )
                        ucMsgChecksum = (cChr-'0') << 4; 
                    else if ( (cChr>='A') && (cChr<='F') )
                        ucMsgChecksum = (cChr-'A'+10) << 4; 
                    else
                        iParseState = 0;
                    break;
      case 22:      iParseState++;
                    if ( (cChr>='0') && (cChr<='9') )
                        ucMsgChecksum |= (cChr-'0');
                    else if ( (cChr>='A') && (cChr<='F') )
                        ucMsgChecksum |= (cChr-'A'+10);
                    else
                        iParseState = 0;
                    break;
      case 23:      // don't care about char (should be a CR)
                    // just check the results
                    if ( ucMsgChecksum==ucFinalChecksum )
                    {   // Checksum okay
                        // reset
                        iParseState = 0;
                        // return valid message
                        return true;
                    }
                    #if GPS_DEBUG>0
                      else
                          printf( "!GPS Check failed - got %02X wanted %02X\r\n", (int)ucMsgChecksum, (int)ucFinalChecksum );
                    #endif                            
                    // reset
                    iParseState = 0;
                    break;
    }

    #if GPS_DEBUG>2
      // report parser state
      printf( ":%d:", iParseState );
    #endif                            

    // GPS sentence note yet completed
    return false;
}

//---------------------------------------------------------------------------
//
//  Parse a number character by character
//      If character is invalid, returns false
//
bool TGPSController::ParseNumber( char cChar )
{
    // process digits
    if ( (cChar>='0') && (cChar<='9') )
    {
        ParseNum.uiNumber = (ParseNum.uiNumber*10) + (cChar-'0');
        ParseNum.iNumberLen++;
        if ( ParseNum.iNumberDecimals>=0 )
            ParseNum.iNumberDecimals++; 
        return true;
    }
    if ( cChar=='.' )
    {
        if ( ParseNum.iNumberDecimals>=0 )
            return false;   // a second decimal point!
        ParseNum.iNumberDecimals = 0;
        return true;
    }
    if ( cChar=='-' )
    {
        if ( ParseNum.iNumberLen>0 )
            return false;   // '-' must be the first character
        ParseNum.iNumberSign = -1;
        return true;
    }
    // otherwise an invalid character
    return false;
}

//---------------------------------------------------------------------------
//
//  Reset the number parser
//
void TGPSController::ResetNumber( )
{
    ParseNum.uiNumber = 0;
    ParseNum.iNumberLen = 0;
    ParseNum.iNumberSign = 1;
    ParseNum.iNumberDecimals = -1;
    cCharacter = 0;
}


//---------------------------------------------------------------------------
//
//  Process the data from the GPS message
//      Returns 0 if all data is good, or an error code
//
int TGPSController::ProcessGPSData( )
{
    // check Quality
    if ( GPSQuality.iNumberLen<1 )          return 10;
    if ( GPSQuality.iNumberDecimals!=-1 )   return 11;
    if ( GPSQuality.iNumberSign!=1 )        return 12;
    // check Satellites
    if ( GPSSatellites.iNumberLen<1 )       return 20;
    if ( GPSSatellites.iNumberDecimals!=-1 ) return 21;
    if ( GPSSatellites.iNumberSign!=1 )     return 22;
    // Store sats and quality parameters
    GPSRecord.iGPSSatellites = GPSSatellites.uiNumber;
    GPSRecord.iGPSQuality = GPSQuality.uiNumber;
    // Check quality level
    if ( GPSQuality.uiNumber<1 )            return 100;     // no fix
    // check Time
    if ( GPSTime.iNumberLen<6 )            return 30;
    if ( GPSTime.iNumberSign!=1 )           return 32;
    // check Latitude
    if ( GPSLatitude.iNumberLen!=7 )        return 40;
    if ( GPSLatitude.iNumberDecimals!=3 )   return 41;
    if ( GPSLatitude.iNumberSign!=1 )       return 42;
    if ( (GPSLatNS!='N') && (GPSLatNS!='S') ) return 43;
    // check Longitude
    if ( GPSLongitude.iNumberLen!=8 )        return 50;
    if ( GPSLongitude.iNumberDecimals!=3 )   return 51;
    if ( GPSLongitude.iNumberSign!=1 )       return 52;
    if ( (GPSLongEW!='E') && (GPSLongEW!='W') ) return 53;
    // check Altitude
    if ( GPSAltitude.iNumberLen<1 )         return 60;
    // Don't care about DOPs and Height and Types
    // Translate & Store parameters

    // discard fractions of seconds
    while ( (GPSTime.iNumberDecimals--)>0 )
        GPSTime.uiNumber /= 10;

    int32_t iHours = GPSTime.uiNumber/10000;
    int32_t iMins = (GPSTime.uiNumber/100)%100;
    int32_t iSecs = GPSTime.uiNumber%100;
    GPSRecord.iGPSTimeSeconds = iSecs + (60*iMins) + (3600*iHours);

    int32_t iDegrees = GPSLatitude.uiNumber / 100000;
    int32_t iMilliMinutes = GPSLatitude.uiNumber % 100000;
    GPSRecord.iGPSLatMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000);
    if ( GPSLatNS=='S' )
        GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees;

    iDegrees = GPSLongitude.uiNumber / 100000;
    iMilliMinutes = GPSLongitude.uiNumber % 100000;
    GPSRecord.iGPSLongMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000);
    if ( GPSLongEW=='W' )
        GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees;

    GPSRecord.iGPSAltitudeMeters = GPSAltitude.uiNumber * GPSAltitude.iNumberSign;
    while ( (GPSAltitude.iNumberDecimals--)>0 )
        GPSRecord.iGPSAltitudeMeters /= 10;
    
    return 0;
}

//---------------------------------------------------------------------------
//
//  PPS Event routine
//
void TGPSController::PPSPulse()
{
    // indicate we have pulsed
    bPulsed = true;
    
    // capture timer capture registers
    //  1PPP = CAP2.0
    //  LOsc (/4096) = CAP2.1
    uiPPSCapture = LPC_TIM2->CR0;
    uiLOscCapture = LPC_TIM2->CR1;

}

/*
GPS Messages:

GGA - essential fix data which provide 3D location and accuracy data.

 $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47

Where:
     GGA          Global Positioning System Fix Data
     123519       Fix taken at 12:35:19 UTC
     4807.038,N   Latitude 48 deg 07.038' N
     01131.000,E  Longitude 11 deg 31.000' E
     1            Fix quality: 0 = invalid
                               1 = GPS fix (SPS)
                               2 = DGPS fix
                               3 = PPS fix
                   4 = Real Time Kinematic
                   5 = Float RTK
                               6 = estimated (dead reckoning) (2.3 feature)
                   7 = Manual input mode
                   8 = Simulation mode
     08           Number of satellites being tracked
     0.9          Horizontal dilution of position
     545.4,M      Altitude, Meters, above mean sea level
     46.9,M       Height of geoid (mean sea level) above WGS84
                      ellipsoid
     (empty field) time in seconds since last DGPS update
     (empty field) DGPS station ID number
     *47          the checksum data, always begins with *


VTG - Velocity made good. The gps receiver may use the LC prefix instead of GP if it is emulating Loran output.

  $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48

where:
        VTG          Track made good and ground speed
        054.7,T      True track made good (degrees)
        034.4,M      Magnetic track made good
        005.5,N      Ground speed, knots
        010.2,K      Ground speed, Kilometers per hour
        *48          Checksum
        
        

$GPGGA,,,,,,0,05,,,,,,,*63
$GPGGA,,,,,,0,02,,,,,,,*64
$GPGGA,,,,,,0,03,,,,,,,*65
$GPGGA,120609.46,3515.405,S,14904.873,E,0,04,2.24,718,M,14,M,,*42
 $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
 $GPGGA,120610.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4B
 $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
 $GPGGA,120611.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4A
 $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
 $GPGGA,120612.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*49

*/

//---------------------------------------------------------------------------
//  END
//---------------------------------------------------------------------------