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
Diff: gps.cpp
- Revision:
- 0:82ff15078322
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gps.cpp Wed Jan 25 20:32:53 2012 +0000 @@ -0,0 +1,430 @@ +/*--------------------------------------------------------------------------- + + 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 +//--------------------------------------------------------------------------- +