GPS Receiver Project Notes

From Stu2
Jump to: navigation, search

Here are a few notes on the Novatel Superstar II GPS receiver module. I bought several of them from E-bay so I could experiment with some precise time projects. (NTP server and 10MHz reference source) My plan is to use the 1PPS signal as a sync pulse. This worked well in 2001 for an internal network time reference. When we (DOI)were cut off from the Internet, we discovered all of our routers used external stratum 1 clocks. Using a linux kernel mod and a Garmin GPS-35, I built a couple clocks, which worked great.

I think it might be a lot of fun to link the GPS with a PIC and a wireless device. I could store the position data (last couple of hours) and transmit it when my vehicle arrives home. Of course, it would be cool to have the data available while driving. Maybe for war-driving, APRS or simply to display.... it goes on and on.

For now - I'll settle on a simple position display using a PIC 16F628 and HD44780 4 line X 20 Character LCD display.



Here is a project built by Harold Peters, which was based on some of the work below. Harold and I corresponded several times during the project, which was a lot of fun for both of us! You can download a zip file with his code here. 16F873AGPS You can also see the finished project here.

--Stu2 10:19, 11 December 2010 (EST)

Getting Started

I bought 5 GPS receivers from e-bay for $30. See the e-bay text below. The model number is: 169-614471-005 which is the 3.3 volt version. So used a LM-317 voltage regulator to provide 3.3 volts.

Used a Max 3223 (free sample from maxim!) to convert the TTL levels to RS-232.The MAX3223 requires for external 0.1uF caps. (Non-polarized is OK.)

The motorola antenna came from TAPR. ($12/ea) The GPS receivers came with a pig tail to provide a bulk-head SMA connection, which matched the connector provided on the antenna.

Max current draw 40mA (from manual)

GPS Interface - Schematic of the inteface circuits. The GPS connector is not .1" centers. .065" probably. I need to dig up a connector so I don't have to solder directly to the header. See the notes below about the GPS header pins. Pin 2 (preamp) should be connected to pin 1 (Vcc.) After the experiment ran its course, I think Pin 7 should NOT be connected to GND. This may be contrary to some of the text below.

--Stu 19:56, 3 April 2007 (EDT)

Added a PIC and got the serial interface to work. The pic reads in the data and displays the LAT/LON on the LCD. Added a 7406 buffer to the GPS output, fed that into the 16F628. Data line needed buffering, but not inverted. Used the PICEL as a the development platform and mounted the PIC in a socket so I could reroute the IO lines of the pic to match the wiring of the PIC-EL. Very cool.

First Light

Using gtkterm on /dev/ttyS0, 9600 baud, n, 8, 1

Power up:

169-614330-002  �
UCPB: 0x955014A5
PCPB: 0x000035C0

<BAE Systems Canada,Part Nb:169-614471-005, CB=0x3F SHP
Go to Binary @ 9600 baud. NVM off
In Binary @ 9600 baud.

Goes into binary mode....

