GPS interpreter

General discussion on mikroBasic.
Author
Message
Durieux
Posts: 6
Joined: 13 Feb 2006 19:57

GPS interpreter

#1 Post by Durieux » 24 Mar 2006 21:48

Hi -

For those of you interested in GPS stuff... Attached code for a GPS interpreter that reads and parses NMEA messages coming in to USART, and writes them to LCD. In addition, it will allow you to store your present location to EEPROM and from then on calculate instructions on how to get back there.

This version was compiled on MB 4.02. It runs on a P18F452.

Marcel

Code: Select all

'GPS interpreter

'Marcel Durieux, 2006

'Reads in NMEA strings from any GPS receiver unit (over USART), and outputs (on LCD)
'location, speed, course, altitude, accuracy and number of satellites in view, as well as
'universal date and time.
'In addition, it allows saving a current location (in NV memory) and will calculate distance,
'direction and deviation from the desired direction. These calculations are performed
'using great-circle formulae.

'Not implemented, but easy to add, is serial output of the results to some other device.

'The program interprets two of the many NMEA strings, RMC and GGA. Since there
'are slight differences in NMEA versions (# of digits, etc), here are examples
'of valid strings for this program.

'$GPRMC,214200.875,A,3806.5677,N,07826.1267,W,000.0,000.0,120206,009.7,W*69
'$GPGGA,214201.875,3806.5677,N,07826.1267,W,1,05,01.8,00124.4,M,-34.3,M,,*51

'A good overview of the various NMEA output is available at http://aprs.gids.nl/nmea/

'The program runs on a P18F452 clocked at 20 MHz.

program GPS

'data variables
dim Gstr as string[65] 'to hold NMEA sentence, subsequently to be dissected into:
dim LaD as string[2] 'Latitude degrees
dim LaM1 as string[2] 'Latitude whole minutes
dim LaM2 as string[4] 'Latitude fractional minutes
dim LoD as string[3] 'Longitude degrees
dim LoM1 as string[2] 'Longitude whole minutes
dim LoM2 as string[4] 'Longitude fractional minutes
dim LoDir as char 'Longitude direction (E or W)
dim LaDir as char 'Latitude direction (N or S)
dim Kspeed as string[3] 'Speed in knots (as read from device)
dim Crs as string[3] 'Course
dim Galt as string[4] 'Altitude
dim Gacc as string[3] 'Accuracy
dim Gstatus as char 'Status: valid (A) or invalid (V) fix
dim Gsats as string[2] 'number of satellites in view
dim GDate as string[6] 'date
dim GTime as string[6] 'time
'calculated values
dim Mspeed as string[3] 'Calculated speed in mph
dim Cdir as integer ' store numeric version of Crs
dim Dev as integer 'deviation from desired course
dim La1,La2,Lo1,Lo2 as float 'numeric values of Latitude, Longitude
dim Direct,Dist as float 'calculated direction and distance to target

'utility variables
dim i as byte
dim tmp as float
dim GMode as byte 'to hold display mode. The program cycles through 4 display
'modes by briefly pulling either B.7 or B.6 low:
'1. coordinates and number of satellites in view
'2. speed, course, altitude and accuracy
'3. date and time
'4. distance, direction and deviation to target location
dim fnc as byte 'to cycle through NMEA sentences: 1=RMC, 2=GGA
dim WriteFlag as byte 'determines if display needs updating
dim FltStr as string[17] 'to display floats on LCD
dim IntStr as string[6]  'to display integers on LCD

'subroutines:

sub function sq(dim a as real) as real 'calculates square
  Result=a*a
end sub

sub function ReadChr as byte 'reads one char from USART port
    do
    loop until USART_Data_Ready = 1
    result=USART_Read 'read char
end sub

sub procedure CopyGStr(dim start as byte, dim l as byte, dim byref txt as string[10])
'extracts 'l' chars from 'Gstr' (which holds the NMEA sentence) starting at 'start'
	dim i as byte

  for i=0 to l
    txt[i]=Gstr[start+i]
  next i
  txt[l]=chr(0) 'to terminate the string
end sub

main:

'device setup
TRISB=$FF 'two buttons will be connected to B.7 and B.6. Pulling B.6 low
'cycles through the 4 modes (see above), pulling both low saves current location
PORTB=0
TRISD=0 'for 2x16 CLD char unit
USART_Init(4800) 'init USART at 4800 baud to interface with NMEA device
LCD_Init(PORTD) 'init LCD on port D
LCD_Cmd(LCD_Cursor_Off)
LCD_Cmd(LCD_Clear)

'initialize variables
Gstatus="V" 'assume invalid fix at startup
fnc = 1 'get ready to look for first NMEA string
WriteFlag=0 'no data to display yet
GMode=1 'location display
Gsats="00" 'no satellites in view

while true 'main program loop
'monitor NMEA data until a '$' is received, signaling start of a sentence
  do
    i=ReadChr
  loop until i=36 'loop until "$" received
'read sentence (from '$' through '*') into the variable Gstr
	i=0
  do
    Gstr[i]=ReadChr
    if Gstr[i]="*" then
      i=0
    else
      i=i+1
    end if
  loop until (i=0) or (i=65) 'break at 65 chars if for some reason no '*' is found
'now look for RMC or GGA, depending on 'fnc' value.
	if fnc=1 then 'look for RMC
    if ((Gstr[2]="R") and (Gstr[3]="M") and (Gstr[4]="C")) then
      fnc=2 'next read will read GGA
      WriteFlag=1 'get ready to display data
      i=Gstatus
      Gstatus=Gstr[17] 'get status char
      if Gstatus<>i then 'i.e. if status has changed
        LCD_Cmd(LCD_Clear)
      end if
      if Gstatus="A" then ' valid fix, copy the variable from Gstr
        CopyGStr(6,6,GTime)
        CopyGStr(56,6,GDate)
        CopyGStr(19,2,LaD) 'get latitude
        CopyGStr(21,2,LaM1)
        CopyGStr(24,4,LaM2)
        LaDir=GStr[29] 'get latitude direction
        CopyGStr(31,3,LoD) 'get longitude
        CopyGstr(34,2,LoM1)
        CopyGstr(37,4,LoM2)
        LoDir=GStr[42] 'get longitude direction
        CopyGStr(44,3,Kspeed) 'get speed and convert to mph
        i=(Kspeed[0]*100+Kspeed[1]*10+Kspeed[2])*1.15 'first numeric
      	ByteToStr(i,Mspeed) 'then back to string for display
				CopyGStr(50,3,Crs) 'get course
				Cdir=(Crs[0]-48)*100+(Crs[1]-48)*10+Crs[2]-48 'convert to number
      end if
    end if
  end if
  if fnc=2 then 'look for GGA
    if ((Gstr[2]="G") and (Gstr[3]="G") and (Gstr[4]="A")) then
      fnc=1 'next time round, look for RMC
      WriteFlag=1 'update display
      CopyGStr(44,2,Gsats) 'get #sats
      if Gstatus="A" then
        CopyGStr(48,3,Gacc) 'get accuracy
        CopyGstr(53,4,Galt) 'get altitude
      end if
    end if
  end if

'if we have valida data (i.e. WriteFlag=1) then...
    if WriteFlag=1 then
'1. check if the user wants this location stored in EEPROM
     if (PORTB.6=0) and (PORTB.7=0) then 'if BOTH buttons pressed
       LCD_Cmd(LCD_Clear)
       LCD_Out(1,1,"Location saved") 'store current location in EEPROM
'subtracting 48 is to get from ASCII to numeric value
       EEPROM_Write(0,LaD[0]-48)
       EEPROM_Write(1,LaD[1]-48)
       EEPROM_Write(2,LaM1[0]-48)
       EEPROM_Write(3,LaM1[1]-48)
       EEPROM_Write(4,LaM2[0]-48)
       EEPROM_Write(5,LaM2[1]-48)
       EEPROM_Write(6,LaM2[2]-48)
       EEPROM_Write(7,LaM2[3]-48)
       EEPROM_Write(8,LoD[0]-48)
       EEPROM_Write(9,LoD[1]-48)
       EEPROM_Write(10,LoD[2]-48)
       EEPROM_Write(11,LoM1[0]-48)
       EEPROM_Write(12,LoM1[1]-48)
       EEPROM_Write(13,LoM2[0]-48)
       EEPROM_Write(14,LoM2[1]-48)
       EEPROM_Write(15,LoM2[2]-48)
       EEPROM_Write(16,LoM2[3]-48)
       EEPROM_Write(17,LaDir)
       EEPROM_Write(18,LoDir)
       delay_ms(5000)
       LCD_Cmd(LCD_Clear)
     end if
'2. check if the user wants to change modes
     if PORTB.6=0 then ' if B.6 is pulled low
       GMode=Gmode+1  'cycle through modes
       if Gmode=5 then
         Gmode=1
       end if
       LCD_Cmd(LCD_Clear)
       do
       loop until PORTB.7=1 'button released
     end if
