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 //---------------------------------------------------------------------------