GPS devices

In general, a GPS device sends a stream of data every second. The stream is composed of information organized in command groups. Here's an example of the output from a GPS:

$GPGSA,A,3,17,16,22,31,03,18,25,,,,,,1.6,1.0,1.2*39 
$GPGGA,185030.30,4532.8959,N,07344.2298,W,1,07,1.0,23.8,M,-32.0,M,,*69 
$GPGLL,4532.8959,N,07344.2298,W,185030.30,A*12 
$GPRMC,185030.00,A,4532.8959,N,07344.2298,W,0.9,116.9,160198,,*27 
$GPVTG,116.9,T,,,0.9,N,1.7,K*2D 
$GPZDA,185030.30,16,01,1998,,*65 
$GPGSV,2,1,08,03,55,142,50,22,51,059,51,18,48,284,53,31,23,187,52*78

Each line corresponds to a data set. Here's the C structure of some of the data sets:

typedef struct GPSRMC_s { 
   double UTC; 
   int Status; 
   Degree_t Latitude; 
   NORTHSOUTH Northing; 
   Degree_t Longitude; 
   EASTWEST Easting; 
   float Speed; 
   float Heading; 
} GPSRMC_t; 

typedef struct GPSVTG_s { 
   float Heading; 
   float SpeedInKnots; 
   float SpeedInKMH; 
} GPSVTG_t; 

typedef struct GPSUTM_s { 
   UTM_t X; 
   UTM_t Y; 
} GPSUTM_t;

You could provide one API per GPS format command: gps_get_rmc(), gps_get_vtg(), get_get_utm(), and so on. Internally, each function would send a message with a different command, and the reply was the data last received by the GPS.

The first obstacle was that read() and write() are half-duplex operations; you can't use them to send a command and get data back. You could do this:

GPSUTM_t utm; 
Int cmd = GPS_GET_GSA;

fd = open( "/dev/gps1", O_RDWR ); 
write( fd, &cmd, sizeof( cmd) ); 
read( fd, &data, sizeof(data) ); 
close(fd);

but this code looks unnatural. Nobody would expect read() and write() to be used in that way. You could use devctl() to send a command and request specific data, but if you implement the driver as a resource manager, you can have a different path for every command set. The driver would create the following pathnames:

and a program wanting to get GSA information would do this:

gps_gsa_t gsa;
int fd;

fd = open ( "/dev/gps1/gsa.bin", O_RDONLY ); 
read( fd, &gsa, sizeof( gsa ) ); 
close ( fd);

The benefit of having both the .txt and .bin extensions is that data returned by the read() would be in ASCII format if you use the .txt file. If you use the .bin file instead, the data is returned in a C structure for easier use. But why support *.txt if most programs would prefer to use the binary representation? The reason is simple. From the shell you could type:

# cat /dev/gps1/rmc.txt

and you'd see:

# GPRMC,185030.00,A,4532.8959,N,07344.2298,W,0.9,116.9,160198,,*27

You now have access to the GPS data from the shell. But you can do even more:

Contrary to what you might think, it's quite simple to support these features from within the resource manager.