'and here are the disaply routines. First, if no valid data is available...
		 if Gstatus="V" then 'no valid fix
       LCD_Out(1,1,"Searching...")
     else 'valid fix
       select case GMode
         case 1 'location display
           LCD_Chr(1,1," ")
           LCD_Out(1,2,LaD)
           LCD_Chr(1,4,"*")
           LCD_Out(1,5,LaM1)
           LCD_Chr(1,7,".")
           LCD_Out(1,8,LaM2)
           LCD_Chr(1,12,LaDir)
           LCD_Chr(1,14,"S")
           LCD_Out(1,15,Gsats)
           LCD_Out(2,1,LoD)
           LCD_chr(2,4,"*")
           LCD_Out(2,5,LoM1)
           LCD_Chr(2,7,".")
           LCD_Out(2,8,LoM2)
           LCD_Chr(2,12,LoDir)
         case 2 ' speed, direction, altitude, accuracy
           LCD_Out(1,1,"Spd:")
           LCD_Out(1,5,KSpeed)
           LCD_Out(1,10,"Crs:")
           LCD_Out(1,14,Crs)
           LCD_Out(2,1,"Alt:")
           LCD_Out(2,5,Galt)
           LCD_Out(2,10,"Acc:")
           LCD_Out(2,14,Gacc)
         case 3 'time, date
           LCD_Out(1,1,"Date:")
           LCD_Chr(1,7,GDate[2])
           LCD_Chr(1,8,GDate[3])
           LCD_Chr(1,9,"-")
           LCD_Chr(1,10,GDate[0])
           LCD_Chr(1,11,GDate[1])
           LCD_Chr(1,12,"-")
           LCD_Chr(1,13,GDate[4])
           LCD_Chr(1,14,GDate[5])
           LCD_Out(2,1,"Time:")
           LCD_Chr(2,7,GTime[0])
           LCD_Chr(2,8,GTime[1])
           LCD_Chr(2,9,":")
           LCD_Chr(2,10,GTime[2])
           LCD_Chr(2,11,GTime[3])
           LCD_Chr(2,12,":")
           LCD_Chr(2,13,GTime[4])
           LCD_Chr(2,14,GTime[5])
         case 4 'directions to saved location. This is more complicated
					'read target location from EEPROM and convert to numeric
					La2=EEPROM_Read(0)*10+EEPROM_Read(1) 'degrees
					tmp=(EEPROM_Read(2)*10+EEPROM_Read(3))/60 'minutes
					La2=La2+tmp
					tmp=(EEPROM_Read(4)*1000+EEPROM_Read(5)*100+EEPROM_Read(6)*10+EEPROM_Read(7))/600000 'dec minutes
					La2=La2+tmp

					Lo2=EEPROM_Read(8)*100+EEPROM_Read(9)*10+EEPROM_Read(10) 'degrees
					tmp=(EEPROM_Read(11)*10+EEPROM_Read(12))/60 'minutes
					Lo2=Lo2+tmp
					tmp=(EEPROM_Read(13)*1000+EEPROM_Read(14)*100+EEPROM_Read(15)*10+EEPROM_Read(16))/600000 'dec minutes
					Lo2=Lo2+tmp
					
					if EEPROM_Read(17)<>$4e then '"N"
						La2=-La2
					end if
					if EEPROM_Read(18)<>$57 then '"W"
						Lo2=-Lo2
					end if

					'calculate numeric value of current position
					La1=(LaD[0]-48)*10+LaD[1]-48
					tmp=((LaM1[0]-48)*10+LaM1[1]-48)/60
					La1=La1+tmp
					tmp=((LaM2[0]-48)*1000+(LaM2[1]-48)*100+(LaM2[2]-48)*10+(LaM2[3]-48))/600000
					La1=La1+tmp
					if LaDir="S" then
						La1=-La1
					end if
					
					Lo1=(LoD[0]-48)*100+(LoD[1]-48)*10+LoD[2]-48
					tmp=((LoM1[0]-48)*10+(LoM1[1]-48))/60
					Lo1=Lo1+tmp
					tmp=((LoM2[0]-48)*1000+(LoM2[1]-48)*100+(LoM2[2]-48)*10+LoM2[3]-48)/600000
					Lo1=Lo1+tmp
					if LoDir="E" then
						Lo1=-Lo1
					end if
					
					'here comes the trigonometry! Calculate direction and distance from
					'current location to target.
					'First, convert to radians
					La1=La1*pi/180
					Lo1=Lo1*pi/180
					La2=La2*pi/180
					Lo2=Lo2*pi/180
					'calculate distance using great circle formula
					Dist=2*Asin(sqrt(sq(sin((La1-La2)/2))+cos(La1)*cos(La2)*sq(Sin((Lo1-Lo2)/2))))
					'calculate direction
					Direct=acos((sin(La2)-sin(La1)*cos(Dist))/(sin(Dist)*cos(La1)))
					if sin(Lo2-Lo1)>0 then
					  Direct=2*pi-Direct
          end if
					'convert results
					Direct=(180/pi)*Direct 'convert to degrees
					Dist=3982*Dist ' convert to miles

					'calculate deviation
					'0=moving straight towards target. Neg values=moving too far to the
					'left. Pos values=moving too far to the right
					Dev=CDir-Direct
     			if Dev>180 then
					   Dev=Dev-360
					end if
					if Dev<(-180) then
						 Dev=Dev+360
					end if
					
					'display
					FloatToStr(Dist,FltStr)
     			LCD_Out(1,1,"Dist:")
					LCD_out(1,7,FltStr)
					IntToStr(Floor(Direct+0.5),IntStr)
					LCD_Out(2,1,"Dir:")
					LCD_Chr(2,5,IntStr[3])
					LCD_Chr(2,6,IntStr[4])
					LCD_Chr(2,7,IntStr[5])
     			LCD_Out(2,9,"Dev:")
          IntToStr(Dev,IntStr)
					LCD_Chr(2,13,IntStr[2])
					LCD_Chr(2,14,IntStr[3])
					LCD_Chr(2,15,IntStr[4])
					LCD_Chr(2,16,IntStr[5])

         end select
       end if
       WriteFlag=0
   end if
wend

end.

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#2 Post by xor » 24 Mar 2006 22:19

Don't have anything to try it out with, but thanks for the code. This is always very helpful to others for projects.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

User avatar
zristic
mikroElektronika team
Posts: 6608
Joined: 03 Aug 2004 12:59
Contact:

Re: GPS interpreter

#3 Post by zristic » 25 Mar 2006 10:50

Great code!

The project page opens soon.

Nebojsa
mikroElektronika team
Posts: 282
Joined: 16 Aug 2004 18:54
Contact:

#4 Post by Nebojsa » 26 Mar 2006 18:12

xor wrote:Don't have anything to try it out with, but thanks for the code. This is always very helpful to others for projects.
Mikroelektornika will have GPRS and GPS modules soon, ready to use functions will be added to all compilers.

okutkan
Posts: 249
Joined: 07 Sep 2005 18:13
Location: Istanbul

#5 Post by okutkan » 27 Mar 2006 21:37

Hi Nebojsa,

this is the greatest news I heard since I signed in this forum.

I am looking forward to see these new goodies.

Can you give us any details or spy photos maybe :lol:
"A well-written program is its own heaven; a poorly-written program is its own hell."

Supergumby
Posts: 26
Joined: 10 Dec 2004 13:20
Location: Washington, DC

#6 Post by Supergumby » 29 Mar 2006 02:53

Or pricing range?

I've been wanting to build a GPS clock for a while.

kkjensen
Posts: 50
Joined: 30 Mar 2006 18:16

Great!

#7 Post by kkjensen » 30 Mar 2006 18:25

Hey! Great work on this...I've been trying to figure out how to extract NMEA info with the string library but am still a bit new to PIC programming. MikroBasic has definately been the big break I've been looking for:

Couple questions:
1. when I compile the code it stops on this line like there's an error and I don't know why:

"ByteToStr(i,Mspeed) 'then back to string for display"

the ByteToStr function seems to be used properly...

I'm using the demo version of the software...does this code exceed the 2000 line limit for the demo? I know just by using the LCD library the compiled assembly code blows up to over 500 lines. I don't really know if it's 2000 lines of code in basic, assembly or hex that is the limit.

2. Will this code only work on the 18F452 or will the 18F4520 (the newer replacement) work? What about 16F chips with a similar pinout, such as the 16F877? I don't know how to tell how much memory is required...the 18F4520 is definately the best of the bunch but I'd rather try to use one of my PICs that I've got laying around.

3. GPS clock? Cool idea...I've only used a handheld GPS unit before...can you buy a GPS unit without all the extras (i.e. plastic case, LCD screen, etc etc...just the antenna and whatever it takes to output a NMEA string)

Mardaso
Posts: 14
Joined: 23 Apr 2006 12:07
Location: Netherlands

#8 Post by Mardaso » 23 Apr 2006 12:18

Hello, great code! But i do have some problems with it. I've tried this on an 18F458 and an 16F877 the code compiles but doesn't run. (i have a registed version of MB 4.003) It's generating 20K of code! When i strip the code and leaving some code out (the CopyGStr part) it will run.(1k code). Has someone a solution for this one?

Thanks Marcel

Code: Select all


