gpsd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []