Parallax GPS module raw mode reader in C for Baby Orangutans

I have finished the basic ‘raw mode’ Parallax GPS reader for the Baby Orangutan. It parses the $GPRMC data string for latitude, longitude, UTC date and time, etc. Here it is…

/*
A 'raw mode' data reader for the Parallax GPS receiver module #28146 (based on Polstar PMB-248)
for use with the Baby Orangutan robot controller or with slight modifications, other Pololu orangutans
(data stream is captured and parsed) scottfromscott@bellsouth.net
*/

#define F_CPU 20000000UL	// Baby Orangutan frequency (20MHz)
#include <math.h> 
#include <avr/io.h>
#include <util/delay.h> // uses F_CPU to achieve us and ms delays
#include <pololu/orangutan.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

char gps[650]; // array to store $GPRMS string

//format of Parallax GPS GPMRC string...
//$GPMRC,HHMMSS,A,DDMM.MMMM,S,DDDMM.MMMM,S,KKK.K,HHH.H,DDMMYY,,,A*64<13>
//0123456789012345678901234567890123456789012345678901234567890123456
//          1         2         3         4         5         6      
// $GPRMS string locations for following data items...
int utc_hours=7, utc_minutes=9, utc_seconds=11, latitude=16, \
                   latitude_hemisphere=26, longitude=28, longitude_hemisphere=39, speed=41, \
                   heading=47, utc_day=53, utc_month=55, utc_year=57;

void delayms(unsigned int time_ms)   
{for (unsigned int i = 0; i < time_ms; i++) _delay_ms(1);
}

//the following 4 debug functions flash an external LED I attached to #2 pwm motor port
//because the built-in RED LED is not available when the UART is being used
void debug_port(unsigned char pwm)
{  OCR2A = 0;
   OCR2B = pwm;
}

void debug_init()
{  TCCR2A = 0xF3;
   TCCR2B = 0x02;
   OCR2A = OCR2B = 0;
   DDRD |= (1 << PORTD3) | (1 << PORTD5) | (1 << PORTD6);
   DDRB |= (1 << PORTB3);
}

void debug_pulse_digit(int digit)
{  // pulse external led 1..10 times depending on value of digit
   // 1 -> 1 pulse; 2 -> 2 pulses; ... 9 -> 9 pulses; BUT 0 -> 10 pulses
   int counter, freq=75, delay=250;
   if(digit==0) digit = 10; // special case for 0
   for(counter=1;counter<=digit;counter++)
   {  debug_port(freq); 
      delayms(delay); 
      debug_port(0); 
      delayms(delay); 
   }
   delayms(1000);
}

void debug(float v)
{  // pulse 6 digits (hto.hto[-]) if negative, single pulse follows 6 pulse sequences
   // example output: -23.5 -> 023.500- -> 10 pulses, 2 pulses, 3 pulses, 5 pulses, 10 pulses, 10 pulses, 1 pulse 
   int o,t,h,d,n=0;
   if(v<0){v=-v;n=1;}
   h=(int)(v/100);t=(int)((v-h*100)/10);o=(int)(v-t*10-h*100);
   debug_pulse_digit(h);debug_pulse_digit(t);debug_pulse_digit(o);
   d=(int)((v-h*100-t*10-o)*1000); // .hto -> hto.
   h=(int)(d/100);t=(int)((d-h*100)/10);o=(int)(d-t*10-h*100);
   debug_pulse_digit(h);debug_pulse_digit(t);debug_pulse_digit(o);
   if(n) debug_pulse_digit(1); // seventh pulse indicates negative
   delayms(1000); //pause between numbers
}