'******************************************************************************
' microcontroller   P16F877
'
' Project  GPS_Lcd
' This project is designed to work with PIC 16F877
' with minor adjustments, it should work with any other PIC MCU
'
' This code demonstrates displaying text on LCD (4 bit intrface)
' and use of LCD library procedures and functions.
'******************************************************************************

program GPS_Lcd

'data variables
dim Gstr as string[65] 'to hold NMEA sentence, subsequently to be dissected into:
dim LaD as string[2] 'Latitude degrees
dim LaM1 as string[2] 'Latitude whole minutes
dim LaM2 as string[4] 'Latitude fractional minutes
dim LoD as string[3] 'Longitude degrees
dim LoM1 as string[2] 'Longitude whole minutes
dim LoM2 as string[4] 'Longitude fractional minutes
dim LoDir as char 'Longitude direction (E or W)
dim LaDir as char 'Latitude direction (N or S)
dim Kspeed as string[3] 'Speed in knots (as read from device)
dim Crs as string[3] 'Course
dim Galt as string[4] 'Altitude
dim Gacc as string[3] 'Accuracy
dim Gstatus as char 'Status: valid (A) or invalid (V) fix
dim Gsats as string[2] 'number of satellites in view
dim GDate as string[6] 'date
dim GTime as string[6] 'time
'calculated values
dim Mspeed as string[3] 'Calculated speed in mph
dim Cdir as integer ' store numeric version of Crs
dim Dev as integer 'deviation from desired course
dim La1,La2,Lo1,Lo2 as float 'numeric values of Latitude, Longitude
dim Direct,Dist as float 'calculated direction and distance to target

'utility variables
dim i as byte
dim tmp as float
dim GMode as byte 'to hold display mode. The program cycles through 4 display
'modes by briefly pulling either B.7 or B.6 low:
'1. coordinates and number of satellites in view
'2. speed, course, altitude and accuracy
'3. date and time
'4. distance, direction and deviation to target location
dim fnc as byte 'to cycle through NMEA sentences: 1=RMC, 2=GGA
dim WriteFlag as byte 'determines if display needs updating
dim FltStr as string[17] 'to display floats on LCD
dim IntStr as string[6]  'to display integers on LCD

'subroutines:

sub function sq(dim a as real) as real 'calculates square
  Result=a*a
end sub

sub function ReadChr as byte 'reads one char from USART port
    do
    loop until USART_Data_Ready <> 0
    result=USART_Read 'read char
    Usart_Write(result)              ' Send data via USART to PC
end sub

sub procedure CopyGStr(dim start as byte, dim l as byte, dim byref txt as string[10])
'extracts 'l' chars from 'Gstr' (which holds the NMEA sentence) starting at 'start'
   dim y as byte

  for y=0 to l
    txt[y]=Gstr[start+y]
  next y
  txt[l]=chr(0) 'to terminate the string
end sub


main:
  TRISB=0
  LCD_Config(PORTB,1,3,2,7,6,5,4)
  'Lcd_Init(PORTB)                  ' Initialize LCD connected to PORTB
  Lcd_Cmd(LCD_CLEAR)               ' Send command to LCD "clear display"
  Lcd_Cmd(LCD_CURSOR_OFF)          ' Send command cursor off
  Lcd_Out(2,1,"mikroElektronika")  ' Print txt to LCD, 2nd row, 1st column
  delay_ms(500)
  Usart_init(4800)                 ' Initialize USART module (8 bit, 2400 baud rate, no parity bit...
  Delay_ms(500)
  Lcd_Cmd(LCD_CLEAR)               ' Send command to LCD "clear display"
  Delay_ms(1000)
  Lcd_Out(1,1,"GPS Info MD06")    ' Print txt to LCD, 2nd row, 1st column
  Lcd_Out(2,1,"GPS Info MD06")    ' Print txt to LCD, 2nd row, 1st column
  delay_ms(500)

'initialize variables
Gstatus="V" 'assume invalid fix at startup
fnc = 1 'get ready to look for first NMEA string
WriteFlag=0 'no data to display yet
GMode=1 'location display
Gsats="00" 'no satellites in view

while true 'main program loop
'monitor NMEA data until a '$' is received, signaling start of a sentence
  do
    i=ReadChr
  loop until i=36 'loop until "$" received
'read sentence (from '$' through '*') into the variable Gstr
   i=0
  do
    Gstr[i]=ReadChr
    if Gstr[i]="*" then
      i=0
    else
      i=i+1
    end if
  loop until (i=0) or (i=65)   'Break at 65 chars if for some reason no '*' is found
'now look for RMC or GGA, depending on 'fnc' value.
   if fnc=1 then               'look for RMC
    if ((Gstr[2]="R") and (Gstr[3]="M") and (Gstr[4]="C")) then
      fnc=2                    'next read will read GGA
      WriteFlag=1              'get ready to display data
      i=Gstatus
      Gstatus=Gstr[17]         'get status char
      if Gstatus<>i then       'i.e. if status has changed
        LCD_Cmd(LCD_Clear)
      end if
      if Gstatus="A" then      ' valid fix, copy the variable from Gstr
'        CopyGStr(6,6,GTime)
'        CopyGStr(56,6,GDate)
'        CopyGStr(19,2,LaD)     'get latitude
'        CopyGStr(21,2,LaM1)
'        CopyGStr(24,4,LaM2)
'        LaDir=GStr[29]         'get latitude direction
'        CopyGStr(31,3,LoD)     'get longitude
'        CopyGstr(34,2,LoM1)
'        CopyGstr(37,4,LoM2)
'        LoDir=GStr[42]         'get longitude direction
      end if
    end if
  end if
'if we have valida data (i.e. WriteFlag=1) then...

  if WriteFlag=1 then
    'and here are the display routines. First, if no valid data is available...
      if Gstatus="V" then      'no valid fix
          LCD_Out(1,1,"Searching...   ")
      else                     'valid fix
           LCD_Out(1,1,"GPS Fix!       ")
           LCD_Chr(1,1," ")
           LCD_Out(1,2,LaD)
           LCD_Chr(1,4,"*")
           LCD_Out(1,5,LaM1)
           LCD_Chr(1,7,".")
           LCD_Out(1,8,LaM2)
           LCD_Chr(1,12,LaDir)
           LCD_Chr(1,14,"S")
           LCD_Out(1,15,Gsats)
           LCD_Out(2,1,LoD)
           LCD_chr(2,4,"*")
           LCD_Out(2,5,LoM1)
           LCD_Chr(2,7,".")
           LCD_Out(2,8,LoM2)
           LCD_Chr(2,12,LoDir)
      end if
      WriteFlag=0
  end if
wend

end.


kkjensen
Posts: 50
Joined: 30 Mar 2006 18:16

compiled for 877

#9 Post by kkjensen » 24 Apr 2006 12:37

Hi there,

I stripped the code of the eeprom parts and the code for extracting the info from the GPS output does work quite well. I've only got the demo version of M.B. so compiling the whole thing isn't a possibility and I don't know why the fully compiled code won't work on an 16F877...is it lacking the memory?

I would like to modify some of this code to create a gps log file on a memory stick using the SD/MMC libraries. Any ideas or insites on how to setup a log file?

ttfn,

Kris

P.S. By the way I found the problem to my previous post...I needed to upgrade M.B. to the newest demo. I'd sure like to get the full version now that I've got some stuff working.

luca crotti
Posts: 100
Joined: 06 Nov 2004 21:50

Re: GPS interpreter

#10 Post by luca crotti » 08 May 2006 20:47

hi,
I tried the code you (kindly) shared with the forum.
At the beginning I had some difficulties to have the LCD working.
I added a few lines, as a test, and the LCD worked fine...
so I tried to bypass something, and finally I had the code working (for PIC18F452 @ 20MHz). Here's the code:

Code: Select all


program GPS_PIC_DISPLAY
'GPS interpreter
'++++++++++++++++++++++++++++
' configurazione:
' LCD su PORTD
' BUTTONS on PORTD.7 and PORTD.6
' SERIAL BAUD=4800
'++++++++++++++++++++++++++++
' USATO SU EASYPIC2:
' configurazione:
' LCD su PORTB
' BUTTONS on PORTD.7 and PORTD.6
' SERIAL BAUD=4800
'
'Reads in NMEA strings from any GPS receiver unit (over USART), and outputs (on LCD)
'location, speed, course, altitude, accuracy and number of satellites in view, as well as
'universal date and time.
'In addition, it allows saving a current location (in NV memory) and will calculate distance,
'direction and deviation from the desired direction. These calculations are performed
'using great-circle formulae.

'Not implemented, but easy to add, is serial output of the results to some other device.

'The program interprets two of the many NMEA strings, RMC and GGA. Since there
'are slight differences in NMEA versions (# of digits, etc), here are examples
'of valid strings for this program.

'$GPRMC,214200.875,A,3806.5677,N,07826.1267,W,000.0,000.0,120206,009.7,W*69
'$GPGGA,214201.875,3806.5677,N,07826.1267,W,1,05,01.8,00124.4,M,-34.3,M,,*51
'A good overview of the various NMEA output is available at http://aprs.gids.nl/nmea/
'The program runs on a P18F452 clocked at 20 MHz.

