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 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
@@ -0,0 +1,5 @@
1
+ module GPS
2
+ class Error < Exception; end
3
+ class ConnectionError < Error; end
4
+ class ProtocolError < Error; end
5
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'socket'
2
+ require 'multi_json'
3
+
4
+ module GPS
5
+ module Util
6
+
7
+ end
8
+ end
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: []