gpsd 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/gpsd.rb +135 -0
- data/lib/gpsd/errors.rb +5 -0
- data/lib/gpsd/response.rb +63 -0
- data/lib/gpsd/util.rb +8 -0
- metadata +59 -0
data/lib/gpsd.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'socket'
|
3
|
+
require 'io/wait'
|
4
|
+
|
5
|
+
require_relative 'gpsd/response'
|
6
|
+
require_relative 'gpsd/errors'
|
7
|
+
|
8
|
+
class Socket
|
9
|
+
def ready?
|
10
|
+
not IO.select([self], nil, nil, 0) == nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module GPS
|
15
|
+
class Receiver
|
16
|
+
DEFAULT_HOSTNAME = 'localhost'
|
17
|
+
DEFAULT_PORT = 2947
|
18
|
+
DEFAULT_TIMEOUT = 5
|
19
|
+
DEFAULT_MAX_ATTEMPT = 3
|
20
|
+
DEFAULT_RETRY_DELAY = 10
|
21
|
+
DEFAULT_READ_TIMEOUT = 2
|
22
|
+
|
23
|
+
attr_reader :version, :filename, :hostname, :port
|
24
|
+
|
25
|
+
def initialize(options={})
|
26
|
+
@_options = options
|
27
|
+
|
28
|
+
# set default options
|
29
|
+
@_options[:timeout] ||= DEFAULT_TIMEOUT
|
30
|
+
@_options[:read_timeout] ||= DEFAULT_READ_TIMEOUT
|
31
|
+
@_options[:hostname] ||= DEFAULT_HOSTNAME
|
32
|
+
@_options[:port] ||= DEFAULT_PORT
|
33
|
+
@_options[:retry_delay] ||= DEFAULT_RETRY_DELAY
|
34
|
+
@_options[:attempts] ||= DEFAULT_MAX_ATTEMPT
|
35
|
+
|
36
|
+
connect() unless options[:autoconnect] == false
|
37
|
+
end
|
38
|
+
|
39
|
+
def connect(options=nil)
|
40
|
+
attempts = 0
|
41
|
+
options = @_options.merge(options || {})
|
42
|
+
|
43
|
+
Timeout.timeout(options[:timeout]) do
|
44
|
+
# connect to a UNIX socket
|
45
|
+
if options[:socket]
|
46
|
+
raise GPS::Error.new("File #{options[:socket]} is not a socket") unless File.socket?(options[:socket])
|
47
|
+
@filename = options[:socket]
|
48
|
+
@socket = UNIXSocket.new(options[:socket])
|
49
|
+
else
|
50
|
+
begin
|
51
|
+
attempts += 1
|
52
|
+
@hostname = options[:hostname]
|
53
|
+
@port = options[:port]
|
54
|
+
@socket = TCPSocket.new(options[:hostname], options[:port].to_i)
|
55
|
+
rescue Errno::ECONNREFUSED
|
56
|
+
raise GPS::ConnectionError.new("Could not connect to #{options[:hostname]}:#{options[:port]} after #{attempts} attempts, connection is closed") if attempts >= options[:attempts]
|
57
|
+
sleep options[:retry_delay]
|
58
|
+
retry
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if (gps_ident = GPS::Response.new(socket().gets()))
|
63
|
+
raise GPS::ProtocolError.new("Problem connecting to GPSd: invalid banner") if gps_ident.nil? or gps_ident.classname != :VERSION
|
64
|
+
@version = gps_ident.release
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def socket()
|
70
|
+
if @socket.closed?
|
71
|
+
connect()
|
72
|
+
end
|
73
|
+
|
74
|
+
return @socket
|
75
|
+
end
|
76
|
+
|
77
|
+
def close()
|
78
|
+
socket().close() if not socket().closed?
|
79
|
+
end
|
80
|
+
|
81
|
+
def send_command(cmd, options={})
|
82
|
+
cmdline = "?#{cmd.to_s.upcase}#{options.empty? ? '' : '='+MultiJson.dump(options)};"
|
83
|
+
|
84
|
+
begin
|
85
|
+
socket().puts(cmdline)
|
86
|
+
rescue Errno::EPIPE
|
87
|
+
connect()
|
88
|
+
retry
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_response(blocking=true)
|
93
|
+
rv = []
|
94
|
+
|
95
|
+
if blocking == true
|
96
|
+
return GPS::Response.new(socket().gets())
|
97
|
+
else
|
98
|
+
while socket().ready?
|
99
|
+
v = socket().gets()
|
100
|
+
rv << GPS::Response.new(v)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
return rv
|
105
|
+
end
|
106
|
+
|
107
|
+
def command(command, options={})
|
108
|
+
block = options.delete(:block)
|
109
|
+
send_command(command, options)
|
110
|
+
|
111
|
+
# wait for reply
|
112
|
+
socket().wait(@_options[:read_timeout])
|
113
|
+
|
114
|
+
return read_response(block)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def watch(options={})
|
119
|
+
options = ({
|
120
|
+
:enable => true,
|
121
|
+
:json => true
|
122
|
+
}) if options.empty?
|
123
|
+
|
124
|
+
return command(:watch, options)
|
125
|
+
end
|
126
|
+
|
127
|
+
def poll()
|
128
|
+
return command(:poll).select{|i|
|
129
|
+
i.classname == :POLL
|
130
|
+
}.sort{|a,b|
|
131
|
+
a.time <=> b.time
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/gpsd/errors.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require_relative 'errors'
|
3
|
+
|
4
|
+
module GPS
|
5
|
+
class Response
|
6
|
+
attr_reader :payload
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
if response.is_a?(String)
|
10
|
+
@payload = MultiJson.load(response)
|
11
|
+
elsif response.is_a?(Hash)
|
12
|
+
@payload = response
|
13
|
+
else
|
14
|
+
raise GPS::ProtocolError.new("Invalid response data")
|
15
|
+
end
|
16
|
+
|
17
|
+
@payload['time'] = Time.parse(@payload['time']) if @payload.has_key?('time')
|
18
|
+
|
19
|
+
case @payload['class']
|
20
|
+
when 'POLL'
|
21
|
+
@payload['tpv'] = @payload['tpv'].collect{|i|
|
22
|
+
GPS::Response.new(i)
|
23
|
+
}
|
24
|
+
|
25
|
+
@payload['sky'] = @payload['sky'].collect{|i|
|
26
|
+
GPS::Response.new(i)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method, *args)
|
32
|
+
if @payload.has_key?(method.to_s)
|
33
|
+
return @payload[method.to_s]
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
return @payload[key.to_s]
|
41
|
+
rescue
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def classname()
|
46
|
+
return @payload['class'].to_sym
|
47
|
+
rescue
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_hash()
|
52
|
+
Hash[payload().collect{|k,v|
|
53
|
+
if v.is_a?(GPS::Response)
|
54
|
+
[k, v.to_hash()]
|
55
|
+
elsif v.is_a?(Array) and v.first.is_a?(GPS::Response)
|
56
|
+
[k, v.collect{|i| i.to_hash() }]
|
57
|
+
else
|
58
|
+
[k, v]
|
59
|
+
end
|
60
|
+
}]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/gpsd/util.rb
ADDED
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gpsd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gary Hetzel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: multi_json
|
16
|
+
requirement: &13112800 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.7.9
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *13112800
|
25
|
+
description: This is a client interface to the GPSd service daemon.
|
26
|
+
email: garyhetzel@gmail.com
|
27
|
+
executables: []
|
28
|
+
extensions: []
|
29
|
+
extra_rdoc_files: []
|
30
|
+
files:
|
31
|
+
- lib/gpsd.rb
|
32
|
+
- lib/gpsd/errors.rb
|
33
|
+
- lib/gpsd/response.rb
|
34
|
+
- lib/gpsd/util.rb
|
35
|
+
homepage: https://github.com/ghetzel/gpsd
|
36
|
+
licenses: []
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.8.11
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: A Ruby client interface for GPSd
|
59
|
+
test_files: []
|