'data variables
dim Gstr as string[65] 'to hold NMEA sentence, subsequently to be dissected into:
dim LaD as string[2] 'Latitude degrees
dim LaM1 as string[2] 'Latitude whole minutes
dim LaM2 as string[4] 'Latitude fractional minutes
dim LoD as string[3] 'Longitude degrees
dim LoM1 as string[2] 'Longitude whole minutes
dim LoM2 as string[4] 'Longitude fractional minutes
dim LoDir as char 'Longitude direction (E or W)
dim LaDir as char 'Latitude direction (N or S)
dim Kspeed as string[3] 'Speed in knots (as read from device)
dim Crs as string[3] 'Course
dim Galt as string[4] 'Altitude
dim Gacc as string[3] 'Accuracy
dim Gstatus as char 'Status: valid (A) or invalid (V) fix
dim Gsats as string[2] 'number of satellites in view
dim GDate as string[6] 'date
dim GTime as string[6] 'time
'calculated values
dim Mspeed as string[3] 'Calculated speed in mph
dim Cdir as integer ' store numeric version of Crs
dim Dev as integer 'deviation from desired course
dim La1,La2,Lo1,Lo2 as float 'numeric values of Latitude, Longitude
dim Direct,Dist as float 'calculated direction and distance to target

'utility variables
dim i as byte
dim tmp as float
dim GMode as byte 'to hold display mode. The program cycles through 4 display
'modes by briefly pulling either B.7 or B.6 low:
'1. coordinates and number of satellites in view
'2. speed, course, altitude and accuracy
'3. date and time
'4. distance, direction and deviation to target location
dim fnc as byte 'to cycle through NMEA sentences: 1=RMC, 2=GGA
dim WriteFlag as byte 'determines if display needs updating
dim FltStr as string[17] 'to display floats on LCD
dim IntStr as string[6]  'to display integers on LCD

'subroutines:

sub function sq(dim a as real) as real 'calculates square
  Result=a*a
end sub

sub function ReadChr as byte 'reads one char from USART port
    do
    loop until USART_Data_Ready = 1
    result=USART_Read 'read char
end sub

sub procedure CopyGStr(dim start as byte, dim l as byte, dim byref txt as string[10])
'extracts 'l' chars from 'Gstr' (which holds the NMEA sentence) starting at 'start'
   dim i as byte

  for i=0 to l
    txt[i]=Gstr[start+i]
  next i
  txt[l]=chr(0) 'to terminate the string
end sub

main:

'device setup
'PORTB=0
'TRISB=$00 'for 2x16 CLD char unit
PORTD=0
TRISD=%11000000 'for buttons on RD.6 and RD.7
LCD_Init(PORTB) 'init LCD on port B
Delay_ms(300)
LCD_Cmd(LCD_Cursor_Off)
LCD_Cmd(LCD_Clear)
LCD_OUT(1,1," prova testo: ")
Delay_ms(1000)
'goto pippo
USART_Init(4800) 'init USART at 4800 baud to interface with NMEA device
Delay_ms(500)
'initialize variables
Gstatus="V" 'assume invalid fix at startup
fnc = 1 'get ready to look for first NMEA string
WriteFlag=0 'no data to display yet
GMode=1 'location display
Gsats="00" 'no satellites in view

while true 'main program loop
'monitor NMEA data until a '$' is received, signaling start of a sentence
  do
    i=ReadChr
  loop until i=36 'loop until "$" received
'read sentence (from '$' through '*') into the variable Gstr
   i=0
  do
    Gstr[i]=ReadChr
    if Gstr[i]="*" then
      i=0
    else
      i=i+1
    end if
  loop until (i=0) or (i=65) 'break at 65 chars if for some reason no '*' is found
'now look for RMC or GGA, depending on 'fnc' value.
   if fnc=1 then 'look for RMC
    if ((Gstr[2]="R") and (Gstr[3]="M") and (Gstr[4]="C")) then
      fnc=2 'next read will read GGA
      WriteFlag=1 'get ready to display data
      i=Gstatus
      Gstatus=Gstr[17] 'get status char
      if Gstatus<>i then 'i.e. if status has changed
        LCD_Cmd(LCD_Clear)
      end if
      if Gstatus="A" then ' valid fix, copy the variable from Gstr
        CopyGStr(6,6,GTime)
        CopyGStr(56,6,GDate)
        CopyGStr(19,2,LaD) 'get latitude
        CopyGStr(21,2,LaM1)
        CopyGStr(24,4,LaM2)
        LaDir=GStr[29] 'get latitude direction
        CopyGStr(31,3,LoD) 'get longitude
        CopyGstr(34,2,LoM1)
        CopyGstr(37,4,LoM2)
        LoDir=GStr[42] 'get longitude direction
        CopyGStr(44,3,Kspeed) 'get speed and convert to mph
        i=(Kspeed[0]*100+Kspeed[1]*10+Kspeed[2])*1.15 'first numeric
         ByteToStr(i,Mspeed) 'then back to string for display
            CopyGStr(50,3,Crs) 'get course
            Cdir=(Crs[0]-48)*100+(Crs[1]-48)*10+Crs[2]-48 'convert to number
      end if
    end if
  end if
  if fnc=2 then 'look for GGA
    if ((Gstr[2]="G") and (Gstr[3]="G") and (Gstr[4]="A")) then
      fnc=1 'next time round, look for RMC
      WriteFlag=1 'update display
      CopyGStr(44,2,Gsats) 'get #sats
      if Gstatus="A" then
        CopyGStr(48,3,Gacc) 'get accuracy
        CopyGstr(53,4,Galt) 'get altitude
      end if
    end if
  end if

'if we have valida data (i.e. WriteFlag=1) then...
    if WriteFlag=1 then


'1. check if the user wants this location stored in EEPROM
'goto fine1
     if (PORTD.6=0) and (PORTD.7=0) then 'if BOTH buttons pressed
       LCD_Cmd(LCD_Clear)
       LCD_Out(1,1,"Location saved") 'store current location in EEPROM
'subtracting 48 is to get from ASCII to numeric value
       EEPROM_Write(0,LaD[0]-48)
       EEPROM_Write(1,LaD[1]-48)
       EEPROM_Write(2,LaM1[0]-48)
       EEPROM_Write(3,LaM1[1]-48)
       EEPROM_Write(4,LaM2[0]-48)
       EEPROM_Write(5,LaM2[1]-48)
       EEPROM_Write(6,LaM2[2]-48)
       EEPROM_Write(7,LaM2[3]-48)
       EEPROM_Write(8,LoD[0]-48)
       EEPROM_Write(9,LoD[1]-48)
       EEPROM_Write(10,LoD[2]-48)
       EEPROM_Write(11,LoM1[0]-48)
       EEPROM_Write(12,LoM1[1]-48)
       EEPROM_Write(13,LoM2[0]-48)
       EEPROM_Write(14,LoM2[1]-48)
       EEPROM_Write(15,LoM2[2]-48)
       EEPROM_Write(16,LoM2[3]-48)
       EEPROM_Write(17,LaDir)
       EEPROM_Write(18,LoDir)
       delay_ms(5000)
       LCD_Cmd(LCD_Clear)
     end if
 fine1:

'2. check if the user wants to change modes
     if PORTD.6=0 then ' if B.6 is pulled low
       GMode=Gmode+1  'cycle through modes
       if Gmode=5 then
         Gmode=1
       end if
       LCD_Cmd(LCD_Clear)
       do
       loop until PORTD.7=1 'button released
     end if
'and here are the disaply routines. First, if no valid data is available...
       if Gstatus="V" then 'no valid fix
       LCD_Out(1,1,"Searching...")
     else 'valid fix
       select case GMode
         case 1 'location display
           LCD_Chr(1,1," ")
           LCD_Out(1,2,LaD)
           LCD_Chr(1,4,"*")
           LCD_Out(1,5,LaM1)
           LCD_Chr(1,7,".")
           LCD_Out(1,8,LaM2)
           LCD_Chr(1,12,LaDir)
           LCD_Chr(1,14,"S")
           LCD_Out(1,15,Gsats)
           LCD_Out(2,1,LoD)
           LCD_chr(2,4,"*")
           LCD_Out(2,5,LoM1)
           LCD_Chr(2,7,".")
           LCD_Out(2,8,LoM2)
           LCD_Chr(2,12,LoDir)
         case 2 ' speed, direction, altitude, accuracy
           LCD_Out(1,1,"Spd:")
           LCD_Out(1,5,KSpeed)
           LCD_Out(1,10,"Crs:")
           LCD_Out(1,14,Crs)
           LCD_Out(2,1,"Alt:")
           LCD_Out(2,5,Galt)
           LCD_Out(2,10,"Acc:")
           LCD_Out(2,14,Gacc)
         case 3 'time, date
           LCD_Out(1,1,"Date:")
           LCD_Chr(1,7,GDate[2])
           LCD_Chr(1,8,GDate[3])
           LCD_Chr(1,9,"-")
           LCD_Chr(1,10,GDate[0])
           LCD_Chr(1,11,GDate[1])
           LCD_Chr(1,12,"-")
           LCD_Chr(1,13,GDate[4])
           LCD_Chr(1,14,GDate[5])
           LCD_Out(2,1,"Time:")
           LCD_Chr(2,7,GTime[0])
           LCD_Chr(2,8,GTime[1])
           LCD_Chr(2,9,":")
           LCD_Chr(2,10,GTime[2])
           LCD_Chr(2,11,GTime[3])
           LCD_Chr(2,12,":")
           LCD_Chr(2,13,GTime[4])
           LCD_Chr(2,14,GTime[5])
         case 4 'directions to saved location. This is more complicated
               'read target location from EEPROM and convert to numeric
