Parse output from a running gpsd daemon to retrieve GPS data. See: gpsd.io/client-howto.html
- GPSD_HOST
The host running gpsd to connect to.
- GPSD_PORT
The default gpsd socket port.
- SOCKET_TIMEOUT
Timeout for socket reads.
- host R
The host running gpsd.
- port R
The port gpsd is listening on.
- socket R
The raw socket to gpsd.
new( host: GPSD_HOST, port: GPSD_PORT )
Create a new gpsd parser. Defer connection.
def initialize( host: GPSD_HOST, port: GPSD_PORT )
@socket = nil
@host = host
@port = port
end
Connect to a running gpsd daemon.
def connect
@socket = TCPSocket.new( self.host, self.port )
self.socket.puts '?WATCH={"enable":true}'
self.socket.flush
self.get_daemon_info
end
Retrieve current GPSD time and location data, after the initial connection is made.
def fetch_state
return unless self.socket
self.socket.puts '?POLL;'
self.socket.flush
ready, _, _ = IO.select( [self.socket], nil, nil, SOCKET_TIMEOUT )
data = nil
if ready && ! ready.empty?
data = self.parse_response( self.socket.gets )
end
return unless data
gps = self.gps_position( data )
payload = { time: self.gps_time( data ) }
payload.merge!( gps: gps ) if gps
return payload
rescue Errno::EPIPE, SocketError => err
self.log.error "Error while polling gpsd: %p (attempting to reconnect)" % [ err.message ]
self.connect
return nil
end
Protected Instance Methods
GPSD is a simple, lined based protocol. Pull initial connection info off the socket.
def get_daemon_info
ready, _, _ = IO.select( [self.socket], nil, nil, SOCKET_TIMEOUT )
while ready && ! ready.empty?
data = JSON.parse( self.socket.gets )
if data[ 'class' ] == "VERSION"
self.log.warn "Connected to GPSD v%s" % [ data['release'] ]
end
ready, _, _ = IO.select( [self.socket], nil, nil, SOCKET_TIMEOUT )
end
end
Construct and return a ‘sys.gps.position’ message, or nil
if there’s no GPS fix. Munge the GPS message to be more CoT-like before delivery.
def gps_position( data )
level = data[ 'mode' ]
if level <= 1
self.log.warn "No GPS fix yet."
return nil
end
return {
pos: data.values_at( 'lat', 'lon' ),
hae: data[ 'alt' ] || 0,
ce: level == 3 ? 3.0 : 15.0
}
end
Return the current time from the PPS signal.
def gps_time( data )
return Time.parse( data['time'] ).to_i
end
Parse and return the relevant information from the gpsd response, or nil
if it isn’t what we’re expecting.
def parse_response( raw )
data = JSON.parse( raw )
return unless data[ 'class' ] == "POLL"
return data.dig( 'tpv', 0 )
end