float get_gps(int request)
{  float r;
   if(request==utc_hours)
      r=(gps[utc_hours+0]-48)*10+(gps[utc_hours+1]-48);
   else if(request==utc_minutes)
      r=(gps[utc_minutes+0]-48)*10+(gps[utc_minutes+1]-48);
   else if(request==utc_seconds)
      r=(gps[utc_seconds+0]-48)*10+(gps[utc_seconds+1]-48);
   else if(request==latitude)
   {  //accuracy: DDMM -- .MMMM ignored; minutes converted to decimal degrees
      r=(gps[latitude+0]-48)*10+(gps[latitude+1]-48)+(float)((gps[latitude+2]-48)*10+(gps[latitude+3]-48))/60;
      if(gps[latitude_hemisphere]=='S') r=-r; // apply sign
   }
   else if(request==longitude)
   {  //accuracy: DDDMM -- .MMMM ignored; minutes converted to decimal degrees
      r=(gps[longitude+0]-48)*100+(gps[longitude+1]-48)*10+(gps[longitude+2]-48) \
           +(float)((gps[longitude+3]-48)* 10+(gps[longitude+4]-48))/60;
      if(gps[longitude_hemisphere]=='W') r=-r; // apply sign
   }
   else if(request==speed)
      //fyi: mph = 6076/5280 * knots ~= 1.15*knots; speed below is in knots 
      r=(gps[speed+0]-48)*100+(gps[speed+1]-48)*10+(gps[speed+2]-48)+(float)(gps[speed+3]-48)*0.1;
   else if(request==heading)
      r=(gps[heading+0]-48)*100+(gps[heading+1]-48)*10+(gps[heading+2]-48)+(float)(gps[heading+3]-48)*0.1;
   else if(request==utc_day)
      r=(gps[utc_day+0]-48)*10+(gps[utc_day+1]-48);
   else if(request==utc_month)
      r=(gps[utc_month+0]-48)*10+(gps[utc_month+1]-48);
   else if(request==utc_year)
      r=(gps[utc_year+0]-48)*10+(gps[utc_year+1]-48);
   else
   {
      debug(555);
      while(1); // error
      r = 0;
   }
   return r;
}

void init_gps()  
{  //delayms(40000); //may require delay on cold start for gps module to acquire satellites
   debug_init();
   serial_set_baud_rate(4800);
   char buf[1];
 	// request 1 char at a time from gps and copy to char array gps[]
   int gps_index, byte_count;
   for(gps_index=0;gps_index<=649;gps_index++)
   {  serial_receive(buf,1);
	   byte_count = serial_get_received_bytes();
	   while(byte_count == 0) byte_count = serial_get_received_bytes();
	   gps[gps_index]=buf[0];
   }
   //find first instance of "$GPRMC" in gps[]
   char type[6] = "$GPRMC";
   int type_matched = 0;
   int type_index;

   for(gps_index=0;gps_index<=644;gps_index++)
   {  for(type_index=0; type_index<=5;type_index++)
	   {  if(gps[gps_index+type_index] == type[type_index]) type_matched=1;
		   else 
		   {  type_matched = 0;
		      break;
         }
	   } 
	  if(type_matched) break;
   }
   // upon exit type_matched==1 and gps_index <645 else error...

   if(!type_matched)
   {  debug(555); // my arbitrary error code is 555
      while(1);
   } 
   // if we made it this far, everything ok

	//move data to start of array...

	for(int k=0;k<=65;k++) gps[k] = gps[gps_index+k];
}

int main()
{
   init_gps();

   // at this point you  may access gps values captured using 'get_gps(<keyword>)'
   // where <keyword> is any of: utc_hours, utc_minutes, utc_seconds, latitude,
   // longitude, speed, heading, utc_day, utc_month, utc_year
   // return type is float
   // note:
   // [-]longitude -> west of prime meridian, [-]latitude -> south of equator
   //example queries...
   //float lon = get_gps(longitude);
   //debug(lon); 
   //debug(get_gps(latitude));
   //delayms(5000);
   //debug(get_gps(utc_hours));

   while(1); // wait forever
   return 0;
}

gpsdebug.zip (2.45 KB)

Hi Scott,

I´m a new bee with avr and C++ I would like to know if is possible to use this code and print the Lat, Lon etc on the LCD. In the picture seems that you connect the GPS on PD0 and what I´m trying to do is the same thing with Magellan GPS315 with a magellan string like this one: //PMGNTRK,4322.061,N,07948.473,W,00116,M,173949.42,A,020602*67

Thank you!

Best regards.

Jose Luis