goto fine4
               La2=EEPROM_Read(0)*10+EEPROM_Read(1) 'degrees
               tmp=(EEPROM_Read(2)*10+EEPROM_Read(3))/60 'minutes
               La2=La2+tmp
               tmp=(EEPROM_Read(4)*1000+EEPROM_Read(5)*100+EEPROM_Read(6)*10+EEPROM_Read(7))/600000 'dec minutes
               La2=La2+tmp

               Lo2=EEPROM_Read(8)*100+EEPROM_Read(9)*10+EEPROM_Read(10) 'degrees
               tmp=(EEPROM_Read(11)*10+EEPROM_Read(12))/60 'minutes
               Lo2=Lo2+tmp
               tmp=(EEPROM_Read(13)*1000+EEPROM_Read(14)*100+EEPROM_Read(15)*10+EEPROM_Read(16))/600000 'dec minutes
               Lo2=Lo2+tmp

               if EEPROM_Read(17)<>$4e then '"N"
                  La2=-La2
               end if
               if EEPROM_Read(18)<>$57 then '"W"
                  Lo2=-Lo2
               end if

               'calculate numeric value of current position
               La1=(LaD[0]-48)*10+LaD[1]-48
               tmp=((LaM1[0]-48)*10+LaM1[1]-48)/60
               La1=La1+tmp
               tmp=((LaM2[0]-48)*1000+(LaM2[1]-48)*100+(LaM2[2]-48)*10+(LaM2[3]-48))/600000
               La1=La1+tmp
               if LaDir="S" then
                  La1=-La1
               end if

               Lo1=(LoD[0]-48)*100+(LoD[1]-48)*10+LoD[2]-48
               tmp=((LoM1[0]-48)*10+(LoM1[1]-48))/60
               Lo1=Lo1+tmp
               tmp=((LoM2[0]-48)*1000+(LoM2[1]-48)*100+(LoM2[2]-48)*10+LoM2[3]-48)/600000
               Lo1=Lo1+tmp
               if LoDir="E" then
                  Lo1=-Lo1
               end if

               'here comes the trigonometry! Calculate direction and distance from
               'current location to target.
               'First, convert to radians
               La1=La1*pi/180
               Lo1=Lo1*pi/180
               La2=La2*pi/180
               Lo2=Lo2*pi/180
               'calculate distance using great circle formula
               Dist=2*Asin(sqrt(sq(sin((La1-La2)/2))+cos(La1)*cos(La2)*sq(Sin((Lo1-Lo2)/2))))
               'calculate direction
               Direct=acos((sin(La2)-sin(La1)*cos(Dist))/(sin(Dist)*cos(La1)))
               if sin(Lo2-Lo1)>0 then
                 Direct=2*pi-Direct
               end if
               'convert results
               Direct=(180/pi)*Direct 'convert to degrees
               Dist=3982*Dist ' convert to miles

               'calculate deviation
               '0=moving straight towards target. Neg values=moving too far to the
               'left. Pos values=moving too far to the right
               Dev=CDir-Direct
              if Dev>180 then
                  Dev=Dev-360
               end if
               if Dev<(-180) then
                   Dev=Dev+360
               end if

               'display
               FloatToStr(Dist,FltStr)
               LCD_Out(1,1,"Dist:")
               LCD_out(1,7,FltStr)
               IntToStr(Floor(Direct+0.5),IntStr)
               LCD_Out(2,1,"Dir:")
               LCD_Chr(2,5,IntStr[3])
               LCD_Chr(2,6,IntStr[4])
               LCD_Chr(2,7,IntStr[5])
               LCD_Out(2,9,"Dev:")
               IntToStr(Dev,IntStr)
               LCD_Chr(2,13,IntStr[2])
               LCD_Chr(2,14,IntStr[3])
               LCD_Chr(2,15,IntStr[4])
               LCD_Chr(2,16,IntStr[5])
fine4:
         end select
       end if
       WriteFlag=0
   end if
wend
pippo:
end.

I thing that LCD module has (again) some problems on working with large programs. mE released a little patch for P16 line mcu, so can you please check the code that Durieux gave us to see what can be the problem?

Thanks :-)

Luca

Durieux wrote:Hi -

For those of you interested in GPS stuff... Attached code for a GPS interpreter that reads and parses NMEA messages coming in to USART, and writes them to LCD. In addition, it will allow you to store your present location to EEPROM and from then on calculate instructions on how to get back there.

This version was compiled on MB 4.02. It runs on a P18F452.

Marcel

Code: Select all

'GPS interpreter

'Marcel Durieux, 2006

'Reads in NMEA strings from any GPS receiver unit (over USART), and outputs (on LCD)
'location, speed, course, altitude, accuracy and number of satellites in view, as well as
'universal date and time.
'In addition, it allows saving a current location (in NV memory) and will calculate distance,
'direction and deviation from the desired direction. These calculations are performed
'using great-circle formulae.

'Not implemented, but easy to add, is serial output of the results to some other device.

'The program interprets two of the many NMEA strings, RMC and GGA. Since there
'are slight differences in NMEA versions (# of digits, etc), here are examples
'of valid strings for this program.

'$GPRMC,214200.875,A,3806.5677,N,07826.1267,W,000.0,000.0,120206,009.7,W*69
'$GPGGA,214201.875,3806.5677,N,07826.1267,W,1,05,01.8,00124.4,M,-34.3,M,,*51

'A good overview of the various NMEA output is available at http://aprs.gids.nl/nmea/

'The program runs on a P18F452 clocked at 20 MHz.

program GPS

'data variables
dim Gstr as string[65] 'to hold NMEA sentence, subsequently to be dissected into:
dim LaD as string[2] 'Latitude degrees
dim LaM1 as string[2] 'Latitude whole minutes
dim LaM2 as string[4] 'Latitude fractional minutes
dim LoD as string[3] 'Longitude degrees
dim LoM1 as string[2] 'Longitude whole minutes
dim LoM2 as string[4] 'Longitude fractional minutes
dim LoDir as char 'Longitude direction (E or W)
dim LaDir as char 'Latitude direction (N or S)
dim Kspeed as string[3] 'Speed in knots (as read from device)
dim Crs as string[3] 'Course
dim Galt as string[4] 'Altitude
dim Gacc as string[3] 'Accuracy
dim Gstatus as char 'Status: valid (A) or invalid (V) fix
dim Gsats as string[2] 'number of satellites in view
dim GDate as string[6] 'date
dim GTime as string[6] 'time
'calculated values
dim Mspeed as string[3] 'Calculated speed in mph
dim Cdir as integer ' store numeric version of Crs
dim Dev as integer 'deviation from desired course
dim La1,La2,Lo1,Lo2 as float 'numeric values of Latitude, Longitude
dim Direct,Dist as float 'calculated direction and distance to target

'utility variables
dim i as byte
dim tmp as float
dim GMode as byte 'to hold display mode. The program cycles through 4 display
'modes by briefly pulling either B.7 or B.6 low:
'1. coordinates and number of satellites in view
'2. speed, course, altitude and accuracy
'3. date and time
'4. distance, direction and deviation to target location
dim fnc as byte 'to cycle through NMEA sentences: 1=RMC, 2=GGA
dim WriteFlag as byte 'determines if display needs updating
dim FltStr as string[17] 'to display floats on LCD
dim IntStr as string[6]  'to display integers on LCD

'subroutines:

sub function sq(dim a as real) as real 'calculates square
  Result=a*a
end sub

sub function ReadChr as byte 'reads one char from USART port
    do
    loop until USART_Data_Ready = 1
    result=USART_Read 'read char
end sub

sub procedure CopyGStr(dim start as byte, dim l as byte, dim byref txt as string[10])
'extracts 'l' chars from 'Gstr' (which holds the NMEA sentence) starting at 'start'
	dim i as byte

  for i=0 to l
    txt[i]=Gstr[start+i]
  next i
  txt[l]=chr(0) 'to terminate the string
end sub

main:

'device setup
TRISB=$FF 'two buttons will be connected to B.7 and B.6. Pulling B.6 low
'cycles through the 4 modes (see above), pulling both low saves current location
PORTB=0
TRISD=0 'for 2x16 CLD char unit
USART_Init(4800) 'init USART at 4800 baud to interface with NMEA device
LCD_Init(PORTD) 'init LCD on port D
LCD_Cmd(LCD_Cursor_Off)
LCD_Cmd(LCD_Clear)

'initialize variables
Gstatus="V" 'assume invalid fix at startup
fnc = 1 'get ready to look for first NMEA string
WriteFlag=0 'no data to display yet
GMode=1 'location display
Gsats="00" 'no satellites in view

