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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers gps.cpp Source File

gps.cpp

00001 /*---------------------------------------------------------------------------
00002 
00003     QRSS Receiver Application
00004         
00005     by Clayton ZL3TKA/VK1TKA
00006     clayton@isnotcrazy.com
00007 
00008     GPS Module
00009 
00010 ---------------------------------------------------------------------------*/
00011 // include files
00012 
00013 #include "gps.h"
00014 #include "comms.h"
00015 
00016 // Definitions
00017 
00018 #define GPS_DEBUG   0
00019 
00020 // comments
00021 
00022 // Macros
00023 
00024 // Local Data
00025 
00026 // Global Data
00027 TGPSController GPSModule;
00028 
00029 
00030 // Function Prototypes
00031 
00032 //---------------------------------------------------------------------------
00033 //  TGPSController Class Methods
00034 //---------------------------------------------------------------------------
00035 
00036 //---------------------------------------------------------------------------
00037 //
00038 //  Constructor
00039 //
00040 TGPSController::TGPSController() :
00041         PPSInput( p30 ),
00042         GPSPort( p28, p27 ),
00043         GPSUpLED( LED3 ),
00044         PPSEvent( p30 ),
00045         bPulsed( false ),
00046         uiPPSCapture( 0 ),
00047         uiLOscCapture( 0 )
00048 {
00049     // clear current and last line
00050     szCurrentLine[0] = 0;
00051     szLastLine[0] = 0;
00052     bNewLine = false;
00053 }
00054 
00055 //---------------------------------------------------------------------------
00056 //
00057 //  Initialise routine
00058 //
00059 void TGPSController::Init()
00060 {
00061     // Set up GPS port
00062     GPSPort.baud( 4800 );
00063 
00064     // capture 1PPS event    
00065     PPSEvent.rise( this, &TGPSController::PPSPulse );
00066 
00067     // Start timeout timer
00068     GPSMsgTimeout.start();
00069     GPSPulseTimeout.start();
00070 
00071     // Set up timer hardware
00072     LPC_PINCON->PINSEL0 |=  ( (0x3<<8) | (0x3<<10) );   // enable CAP2.0, CAP2.1
00073     LPC_SC->PCONP |= 1 << 22;   // power up TIMER2 (PCONP[22])
00074     LPC_SC->PCLKSEL1 &= ~(0x3<<12);
00075     LPC_SC->PCLKSEL1 |=  (0x1<<12); // Timer2 PCLK = CCLK
00076     LPC_TIM2->TCR = 0x2;        // reset timer
00077     LPC_TIM2->CTCR = 0x0;       // timer mode
00078     LPC_TIM2->PR = 0;           // no prescale
00079     LPC_TIM2->MCR = 0x0;        // no match
00080     LPC_TIM2->CCR = (1<<0) | (1<<3);    // Capture rising edges on both CAP2.0 & CAP2.1
00081     LPC_TIM2->TCR = 1;          // start the timer
00082 }
00083 
00084 //---------------------------------------------------------------------------
00085 //
00086 //  Poll routine
00087 //
00088 void TGPSController::Poll()
00089 {
00090     // Test for a 1PPS pulse
00091     if ( bPulsed )
00092     {   // 1PPS from the GPS unit
00093         bPulsed = false;
00094         GPSPulseTimeout.reset();
00095 
00096         // flush out any old serial data
00097         while ( GPSPort.readable() )
00098             GPSPort.getc();
00099         #if GPS_DEBUG>2
00100          printf( "***1PPS***\r\n" );
00101         #endif
00102         // Record capture data to comms module
00103         Comms.RecordPPSTimestamps( uiPPSCapture, uiLOscCapture, GPSRecord );
00104     }
00105 
00106     // Test for GPS serial port data
00107     while ( GPSPort.readable() )
00108     {
00109         char cNextChar = GPSPort.getc();
00110         #if GPS_DEBUG>3
00111           printf( "%c", cNextChar );
00112         #endif                            
00113         if ( ParseData(cNextChar) )
00114         {   // have a completed GPS sentence
00115             GPSMsgTimeout.reset();
00116             // analysis the sentence data
00117             int iStatus = ProcessGPSData();
00118             if ( iStatus==0 )
00119             {   // good sentence
00120                 #if GPS_DEBUG>0
00121                  printf( "Time: %d\r\n",   GPSRecord.iGPSTimeSeconds );
00122                  printf( "Lat: %d\r\n",    GPSRecord.iGPSLatMicroDegrees );
00123                  printf( "Long: %d\r\n",   GPSRecord.iGPSLongMicroDegrees );
00124                  printf( "Alt: %d\r\n",    GPSRecord.iGPSAltitudeMeters );
00125                  printf( "Sats: %d\r\n",   GPSRecord.iGPSSatellites );
00126                  printf( "Qual: %d\r\n",   GPSRecord.iGPSQuality );
00127                 #endif
00128             }
00129         }
00130     }
00131 
00132     // Test for GPS timeout (no data)
00133     if ( GPSMsgTimeout.read_ms()>GPS_MSGTIMEOUT )
00134     {   // No GPS messages for a period
00135         #if GPS_DEBUG>1
00136           printf( "GPS Timeout - setting to offline\r\n" );
00137         #endif                            
00138         GPSRecord.iGPSQuality = 0;      // set quality to 0 - invalid data
00139         GPSMsgTimeout.reset();
00140         // Record invalid GPS status to comms module
00141         Comms.RecordPPSTimestamps( 0, 0, GPSRecord );
00142     }
00143 
00144     // Test for GPS timeout (no 1PPS)
00145     if ( GPSPulseTimeout.read_ms()>GPS_PULSETIMEOUT )
00146     {   // No GPS pulse for a period
00147         // we just forward data to Comms module with invalid capture data
00148         Comms.RecordPPSTimestamps( 0, 0, GPSRecord );
00149         GPSPulseTimeout.reset();
00150     }
00151 
00152 }
00153 
00154 //---------------------------------------------------------------------------
00155 //
00156 //  Parse data from the GPS
00157 //      Return true if a completed valid sentence is received
00158 //
00159 #define EXPECT_CHAR(CC)     if (cChr==CC)iParseState++; else iParseState=0;ResetNumber()
00160 #define PARSE_NUMBER(NN)    if(cChr==','){NN=ParseNum;ResetNumber();iParseState++;break;}if (!ParseNumber(cChr))iParseState=0
00161 #define PARSE_CHARACTER(CH) if(cChr==','){CH=cCharacter;ResetNumber();iParseState++;break;}cCharacter=cChr
00162 //
00163 bool TGPSController::ParseData( char cChr )
00164 {
00165     ucChecksum ^= cChr;
00166     switch ( iParseState )
00167     {
00168       case 0:
00169         if ( cChr=='$' )
00170             iParseState++;
00171         ucChecksum = 0;            
00172         break;
00173       case 1:       EXPECT_CHAR('G');               break;
00174       case 2:       EXPECT_CHAR('P');               break;
00175       case 3:       EXPECT_CHAR('G');               break;
00176       case 4:       EXPECT_CHAR('G');               break;
00177       case 5:       EXPECT_CHAR('A');               break;
00178       case 6:       EXPECT_CHAR(',');               break;
00179       case 7:       PARSE_NUMBER( GPSTime );        break;
00180       case 8:       PARSE_NUMBER( GPSLatitude );    break;
00181       case 9:       PARSE_CHARACTER( GPSLatNS );    break;
00182       case 10:      PARSE_NUMBER( GPSLongitude );   break;
00183       case 11:      PARSE_CHARACTER( GPSLongEW );   break;
00184       case 12:      PARSE_NUMBER( GPSQuality );     break;
00185       case 13:      PARSE_NUMBER( GPSSatellites );  break;
00186       case 14:      PARSE_NUMBER( GPSDOP );         break;
00187       case 15:      PARSE_NUMBER( GPSAltitude );    break;
00188       case 16:      PARSE_CHARACTER( GPSAltType );  break;
00189       case 17:      PARSE_NUMBER( GPSHeight );      break;
00190       case 18:      PARSE_CHARACTER( GPSHeightType ); break;
00191       case 19:      EXPECT_CHAR(',');   
00192                     ucFinalChecksum = ucChecksum; 
00193                     break;
00194       case 20:      EXPECT_CHAR('*');               break;
00195       case 21:      iParseState++;
00196                     if ( (cChr>='0') && (cChr<='9') )
00197                         ucMsgChecksum = (cChr-'0') << 4; 
00198                     else if ( (cChr>='A') && (cChr<='F') )
00199                         ucMsgChecksum = (cChr-'A'+10) << 4; 
00200                     else
00201                         iParseState = 0;
00202                     break;
00203       case 22:      iParseState++;
00204                     if ( (cChr>='0') && (cChr<='9') )
00205                         ucMsgChecksum |= (cChr-'0');
00206                     else if ( (cChr>='A') && (cChr<='F') )
00207                         ucMsgChecksum |= (cChr-'A'+10);
00208                     else
00209                         iParseState = 0;
00210                     break;
00211       case 23:      // don't care about char (should be a CR)
00212                     // just check the results
00213                     if ( ucMsgChecksum==ucFinalChecksum )
00214                     {   // Checksum okay
00215                         // reset
00216                         iParseState = 0;
00217                         // return valid message
00218                         return true;
00219                     }
00220                     #if GPS_DEBUG>0
00221                       else
00222                           printf( "!GPS Check failed - got %02X wanted %02X\r\n", (int)ucMsgChecksum, (int)ucFinalChecksum );
00223                     #endif                            
00224                     // reset
00225                     iParseState = 0;
00226                     break;
00227     }
00228 
00229     #if GPS_DEBUG>2
00230       // report parser state
00231       printf( ":%d:", iParseState );
00232     #endif                            
00233 
00234     // GPS sentence note yet completed
00235     return false;
00236 }
00237 
00238 //---------------------------------------------------------------------------
00239 //
00240 //  Parse a number character by character
00241 //      If character is invalid, returns false
00242 //
00243 bool TGPSController::ParseNumber( char cChar )
00244 {
00245     // process digits
00246     if ( (cChar>='0') && (cChar<='9') )
00247     {
00248         ParseNum.uiNumber = (ParseNum.uiNumber*10) + (cChar-'0');
00249         ParseNum.iNumberLen++;
00250         if ( ParseNum.iNumberDecimals>=0 )
00251             ParseNum.iNumberDecimals++; 
00252         return true;
00253     }
00254     if ( cChar=='.' )
00255     {
00256         if ( ParseNum.iNumberDecimals>=0 )
00257             return false;   // a second decimal point!
00258         ParseNum.iNumberDecimals = 0;
00259         return true;
00260     }
00261     if ( cChar=='-' )
00262     {
00263         if ( ParseNum.iNumberLen>0 )
00264             return false;   // '-' must be the first character
00265         ParseNum.iNumberSign = -1;
00266         return true;
00267     }
00268     // otherwise an invalid character
00269     return false;
00270 }
00271 
00272 //---------------------------------------------------------------------------
00273 //
00274 //  Reset the number parser
00275 //
00276 void TGPSController::ResetNumber( )
00277 {
00278     ParseNum.uiNumber = 0;
00279     ParseNum.iNumberLen = 0;
00280     ParseNum.iNumberSign = 1;
00281     ParseNum.iNumberDecimals = -1;
00282     cCharacter = 0;
00283 }
00284 
00285 
00286 //---------------------------------------------------------------------------
00287 //
00288 //  Process the data from the GPS message
00289 //      Returns 0 if all data is good, or an error code
00290 //
00291 int TGPSController::ProcessGPSData( )
00292 {
00293     // check Quality
00294     if ( GPSQuality.iNumberLen<1 )          return 10;
00295     if ( GPSQuality.iNumberDecimals!=-1 )   return 11;
00296     if ( GPSQuality.iNumberSign!=1 )        return 12;
00297     // check Satellites
00298     if ( GPSSatellites.iNumberLen<1 )       return 20;
00299     if ( GPSSatellites.iNumberDecimals!=-1 ) return 21;
00300     if ( GPSSatellites.iNumberSign!=1 )     return 22;
00301     // Store sats and quality parameters
00302     GPSRecord.iGPSSatellites = GPSSatellites.uiNumber;
00303     GPSRecord.iGPSQuality = GPSQuality.uiNumber;
00304     // Check quality level
00305     if ( GPSQuality.uiNumber<1 )            return 100;     // no fix
00306     // check Time
00307     if ( GPSTime.iNumberLen<6 )            return 30;
00308     if ( GPSTime.iNumberSign!=1 )           return 32;
00309     // check Latitude
00310     if ( GPSLatitude.iNumberLen!=7 )        return 40;
00311     if ( GPSLatitude.iNumberDecimals!=3 )   return 41;
00312     if ( GPSLatitude.iNumberSign!=1 )       return 42;
00313     if ( (GPSLatNS!='N') && (GPSLatNS!='S') ) return 43;
00314     // check Longitude
00315     if ( GPSLongitude.iNumberLen!=8 )        return 50;
00316     if ( GPSLongitude.iNumberDecimals!=3 )   return 51;
00317     if ( GPSLongitude.iNumberSign!=1 )       return 52;
00318     if ( (GPSLongEW!='E') && (GPSLongEW!='W') ) return 53;
00319     // check Altitude
00320     if ( GPSAltitude.iNumberLen<1 )         return 60;
00321     // Don't care about DOPs and Height and Types
00322     // Translate & Store parameters
00323 
00324     // discard fractions of seconds
00325     while ( (GPSTime.iNumberDecimals--)>0 )
00326         GPSTime.uiNumber /= 10;
00327 
00328     int32_t iHours = GPSTime.uiNumber/10000;
00329     int32_t iMins = (GPSTime.uiNumber/100)%100;
00330     int32_t iSecs = GPSTime.uiNumber%100;
00331     GPSRecord.iGPSTimeSeconds = iSecs + (60*iMins) + (3600*iHours);
00332 
00333     int32_t iDegrees = GPSLatitude.uiNumber / 100000;
00334     int32_t iMilliMinutes = GPSLatitude.uiNumber % 100000;
00335     GPSRecord.iGPSLatMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000);
00336     if ( GPSLatNS=='S' )
00337         GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees;
00338 
00339     iDegrees = GPSLongitude.uiNumber / 100000;
00340     iMilliMinutes = GPSLongitude.uiNumber % 100000;
00341     GPSRecord.iGPSLongMicroDegrees = (iMilliMinutes * 100 / 6) + (iDegrees*1000000);
00342     if ( GPSLongEW=='W' )
00343         GPSRecord.iGPSLatMicroDegrees = -GPSRecord.iGPSLatMicroDegrees;
00344 
00345     GPSRecord.iGPSAltitudeMeters = GPSAltitude.uiNumber * GPSAltitude.iNumberSign;
00346     while ( (GPSAltitude.iNumberDecimals--)>0 )
00347         GPSRecord.iGPSAltitudeMeters /= 10;
00348     
00349     return 0;
00350 }
00351 
00352 //---------------------------------------------------------------------------
00353 //
00354 //  PPS Event routine
00355 //
00356 void TGPSController::PPSPulse()
00357 {
00358     // indicate we have pulsed
00359     bPulsed = true;
00360     
00361     // capture timer capture registers
00362     //  1PPP = CAP2.0
00363     //  LOsc (/4096) = CAP2.1
00364     uiPPSCapture = LPC_TIM2->CR0;
00365     uiLOscCapture = LPC_TIM2->CR1;
00366 
00367 }
00368 
00369 /*
00370 GPS Messages:
00371 
00372 GGA - essential fix data which provide 3D location and accuracy data.
00373 
00374  $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
00375 
00376 Where:
00377      GGA          Global Positioning System Fix Data
00378      123519       Fix taken at 12:35:19 UTC
00379      4807.038,N   Latitude 48 deg 07.038' N
00380      01131.000,E  Longitude 11 deg 31.000' E
00381      1            Fix quality: 0 = invalid
00382                                1 = GPS fix (SPS)
00383                                2 = DGPS fix
00384                                3 = PPS fix
00385                    4 = Real Time Kinematic
00386                    5 = Float RTK
00387                                6 = estimated (dead reckoning) (2.3 feature)
00388                    7 = Manual input mode
00389                    8 = Simulation mode
00390      08           Number of satellites being tracked
00391      0.9          Horizontal dilution of position
00392      545.4,M      Altitude, Meters, above mean sea level
00393      46.9,M       Height of geoid (mean sea level) above WGS84
00394                       ellipsoid
00395      (empty field) time in seconds since last DGPS update
00396      (empty field) DGPS station ID number
00397      *47          the checksum data, always begins with *
00398 
00399 
00400 VTG - Velocity made good. The gps receiver may use the LC prefix instead of GP if it is emulating Loran output.
00401 
00402   $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
00403 
00404 where:
00405         VTG          Track made good and ground speed
00406         054.7,T      True track made good (degrees)
00407         034.4,M      Magnetic track made good
00408         005.5,N      Ground speed, knots
00409         010.2,K      Ground speed, Kilometers per hour
00410         *48          Checksum
00411         
00412         
00413 
00414 $GPGGA,,,,,,0,05,,,,,,,*63
00415 $GPGGA,,,,,,0,02,,,,,,,*64
00416 $GPGGA,,,,,,0,03,,,,,,,*65
00417 $GPGGA,120609.46,3515.405,S,14904.873,E,0,04,2.24,718,M,14,M,,*42
00418  $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
00419  $GPGGA,120610.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4B
00420  $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
00421  $GPGGA,120611.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*4A
00422  $GPVTG,000.0,T,346.7,M,0.000,N,0.000,K*48
00423  $GPGGA,120612.46,3515.405,S,14904.873,E,1,04,2.24,718,M,14,M,,*49
00424 
00425 */
00426 
00427 //---------------------------------------------------------------------------
00428 //  END
00429 //---------------------------------------------------------------------------
00430