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

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