while true 'main program loop
'monitor NMEA data until a '$' is received, signaling start of a sentence
  do
    i=ReadChr
  loop until i=36 'loop until "$" received
'read sentence (from '$' through '*') into the variable Gstr
	i=0
  do
    Gstr[i]=ReadChr
    if Gstr[i]="*" then
      i=0
    else
      i=i+1
    end if
  loop until (i=0) or (i=65) 'break at 65 chars if for some reason no '*' is found
'now look for RMC or GGA, depending on 'fnc' value.
	if fnc=1 then 'look for RMC
    if ((Gstr[2]="R") and (Gstr[3]="M") and (Gstr[4]="C")) then
      fnc=2 'next read will read GGA
      WriteFlag=1 'get ready to display data
      i=Gstatus
      Gstatus=Gstr[17] 'get status char
      if Gstatus<>i then 'i.e. if status has changed
        LCD_Cmd(LCD_Clear)
      end if
      if Gstatus="A" then ' valid fix, copy the variable from Gstr
        CopyGStr(6,6,GTime)
        CopyGStr(56,6,GDate)
        CopyGStr(19,2,LaD) 'get latitude
        CopyGStr(21,2,LaM1)
        CopyGStr(24,4,LaM2)
        LaDir=GStr[29] 'get latitude direction
        CopyGStr(31,3,LoD) 'get longitude
        CopyGstr(34,2,LoM1)
        CopyGstr(37,4,LoM2)
        LoDir=GStr[42] 'get longitude direction
        CopyGStr(44,3,Kspeed) 'get speed and convert to mph
        i=(Kspeed[0]*100+Kspeed[1]*10+Kspeed[2])*1.15 'first numeric
      	ByteToStr(i,Mspeed) 'then back to string for display
				CopyGStr(50,3,Crs) 'get course
				Cdir=(Crs[0]-48)*100+(Crs[1]-48)*10+Crs[2]-48 'convert to number
      end if
    end if
  end if
  if fnc=2 then 'look for GGA
    if ((Gstr[2]="G") and (Gstr[3]="G") and (Gstr[4]="A")) then
      fnc=1 'next time round, look for RMC
      WriteFlag=1 'update display
      CopyGStr(44,2,Gsats) 'get #sats
      if Gstatus="A" then
        CopyGStr(48,3,Gacc) 'get accuracy
        CopyGstr(53,4,Galt) 'get altitude
      end if
    end if
  end if

'if we have valida data (i.e. WriteFlag=1) then...
    if WriteFlag=1 then
'1. check if the user wants this location stored in EEPROM
     if (PORTB.6=0) and (PORTB.7=0) then 'if BOTH buttons pressed
       LCD_Cmd(LCD_Clear)
       LCD_Out(1,1,"Location saved") 'store current location in EEPROM
'subtracting 48 is to get from ASCII to numeric value
       EEPROM_Write(0,LaD[0]-48)
       EEPROM_Write(1,LaD[1]-48)
       EEPROM_Write(2,LaM1[0]-48)
       EEPROM_Write(3,LaM1[1]-48)
       EEPROM_Write(4,LaM2[0]-48)
       EEPROM_Write(5,LaM2[1]-48)
       EEPROM_Write(6,LaM2[2]-48)
       EEPROM_Write(7,LaM2[3]-48)
       EEPROM_Write(8,LoD[0]-48)
       EEPROM_Write(9,LoD[1]-48)
       EEPROM_Write(10,LoD[2]-48)
       EEPROM_Write(11,LoM1[0]-48)
       EEPROM_Write(12,LoM1[1]-48)
       EEPROM_Write(13,LoM2[0]-48)
       EEPROM_Write(14,LoM2[1]-48)
       EEPROM_Write(15,LoM2[2]-48)
       EEPROM_Write(16,LoM2[3]-48)
       EEPROM_Write(17,LaDir)
       EEPROM_Write(18,LoDir)
       delay_ms(5000)
       LCD_Cmd(LCD_Clear)
     end if
'2. check if the user wants to change modes
     if PORTB.6=0 then ' if B.6 is pulled low
       GMode=Gmode+1  'cycle through modes
       if Gmode=5 then
         Gmode=1
       end if
       LCD_Cmd(LCD_Clear)
       do
       loop until PORTB.7=1 'button released
     end if
'and here are the disaply routines. First, if no valid data is available...
		 if Gstatus="V" then 'no valid fix
       LCD_Out(1,1,"Searching...")
     else 'valid fix
       select case GMode
         case 1 'location display
           LCD_Chr(1,1," ")
           LCD_Out(1,2,LaD)
           LCD_Chr(1,4,"*")
           LCD_Out(1,5,LaM1)
           LCD_Chr(1,7,".")
           LCD_Out(1,8,LaM2)
           LCD_Chr(1,12,LaDir)
           LCD_Chr(1,14,"S")
           LCD_Out(1,15,Gsats)
           LCD_Out(2,1,LoD)
           LCD_chr(2,4,"*")
           LCD_Out(2,5,LoM1)
           LCD_Chr(2,7,".")
           LCD_Out(2,8,LoM2)
           LCD_Chr(2,12,LoDir)
         case 2 ' speed, direction, altitude, accuracy
           LCD_Out(1,1,"Spd:")
           LCD_Out(1,5,KSpeed)
           LCD_Out(1,10,"Crs:")
           LCD_Out(1,14,Crs)
           LCD_Out(2,1,"Alt:")
           LCD_Out(2,5,Galt)
           LCD_Out(2,10,"Acc:")
           LCD_Out(2,14,Gacc)
         case 3 'time, date
           LCD_Out(1,1,"Date:")
           LCD_Chr(1,7,GDate[2])
           LCD_Chr(1,8,GDate[3])
           LCD_Chr(1,9,"-")
           LCD_Chr(1,10,GDate[0])
           LCD_Chr(1,11,GDate[1])
           LCD_Chr(1,12,"-")
           LCD_Chr(1,13,GDate[4])
           LCD_Chr(1,14,GDate[5])
           LCD_Out(2,1,"Time:")
           LCD_Chr(2,7,GTime[0])
           LCD_Chr(2,8,GTime[1])
           LCD_Chr(2,9,":")
           LCD_Chr(2,10,GTime[2])
           LCD_Chr(2,11,GTime[3])
           LCD_Chr(2,12,":")
           LCD_Chr(2,13,GTime[4])
           LCD_Chr(2,14,GTime[5])
         case 4 'directions to saved location. This is more complicated
					'read target location from EEPROM and convert to numeric
					La2=EEPROM_Read(0)*10+EEPROM_Read(1) 'degrees
					tmp=(EEPROM_Read(2)*10+EEPROM_Read(3))/60 'minutes
					La2=La2+tmp
					tmp=(EEPROM_Read(4)*1000+EEPROM_Read(5)*100+EEPROM_Read(6)*10+EEPROM_Read(7))/600000 'dec minutes
					La2=La2+tmp

					Lo2=EEPROM_Read(8)*100+EEPROM_Read(9)*10+EEPROM_Read(10) 'degrees
					tmp=(EEPROM_Read(11)*10+EEPROM_Read(12))/60 'minutes
					Lo2=Lo2+tmp
					tmp=(EEPROM_Read(13)*1000+EEPROM_Read(14)*100+EEPROM_Read(15)*10+EEPROM_Read(16))/600000 'dec minutes
					Lo2=Lo2+tmp
					
					if EEPROM_Read(17)<>$4e then '"N"
						La2=-La2
					end if
					if EEPROM_Read(18)<>$57 then '"W"
						Lo2=-Lo2
					end if

					'calculate numeric value of current position
					La1=(LaD[0]-48)*10+LaD[1]-48
					tmp=((LaM1[0]-48)*10+LaM1[1]-48)/60
					La1=La1+tmp
					tmp=((LaM2[0]-48)*1000+(LaM2[1]-48)*100+(LaM2[2]-48)*10+(LaM2[3]-48))/600000
					La1=La1+tmp
					if LaDir="S" then
						La1=-La1
					end if
					
					Lo1=(LoD[0]-48)*100+(LoD[1]-48)*10+LoD[2]-48
					tmp=((LoM1[0]-48)*10+(LoM1[1]-48))/60
					Lo1=Lo1+tmp
					tmp=((LoM2[0]-48)*1000+(LoM2[1]-48)*100+(LoM2[2]-48)*10+LoM2[3]-48)/600000
					Lo1=Lo1+tmp
					if LoDir="E" then
						Lo1=-Lo1
					end if
					
					'here comes the trigonometry! Calculate direction and distance from
					'current location to target.
					'First, convert to radians
					La1=La1*pi/180
					Lo1=Lo1*pi/180
					La2=La2*pi/180
					Lo2=Lo2*pi/180
					'calculate distance using great circle formula
					Dist=2*Asin(sqrt(sq(sin((La1-La2)/2))+cos(La1)*cos(La2)*sq(Sin((Lo1-Lo2)/2))))
					'calculate direction
					Direct=acos((sin(La2)-sin(La1)*cos(Dist))/(sin(Dist)*cos(La1)))
					if sin(Lo2-Lo1)>0 then
					  Direct=2*pi-Direct
          end if
					'convert results
					Direct=(180/pi)*Direct 'convert to degrees
					Dist=3982*Dist ' convert to miles

					'calculate deviation
					'0=moving straight towards target. Neg values=moving too far to the
					'left. Pos values=moving too far to the right
					Dev=CDir-Direct
     			if Dev>180 then
					   Dev=Dev-360
					end if
					if Dev<(-180) then
						 Dev=Dev+360
					end if
					
					'display
					FloatToStr(Dist,FltStr)
     			LCD_Out(1,1,"Dist:")
					LCD_out(1,7,FltStr)
					IntToStr(Floor(Direct+0.5),IntStr)
					LCD_Out(2,1,"Dir:")
					LCD_Chr(2,5,IntStr[3])
					LCD_Chr(2,6,IntStr[4])
					LCD_Chr(2,7,IntStr[5])
     			LCD_Out(2,9,"Dev:")
          IntToStr(Dev,IntStr)
					LCD_Chr(2,13,IntStr[2])
					LCD_Chr(2,14,IntStr[3])
					LCD_Chr(2,15,IntStr[4])
					LCD_Chr(2,16,IntStr[5])

         end select
       end if
       WriteFlag=0
   end if