From page 25 in the manual, connect pin 7 (DISC_IP_2, protocol select) to GND -> selects NEMA, 4800 baud. If you connect pin 8 (DISC_IP_3, NVM) to ground, you disable the NVM. (You don't want this.) So now, the receiver boots up at 9600 baud, plays the boot message and switches to 4800, NEMA. Obviously, to see the boot message, gtkterm has to be set at 9600 baud, then reconfigure to 4800 to see the messages.

First light in NEMA mode. Note the binary characters. gtkterm was set at 4800 baud:

�����j:[[JI�    M       �
�zKj|   �
�������         �       '<8���(���d���������$��*(x���}
In NMEA @ 4800 baud.

Software is available! This was a nice find. On the Novatel page (see link below) I found the Viewstar program, which communicates with the GPS module. After first light (i.e. 4800/NEMA) you might as well switch to 9600/Binary becaues the software has many more options and is a little more 'automatic.' However - after several hours playing with the software, I'm stuck. I'm not able to get any postioning status from the module.So I don't know if I have a bad module or what. Time for a beer.

--Stu 20:52, 20 January 2007 (EST)

Eureka! Power up the active antenna.

--Stu 08:27, 21 January 2007 (EST)


Just found out it's an 'active antenna,' but wouldn't you know, it needs 5V. Guess I need another regulator. Pin 1 on the GPS module is labled Preamp. I think I provide power to pin 1 to light up the active antenan. My antenna specs are 20 mA @ 5V and the gain is 28db.

From the troubleshooting section of the manual (page 42): "if using an active antenna, verify antenna is receiving correct power from RF connector; preamp input into SUPERSTAR II is feeding a DC bias on the center conductor." So looks like I apply power to that pin.

The input at the receiver is supposed to be -3 to 8 db.So I need 45 feet of RG-174 to attenuate the signal? akkk. something doesn't sound right.Maybe I need a passive antenna for testing. Eventually, I will need an active antenna for deployment.

Using a calculator I found on the web:

45 feet RG-174 @ 1500MHz -> 19.7db loss
20 feet RG-174 @ 1500MHz -> 8.7db loss

A good night's sleep does wonders for the guile. This morning I simply connected the preamp pin (pin2) to the 3.3v line (pin 1) and it works! The active antenna is no longer a dummy load. Within a minute or so (didn't time it,) I was tracking 3 satellites. Some of the SNR values were very high, like 50db. So I expect this should work fine over 20 feet of RG-174. Now, I can separate the antenna from the module and not worry about signal strength.

Update - added a 7805 regulator to the interface circuit to provide +5VDC to the antenna preamp. It worked with 3.3, but the specs say 5 and I plan to use a longer piece of coax. Plus, I need to power the 16f628 PIC. --Stu 10:54, 28 April 2007 (EDT)

Software Notes (Viewstar)

Viewstar is available on the Novatel web site. (see the reference link) It's best to put the receiver into binary mode because there are more choices available and you aren't limited to the NEMA strings. When using NEMA, the trick is to open the output window first, then transmit the message to the GPS module. The software doesn't buffer the reply. So if you do it the other way around, you won't see the results.

You can switch protocols by selecting Tools/Protocol - then force to Binary/9600 baud. Check out the 'Messages' received in the lower right hand corner. It should blink a few times and then start counting.

Here are a couple screen shots after I powered up the active antenna.

Channels.png - Shows the channel status

Navigation.png - Navigation window

Status.png - Receiver status

Plot.png - Postion plot over time. (I can't wait to put this on a known point in Corbin!)

I suppose this experiment says any interfacing with the modules should probably be done in binary. You have more options and it's probably more effecient, anyway. (Next on the list of things to do - get a PIC talking to the module.)

Well- I ignored this and used the NEMA output because it was simpler to parse in the PIC. --Stu 10:56, 28 April 2007 (EDT)

Using a 16F628 to display the data

After many hours of learning, I was able to use a 16F628 PIC to read the serial data in from the GPS module and display the results on a LCD panel. The sourceboost C compiler, free edition, worked great. The code used up most of the resources in the PIC, which was all the RAM (limited to 2 banks out of 4) and 2K of code. The free compiler is limited to 2 banks of RAM and the 16f628 has 4. Instead of buying the the 'Standard Edition,' which would have allowed me to use all four banks, I used this problem as an excuse to tighten up the code.

I wrote the program within the sourceboost IDE, which ran in a Windows 2000 VMWare virtual machine on my Linux server. I mapped a drive from the Virtual Machine to my stand alone Windows server, where I used Winpic to program the PIC via a serial port. My initial plan was to do everything on my Linux machine because it's a faster computer, but for some reason, I wasn't able to program the PIC from a serial port within the VM. Probably a timing issue.

The project taught me:

  • How to interface the serial port with a PIC (hardware)
  • How to read serial data
  • How to display data on a hitachi HD44780 LCD display (basics)
  • The inner workings of the PIC-EL
  • what I forgot about C
  • the NEMA sentences

Note - The PIC-EL pinouts didn't work for me I needed port B for the serial port. So it was easier to use a 20 pin DIP socket to connect the right pins on the PIC to the PIC-EL and the LCD. Obviously, the code is highly dependent on the PIC connections.

--Stu 22:39, 27 April 2007 (EDT)

Notes on the Software

The program code is listed at the bottom of this page. I used two files, a library of LCD functions and the main program. The pseudo-code goes something like:

setup the serial port
reset the LCD
display the welcome message
do while(1) - i.e. forever
  wait for serial input
  parse the $GPGGA line (positioning)
  skip the time, if not a valid fix
  read in lat/lon
  print the first two lines - lat/lon
  read in # of sats
    if > 5 - 3D fix - print # of satellites and altitude
    if 3> # of sats < 5 - print 2D fix info
    if < 3 - print Searching...
    blink the LEDs
  print the last two lines
  clear the rxBuffer

I split the printing into two sessions because I didn't have enough RAM to parse all four lines, then print.

Notes on the Hardware with Pictures

Here is a schematic. GPS Interface The 7805 regulator and 7406 buffer aren't included, yet.

A MAX232 provides the RS-232 signals to the outside world. I used the TTL signals from the GPS module to feed the PIC. Use the non-inverted signal, but buffer the signal. Two regulators were used to provide +5VDC and +3VDC for the various parts. The GPS module wants 3VDC, but the preamp in the antenna needs 5VDC. The PIC needs 5VDC, too.


PIC-LCD Connections are listed below. It's a little crazy because I used the PIC-EL as the development platform. Normally, the PIC-EL uses port B to interface with the LCD, but since I needed RB1 for the serial port, I had to juggle the pins. The simplest solution was to keep the high bits for the LCD control pins and use the consecutive bits on port A for the data.

RA0 - LCD DB4 - LCD Pin 11
RA1 - LCD DB5 - LCD Pin 12 - LED 3
RA2 - LCD DB6 - LCD Pin 13 - LED 2
RA3 - LCD DB7 - LCD Pin 14 - LED 1

RB4 - E   -  LCD Pin 6
RB5 - WR  -  LCD Pin 5
RB6 - RS  -  LCD Pin 4

RB1 - Serial Data IN

Getting GPSD to Work

After many months, I finally mounted the GPS board into a case and hooked up the GPS to a PC Engines WRAP board using the serial port. I used gpsd to provide the 1PPS signal to ntp. Here are a couple of high level notes.

  • /dev/ttyS0 was used as the console port so I reclaimed it for gpsd.
  • Added a RS-232 driver, 5 Volt power supply (7805), three diodes to reduce 5.0 -> 3.2V. The GPS board needs 3.3VDC.
  • I recompiled GPSD using the notes from this page Voyage Linux - Development - this was the hold up!
  • Added a pulse lengthening circuit to extend the 1PPS pulse from 100ms to 200ms. Used a 555 timer and a transistor. The 1PPS signal from the GPS RX is 3v, positive going. A NPN transistor buffers the signal and inverts it. The 555 was configured as a one-shot, which produced a positive-going 200ms pulse. Added a LED to pin 3 (output) for fun. It blinks once per second - when locked.
  • Used the DCD line. (I wanted to recompile to change from DCD to RTS, but I changed the serial cable and found I didn't need to change 1PPS pins. This kept the configuration somewhat standard.)
  • I made sure $GPRMC was turned on. (It wasn't so I was getting messages about needing the year from ZDA or RMC.)

server minpoll 4 prefer
fudge time1 .2 refid GPSa

server minpoll 4 prefer
fudge refid PPSa

Starting GPSD with the following:

 /usr/local/sbin/gpsd -n /dev/ttyS0

You need the -n, otherwise RTS-CTS should be tied together.

Reference Links

Stuff on my local drive will be slow to download from the Internet. - manual (pdf) on local drive - web site with info - novatel documents (L1 GPS Firmware is the software doc) - GPS Firmware manual (pdf) on local drive - StarView Software, reads the data from the GPS receiver - TAPR, special deals, here's the antenna I bought. It's a motorola active antenna, part# GCNAC1232A - Found this link to the antenna. - U-center software, a little better than StarView, Nice displays - Helix antenna GPS on SparkFun (Great site!) - You can't use two GPS receivers to get better accuracy.

Text From E-Bay

[When I bought the antennas - this text was attached to the sales advertisment. It was pretty encouraging.]



I bought a box of TeleTrac units and pulled the GPS modules out of them. I can not sell the TeleTrac boxes per my agreement with the source. I have been told they are standard GPS modules, but I do not know anything about them?

Info I can obtain for the module:

     ARM GP4020 AG
     169-614471-007 BLK 014

Information received from a customer:

Well I successfully received NEMA data from all three of the OEM GPS modules I purchased from you. All I had to do was hook up an antenna using the pig tail that came with the module, connect the data transmit line to a RS232 line driver (MAX232 chip for example) and then to the serial port of my computer, connect the NEMA input line to ground to tell the module to transmit in NEMA at 4800 baud, and supply power. I used 5 volts. It took all three modules about 70 seconds to acquire four satellites to determine a 3D fix and start sending valid time, lat, long, elevation and satellite data to my computer. I used Breyterm, but Window's HyperTerminal would work fine.

The module is made by Novatel, previously CMC, and is the SUPERSTAR II model. Novatel's web page for the SUPERSTAR II is

Giving credit where it is due, the site that connected the Zarlink GP4020 processor to the Novatel GPS module is "The GPL-GPS Project" website: This is a good site with free help of software to replace the proprietary TechnoCom firmware and reprogram the baseband processor (GP4020) with open source firmware. The GPL acronym stands for General Public License.

Program Code

Here are the two files I wrote.


Here is the main program file. The RS-232 code/defs need cleaning. The LCD functions are in a separate file, listed after this chunk of code.

// LCD.c - Display GPS data from the serial port
//      Author: Stu Mitchell
//      Date:   28 April 2007
#include <system.h>
#include <stdlib.h>
#include <string.h>
#include "LCD_lib.c"

// Clock Frequency
#pragma CLOCK_FREQ 4000000

//slm - Target PIC16F628 configuration word

#define spBAUD  9600
#define FOSC 4000000L
#define fSPBCLK (FOSC)        /* UART Baud rate generator clock (high speed) */
//#define fSPBCLK (FOSC / 4)    /* UART Baud rate generator clock (low speed) */
#define SPBRG_VAL (fSPBCLK / (spBAUD * 16L)) - 1L

// Defines for hardware USART - from rs232_driver.h
// PIC16F628 defaults for hardware USART support
#define TX_PORT		0x06
#define TX_TRIS		0x86
#define TX_BIT		2
#define RX_PORT		0x06
#define RX_TRIS		0x86
#define RX_BIT		1
#define e_SPBRG		0x99 
#define e_RCREG		0x1a
#define e_TXREG		0x19 
#define e_TXSTA		0x98 
#define e_RCSTA		0x18 
#define e_TXIF_PIR	0x0c 
#define e_RCIF_PIR	0x0c 
#define e_TXIF_BIT	4 
#define e_RCIF_BIT	5 
#define MODE		(USART_reset_wdt | USART_HW)

// bit_time = FOSC / 4 / BAUDRATE 
// Needs to be defined, but not used for HW USART
#define bit_time 521

// Had to define this here, instead of with the rest of the includes?
#include <rs232_driver.h>

// Start the fun!
void main()
	// Variable declarations
	char rxBuffer[80]; 				//receive buffer
	char delims[] = ",";			// Delimeter is a comma
	char *result = NULL;			// Parsing the string
	char line1[22];					// First line of the display
	char line2[22];					// Second line of the display
	int sats;						// number of satellites in view
	// Set up the ports	
	trisa = 0x00; // port a output
	trisb = 0x0F; // port b High bits output, low bits input
	// Initialize the USART
	uart_init(1,SPBRG_VAL);  // set high speed divisor mode and divisor value for 9600 baud
    // Init the LCD, Clear the Screen, and wait for the first GPGGA string.
    goto_line( 1 );
    lprintf( "GPS Receiver");
    lprintf( "Version 1.0");
    lprintf( "Stu Mitchell");
    // Wait for the first GPGGA string
	// endless loop
	while( 1 ) {
		// Get the string, wait until the new line (i.e. gets ())
		gets( rxBuffer );
		// parse the buffer and build the display lines
		result = strtok( rxBuffer, delims );   		// get message ID
		if ( !strcmp (result, "$GPGGA") ) {     	// Parse, if NEMA ID is position sentence
			if( rxBuffer[7] != ',' ) {    			// If no time, old fix
				result = strtok( NULL, delims );	// store the time
			strcpy( line1, "LAT:  " );				// Set up the first two lines for display	
			strcpy( line2, "LON: " );        
			result = strtok( NULL, delims ); 		// Get lat
			strcat( line1, result );
			result = strtok( NULL, delims ); 		// Get N or S
			strcat( line1, " " );
			strcat( line1, result );
			result = strtok( NULL, delims ); 		// Get longitude
			strcat( line2, result );
			result = strtok( NULL, delims ); 		// Get E or W
			strcat( line2, " " );
			strcat( line2, result );
			// print first two lines, lat and long
			goto_line( 1 );
			lprintf( line1 );
			goto_line( 2 );
			lprintf( line2 );
			// Now work on the ALT and # of sats
			strcpy( line1, "                   ");
			strcpy( line2, "                   ");
			result = strtok( NULL, delims );		// quality indicator
			result = strtok( NULL, delims );		// number of sats
			sats = strtoi( result,NULL,10);
			if( sats >= 5 ) {						// If 3D fix, more than 4 sats 
				strcpy( line2, "Tracking " );
				strcat( line2, result );
				strcat( line2, " sats" );
				result = strtok( NULL, delims );	// grab HDOP
				result = strtok( NULL, delims );	// grab ALT
				strcpy( line1, "ALT: " );
				strcat( line1, result );
				strcat( line1, " M   " );
			if( (sats >= 3) && (sats < 5) ) {
				strcpy( line2, "Tracking " );
				strcat( line2, result );
				strcat( line2, " sats" );
				result = strtok( NULL, delims );	// grab HDOP
				result = strtok( NULL, delims );	// grab ALT
				strcpy( line1, "ALT: 2D           " );
			if( sats < 3 ) {
				strcpy( line2, "Searching...       ");
				strcpy( line1, "ALT: OLD FIX       ");
			// print the next two lines, alt and status			
			goto_line( 3 );
			lprintf( line1 );
			goto_line( 4 );
			lprintf( line2 );
		// Clear the receive buffers
	} // end while loop 

LCD Library file

Here is the library file with the reusable LCD functions. They were based on the 'include' file, which came with Sourceboost. But to understand what was going on, I decided to write my own, scaled down library.

// LCD_lib.c - LCD functions. Based on the lcd_driver.h file
//     Note - this is VERY dependant on the PIC wiring diagram

// LCD with HD44780 drive chip
// Designed and test for 16F628
// PortB - Control signals
// PortA - Data signals
// Wiring Diagram
// LCD 	PIC   
// ===  ====
// RS 	RB06
// WR 	RB05
// E  	RB04
// DB7  RA03
// DB6  RA02
// DB5  RA01
// DB4  RA00
// Using 4 Bit interface

#include <system.h>

// Function Declarations
void LCD_Setup(void);
void LCD_FunctionMode(void);
void LCD_DataMode(void);
void LCD_RawWriteNibble(char);
void LCD_Write(char);
void LCD_RawWrite(char);
void lprintf( const char *lcdptr );   //write string 
void LCD_Clear();
void Blink_LEDs();
void goto_line(int);

// Control signal bit definitions
// HD44780 pin numbers
#define LCD_E     4
#define LCD_RW    5
#define LCD_RS    6

// LCD Commands ( Refer to LCD Data Sheet )
#define clear_lcd         		0x01 // Clear Display
#define return_home       		0x02 // Cursor to Home position
#define entry_mode        		0x06 // Normal entry mode
#define entry_mode_rev    		0x04 // Normal entry mode  -reverse direction
#define entry_mode_scroll 		0x07 // - with shift
#define entry_mode_scroll_rev  	0x05 // reverse direction
#define system_set_8_bit  		0x38 //8 bit data mode 2 line ( 5x7 font )
#define system_set_4_bit  		0x28 // 4 bit data mode 2 line ( 5x7 font )
#define system_set_reset  		0x30 // Reset code
#define display_on        		0x0C // Display ON - 2 line mode
#define display_off       		0x08 // Display off
#define set_dd_line1      		0x80 // Line 1 position 1
#define set_dd_line2      		0xC0 // Line 2 position 1
#define set_dd_line3	  		0x94 // Line 3 position 1
#define set_dd_line4      		0xD4 // Line 4 position 1
#define set_dd_ram        		0x80 // Line 1 position 1
#define write_data        		0x00 // With RS = 1
#define cursor_on         		0x0E // Switch Cursor ON
#define cursor_off        		0x0C // Switch Cursor OFF
#define cursor_blink_on   		0x0F // Cursor plus blink
#define cursor_shift_right 		0x14 // Move cursor right
#define cursor_shift_left 		0x10 // Move cursor left
#define display_shift_right 	0x1C // Scroll display right
#define display_shift_left 		0x18 // Scroll display left

#define WriteNoDelay 1
#define WriteDelayTime 0
#define WriteUseBusy 2

#define LineLength 20 // Length of a single line, used in lprintf

// LCD Function                    

// Clear the LCD
void LCD_Clear()
	LCD_Write( clear_lcd ); // clear display
	LCD_Write( set_dd_ram );

// Setup the LCD
void LCD_Setup(void)
	delay_ms(500); // Power up delay
	// Entering LCD Command
	// Reset sequence as described in data sheets
	LCD_RawWriteNibble( system_set_reset >> 4 ); 
	delay_ms(10); // min delay here of 4.1 ms
	LCD_RawWriteNibble( system_set_reset >> 4 );
	delay_ms(5); // min delay here of 100us
	LCD_RawWriteNibble( system_set_reset >> 4 );
    // Setup 2 lines, bit mode, 5x7 font
	LCD_Write( system_set_4_bit );		
	// Turn off the display
	LCD_Write( display_off );
	// Set the entry mode
	LCD_Write( entry_mode );
	// Turn on the display
	LCD_Write( display_on );
	// Position 1, Line 1
	LCD_Write( set_dd_ram );
	// Entering LCD Data

// Blink the LEDs - yay!
void Blink_LEDs(void)
	char i = 0;
	for(i=0; i< 3; i++) {
	set_bit( porta, 3);
	delay_ms( 50 );
	clear_bit( porta, 3);
	delay_ms( 50 );

// Set the LCD in function mode
void LCD_FunctionMode(void)
	//slm - clear_bit( porta, LCD_RS );
	clear_bit( portb, LCD_RS );

// Set the LCD to data mode
void LCD_DataMode(void)
	//slm - set_bit( porta, LCD_RS );
	set_bit( portb, LCD_RS );

// Write the character to the LCD
void LCD_Write(char d )
	LCD_RawWrite( d );
	delay_ms( 1 ); // enough time for normal command execution - clear and home need longer!!	

// Write the character to the LCD
void LCD_RawWrite( char d )
	// output upper nibble
	LCD_RawWriteNibble( d >> 4 );
	// output lower nibble
	LCD_RawWriteNibble( d );

// Do it 4 bits at a time
void LCD_RawWriteNibble(char d )
	char n = d;
	// Make sure the lower bits of the port are set as outputs
	trisa &= 0xF0;
	// keep upper data bits the same, clear the lower bits	
	porta &= 0xF0;
	//Strip high bits on data
	n &= 0x0F;
	// OR the lower bits with d, which puts the data on portb.
	porta |= n;
	// set writing mode
	clear_bit( portb, LCD_RW );
	// Clock data
   	asm NOP // setup time
	asm NOP // setup time
	asm NOP // setup time
    // Write nibble by toggling E bit
	set_bit( portb, LCD_E );
	asm NOP // delay
	asm NOP // delay
	asm NOP // delay
	clear_bit( portb, LCD_E );
	asm NOP ;// holdup time
	// set back to read mode
	clear_bit( portb, LCD_RW );

// Print one line to the LCD
//   LineLength is a global define
void lprintf( const char *lcdptr )
	char pi;
	char c;
	pi = 0;
    while( pi <= LineLength  )
		c = lcdptr[pi++];
		if ( c == 0 ) 					// End of Line
		LCD_Write( c );					// Display on LCD

// Move to line number 'd'
void goto_line( int d )
	switch( d ) {
		case 1:
			LCD_Write( set_dd_line1 );
		case 2:
			LCD_Write( set_dd_line2 );
		case 3:
			LCD_Write( set_dd_line3 );
		case 4:
			LCD_Write( set_dd_line4 );
			LCD_Write( set_dd_line1 );