wend

end.

kkjensen
Posts: 50
Joined: 30 Mar 2006 18:16

Slower clock speed?

#11 Post by kkjensen » 13 Jun 2006 13:48

Hi there,

I'm ordering some samples from Microchip (sample.microchip.com) and am looking at the 18F4520 which is a bit better than the 19F452. It has an 8Mhz internal oscillator which frees up two more pins and reduces the component count on the board.

Has anyone tested to see if this program works properly at 8MHz? I know the MB compiler "project properties" has the clock speed but I'm still not good enough with PIC processors to know the impact of changing clock speed.

Kris

Thomas K
Posts: 278
Joined: 17 Apr 2008 16:31
Location: Holstebro, Denmark
Contact:

Re: GPS interpreter

#12 Post by Thomas K » 19 May 2008 19:37

Durieux wrote:Hi -

For those of you interested in GPS stuff... Attached code for a GPS interpreter that reads and parses NMEA messages coming in to USART, and writes them to LCD. In addition, it will allow you to store your present location to EEPROM and from then on calculate instructions on how to get back there.

This version was compiled on MB 4.02. It runs on a P18F452.

Marcel

Code: Select all

'GPS interpreter

'Marcel Durieux, 2006

'Reads in NMEA strings from any GPS receiver unit (over USART), and outputs (on LCD)
'location, speed, course, altitude, accuracy and number of satellites in view, as well as
'universal date and time.
'In addition, it allows saving a current location (in NV memory) and will calculate distance,
'direction and deviation from the desired direction. These calculations are performed
'using great-circle formulae.

'Not implemented, but easy to add, is serial output of the results to some other device.

'The program interprets two of the many NMEA strings, RMC and GGA. Since there
'are slight differences in NMEA versions (# of digits, etc), here are examples
'of valid strings for this program.

'$GPRMC,214200.875,A,3806.5677,N,07826.1267,W,000.0,000.0,120206,009.7,W*69
'$GPGGA,214201.875,3806.5677,N,07826.1267,W,1,05,01.8,00124.4,M,-34.3,M,,*51

'A good overview of the various NMEA output is available at http://aprs.gids.nl/nmea/

'The program runs on a P18F452 clocked at 20 MHz.

program GPS

'data variables
dim Gstr as string[65] 'to hold NMEA sentence, subsequently to be dissected into:
dim LaD as string[2] 'Latitude degrees
dim LaM1 as string[2] 'Latitude whole minutes
dim LaM2 as string[4] 'Latitude fractional minutes
dim LoD as string[3] 'Longitude degrees
dim LoM1 as string[2] 'Longitude whole minutes
dim LoM2 as string[4] 'Longitude fractional minutes
dim LoDir as char 'Longitude direction (E or W)
dim LaDir as char 'Latitude direction (N or S)
dim Kspeed as string[3] 'Speed in knots (as read from device)
dim Crs as string[3] 'Course
dim Galt as string[4] 'Altitude
dim Gacc as string[3] 'Accuracy
dim Gstatus as char 'Status: valid (A) or invalid (V) fix
dim Gsats as string[2] 'number of satellites in view
dim GDate as string[6] 'date
dim GTime as string[6] 'time
'calculated values
dim Mspeed as string[3] 'Calculated speed in mph
dim Cdir as integer ' store numeric version of Crs
dim Dev as integer 'deviation from desired course
dim La1,La2,Lo1,Lo2 as float 'numeric values of Latitude, Longitude
dim Direct,Dist as float 'calculated direction and distance to target

'utility variables
dim i as byte
dim tmp as float
dim GMode as byte 'to hold display mode. The program cycles through 4 display
'modes by briefly pulling either B.7 or B.6 low:
'1. coordinates and number of satellites in view
'2. speed, course, altitude and accuracy
'3. date and time
'4. distance, direction and deviation to target location
dim fnc as byte 'to cycle through NMEA sentences: 1=RMC, 2=GGA
dim WriteFlag as byte 'determines if display needs updating
dim FltStr as string[17] 'to display floats on LCD
dim IntStr as string[6]  'to display integers on LCD

'subroutines:

sub function sq(dim a as real) as real 'calculates square
  Result=a*a
end sub

sub function ReadChr as byte 'reads one char from USART port
    do
    loop until USART_Data_Ready = 1
    result=USART_Read 'read char
end sub

sub procedure CopyGStr(dim start as byte, dim l as byte, dim byref txt as string[10])
'extracts 'l' chars from 'Gstr' (which holds the NMEA sentence) starting at 'start'
	dim i as byte

  for i=0 to l
    txt[i]=Gstr[start+i]
  next i
  txt[l]=chr(0) 'to terminate the string
end sub

main:

'device setup
TRISB=$FF 'two buttons will be connected to B.7 and B.6. Pulling B.6 low
'cycles through the 4 modes (see above), pulling both low saves current location
PORTB=0
TRISD=0 'for 2x16 CLD char unit
USART_Init(4800) 'init USART at 4800 baud to interface with NMEA device
LCD_Init(PORTD) 'init LCD on port D
LCD_Cmd(LCD_Cursor_Off)
LCD_Cmd(LCD_Clear)

'initialize variables
Gstatus="V" 'assume invalid fix at startup
fnc = 1 'get ready to look for first NMEA string
WriteFlag=0 'no data to display yet
GMode=1 'location display
Gsats="00" 'no satellites in view

while true 'main program loop
'monitor NMEA data until a '$' is received, signaling start of a sentence
  do
    i=ReadChr
  loop until i=36 'loop until "$" received
'read sentence (from '$' through '*') into the variable Gstr
	i=0
  do
    Gstr[i]=ReadChr
    if Gstr[i]="*" then
      i=0
    else
      i=i+1
    end if
  loop until (i=0) or (i=65) 'break at 65 chars if for some reason no '*' is found
'now look for RMC or GGA, depending on 'fnc' value.
	if fnc=1 then 'look for RMC
    if ((Gstr[2]="R") and (Gstr[3]="M") and (Gstr[4]="C")) then
      fnc=2 'next read will read GGA
      WriteFlag=1 'get ready to display data
      i=Gstatus
      Gstatus=Gstr[17] 'get status char
      if Gstatus<>i then 'i.e. if status has changed
        LCD_Cmd(LCD_Clear)
      end if
      if Gstatus="A" then ' valid fix, copy the variable from Gstr
        CopyGStr(6,6,GTime)
        CopyGStr(56,6,GDate)
        CopyGStr(19,2,LaD) 'get latitude
        CopyGStr(21,2,LaM1)
        CopyGStr(24,4,LaM2)
        LaDir=GStr[29] 'get latitude direction
        CopyGStr(31,3,LoD) 'get longitude
        CopyGstr(34,2,LoM1)
        CopyGstr(37,4,LoM2)
        LoDir=GStr[42] 'get longitude direction
        CopyGStr(44,3,Kspeed) 'get speed and convert to mph
        i=(Kspeed[0]*100+Kspeed[1]*10+Kspeed[2])*1.15 'first numeric
      	ByteToStr(i,Mspeed) 'then back to string for display
				CopyGStr(50,3,Crs) 'get course
				Cdir=(Crs[0]-48)*100+(Crs[1]-48)*10+Crs[2]-48 'convert to number
      end if
    end if
  end if
  if fnc=2 then 'look for GGA
    if ((Gstr[2]="G") and (Gstr[3]="G") and (Gstr[4]="A")) then
      fnc=1 'next time round, look for RMC
      WriteFlag=1 'update display
      CopyGStr(44,2,Gsats) 'get #sats
      if Gstatus="A" then
        CopyGStr(48,3,Gacc) 'get accuracy
        CopyGstr(53,4,Galt) 'get altitude
      end if
    end if
  end if

'if we have valida data (i.e. WriteFlag=1) then...
    if WriteFlag=1 then
'1. check if the user wants this location stored in EEPROM
     if (PORTB.6=0) and (PORTB.7=0) then 'if BOTH buttons pressed
       LCD_Cmd(LCD_Clear)
       LCD_Out(1,1,"Location saved") 'store current location in EEPROM
'subtracting 48 is to get from ASCII to numeric value
       EEPROM_Write(0,LaD[0]-48)
       EEPROM_Write(1,LaD[1]-48)
       EEPROM_Write(2,LaM1[0]-48)
       EEPROM_Write(3,LaM1[1]-48)
       EEPROM_Write(4,LaM2[0]-48)
       EEPROM_Write(5,LaM2[1]-48)
       EEPROM_Write(6,LaM2[2]-48)
       EEPROM_Write(7,LaM2[3]-48)
       EEPROM_Write(8,LoD[0]-48)
       EEPROM_Write(9,LoD[1]-48)
       EEPROM_Write(10,LoD[2]-48)
       EEPROM_Write(11,LoM1[0]-48)
       EEPROM_Write(12,LoM1[1]-48)
       EEPROM_Write(13,LoM2[0]-48)
       EEPROM_Write(14,LoM2[1]-48)
       EEPROM_Write(15,LoM2[2]-48)
       EEPROM_Write(16,LoM2[3]-48)
       EEPROM_Write(17,LaDir)
       EEPROM_Write(18,LoDir)
       delay_ms(5000)
       LCD_Cmd(LCD_Clear)
     end if
'2. check if the user wants to change modes
     if PORTB.6=0 then ' if B.6 is pulled low
       GMode=Gmode+1  'cycle through modes
       if Gmode=5 then
         Gmode=1
       end if
       LCD_Cmd(LCD_Clear)
       do
       loop until PORTB.7=1 'button released
     end if
'and here are the disaply routines. First, if no valid data is available...
		 if Gstatus="V" then 'no valid fix
       LCD_Out(1,1,"Searching...")
     else 'valid fix
       select case GMode
         case 1 'location display
           LCD_Chr(1,1," ")
           LCD_Out(1,2,LaD)
           LCD_Chr(1,4,"*")
           LCD_Out(1,5,LaM1)
           LCD_Chr(1,7,".")
           LCD_Out(1,8,LaM2)
           LCD_Chr(1,12,LaDir)
           LCD_Chr(1,14,"S")
           LCD_Out(1,15,Gsats)
           LCD_Out(2,1,LoD)
           LCD_chr(2,4,"*")
           LCD_Out(2,5,LoM1)
           LCD_Chr(2,7,".")
           LCD_Out(2,8,LoM2)
           LCD_Chr(2,12,LoDir)
         case 2 ' speed, direction, altitude, accuracy
           LCD_Out(1,1,"Spd:")
           LCD_Out(1,5,KSpeed)
           LCD_Out(1,10,"Crs:")
           LCD_Out(1,14,Crs)
           LCD_Out(2,1,"Alt:")
           LCD_Out(2,5,Galt)
           LCD_Out(2,10,"Acc:")
           LCD_Out(2,14,Gacc)
         case 3 'time, date
           LCD_Out(1,1,"Date:")
           LCD_Chr(1,7,GDate[2])
           LCD_Chr(1,8,GDate[3])
           LCD_Chr(1,9,"-")
           LCD_Chr(1,10,GDate[0])
           LCD_Chr(1,11,GDate[1])
           LCD_Chr(1,12,"-")
           LCD_Chr(1,13,GDate[4])
           LCD_Chr(1,14,GDate[5])
           LCD_Out(2,1,"Time:")
           LCD_Chr(2,7,GTime[0])
           LCD_Chr(2,8,GTime[1])
           LCD_Chr(2,9,":")
           LCD_Chr(2,10,GTime[2])
           LCD_Chr(2,11,GTime[3])
           LCD_Chr(2,12,":")
           LCD_Chr(2,13,GTime[4])
           LCD_Chr(2,14,GTime[5])
         case 4 'directions to saved location. This is more complicated
					'read target location from EEPROM and convert to numeric
					La2=EEPROM_Read(0)*10+EEPROM_Read(1) 'degrees
					tmp=(EEPROM_Read(2)*10+EEPROM_Read(3))/60 'minutes
					La2=La2+tmp
					tmp=(EEPROM_Read(4)*1000+EEPROM_Read(5)*100+EEPROM_Read(6)*10+EEPROM_Read(7))/600000 'dec minutes
					La2=La2+tmp

					Lo2=EEPROM_Read(8)*100+EEPROM_Read(9)*10+EEPROM_Read(10) 'degrees
					tmp=(EEPROM_Read(11)*10+EEPROM_Read(12))/60 'minutes
					Lo2=Lo2+tmp
					tmp=(EEPROM_Read(13)*1000+EEPROM_Read(14)*100+EEPROM_Read(15)*10+EEPROM_Read(16))/600000 'dec minutes
					Lo2=Lo2+tmp
					
					if EEPROM_Read(17)<>$4e then '"N"
						La2=-La2
					end if
					if EEPROM_Read(18)<>$57 then '"W"
						Lo2=-Lo2
					end if

					'calculate numeric value of current position
					La1=(LaD[0]-48)*10+LaD[1]-48
					tmp=((LaM1[0]-48)*10+LaM1[1]-48)/60
					La1=La1+tmp
					tmp=((LaM2[0]-48)*1000+(LaM2[1]-48)*100+(LaM2[2]-48)*10+(LaM2[3]-48))/600000
					La1=La1+tmp
					if LaDir="S" then
						La1=-La1
					end if
					
					Lo1=(LoD[0]-48)*100+(LoD[1]-48)*10+LoD[2]-48
					tmp=((LoM1[0]-48)*10+(LoM1[1]-48))/60
					Lo1=Lo1+tmp
					tmp=((LoM2[0]-48)*1000+(LoM2[1]-48)*100+(LoM2[2]-48)*10+LoM2[3]-48)/600000
					Lo1=Lo1+tmp
					if LoDir="E" then
						Lo1=-Lo1
					end if
					
					'here comes the trigonometry! Calculate direction and distance from
					'current location to target.
					'First, convert to radians
					La1=La1*pi/180
					Lo1=Lo1*pi/180
					La2=La2*pi/180
					Lo2=Lo2*pi/180
					'calculate distance using great circle formula
					Dist=2*Asin(sqrt(sq(sin((La1-La2)/2))+cos(La1)*cos(La2)*sq(Sin((Lo1-Lo2)/2))))
					'calculate direction
					Direct=acos((sin(La2)-sin(La1)*cos(Dist))/(sin(Dist)*cos(La1)))
					if sin(Lo2-Lo1)>0 then
					  Direct=2*pi-Direct
          end if
					'convert results
					Direct=(180/pi)*Direct 'convert to degrees
					Dist=3982*Dist ' convert to miles

					'calculate deviation
					'0=moving straight towards target. Neg values=moving too far to the
					'left. Pos values=moving too far to the right
					Dev=CDir-Direct
     			if Dev>180 then
					   Dev=Dev-360
					end if
					if Dev<(-180) then
						 Dev=Dev+360
					end if
					
					'display
					FloatToStr(Dist,FltStr)
     			LCD_Out(1,1,"Dist:")
					LCD_out(1,7,FltStr)
					IntToStr(Floor(Direct+0.5),IntStr)
					LCD_Out(2,1,"Dir:")
					LCD_Chr(2,5,IntStr[3])
					LCD_Chr(2,6,IntStr[4])
					LCD_Chr(2,7,IntStr[5])
     			LCD_Out(2,9,"Dev:")
          IntToStr(Dev,IntStr)
					LCD_Chr(2,13,IntStr[2])
					LCD_Chr(2,14,IntStr[3])
					LCD_Chr(2,15,IntStr[4])
					LCD_Chr(2,16,IntStr[5])

         end select
       end if
       WriteFlag=0
   end if
wend

end.
I will try using this code on my EasyPic5 Board, but as haven't got my GPS, i can't test it yet!
But compiling went fine, i would use a PIC18F4550 as a PIC16F877A don't have enough memory..
But just a question, can't i just change the clock from 20MHz to 10MHz, it wouldn't doubt, would it?

Copy'nPaste
Posts: 573
Joined: 25 Apr 2006 15:39
Location: Cape Town, South Africa

#13 Post by Copy'nPaste » 20 May 2008 07:32

Durieux, Thanks for sharing your code code :D
"Copy'nPaste"

PortlandJohn
Posts: 251
Joined: 20 Jan 2006 11:17
Location: Portland UK

#14 Post by PortlandJohn » 22 May 2008 23:25

Hi
We have developed a GPRS/GSM/GPS modem board
its controlled by an 18F8722
If any one is interested.

John
Never underestimate the value of a reply.

gem1144aaa
Posts: 7
Joined: 12 Aug 2006 12:04

#15 Post by gem1144aaa » 28 Jul 2008 08:48

PortlandJohn wrote:Hi
We have developed a GPRS/GSM/GPS modem board
its controlled by an 18F8722
If any one is interested.

John

Hi
can you send the specs with pictures if any for your product?


best regards

gem

Post Reply

Return to “mikroBasic General”