gpsd2json 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +46 -0
- data/lib/gps2json.rb +211 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 61aaf077f1e5605afa404ee0b7639296273c1f43
|
4
|
+
data.tar.gz: 219dccf5fe3dc81841c126362a9eba3ddb983baa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0dc9aadcfadb0779369fa5094ad951decb2d6deafbb93cb71e37c09868f333a665a8703f5b97e7b6a83741a417efe23cf78a1ff92331e74e02c3a9e78851608f
|
7
|
+
data.tar.gz: 0ddb145d5046fa5d313a29d392253ce14d28868eb2a83d4f69f149aa0f811a2c4a0d2403047c8caeb65e1e0c8563707619ced218cc3fed8cbcc38be6a6a9ed45
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 Mischa Molhoek
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# gpsd2json
|
2
|
+
ruby client to receive JSON formatted info of the gps daemon
|
3
|
+
|
4
|
+
## initialization
|
5
|
+
```bash
|
6
|
+
require 'gpsd2json'
|
7
|
+
gps = GPSD2JSON.new()
|
8
|
+
```
|
9
|
+
## First you set some callbacks on the most important changes
|
10
|
+
```bash
|
11
|
+
gps.on_position_change { |pos| STDERR.puts pos.inpect }
|
12
|
+
gps.on_satellites_change { |sats| STDERR.puts "#{sats.count} found, #{sats.count{|sat| sat['used']} are used" }
|
13
|
+
```
|
14
|
+
## Then, your start watching
|
15
|
+
```bash
|
16
|
+
gps.start
|
17
|
+
```
|
18
|
+
after this, the positions will be given to the callback block
|
19
|
+
|
20
|
+
## When you had enough, you can stop watching
|
21
|
+
```bash
|
22
|
+
gps.stop
|
23
|
+
```
|
24
|
+
|
25
|
+
## there is on more callback to receive all data as raw json
|
26
|
+
```bash
|
27
|
+
gps.on_raw_data { |json| STDERR.puts json.inspect}
|
28
|
+
```
|
29
|
+
|
30
|
+
## Also, you can change the minimum speed requered to return a position change, with
|
31
|
+
```bash
|
32
|
+
gps.change_min_speed(speed: <whatever speed>)
|
33
|
+
```
|
34
|
+
|
35
|
+
## development
|
36
|
+
```bash
|
37
|
+
# Install
|
38
|
+
bundle
|
39
|
+
# irb
|
40
|
+
bundle exec irb -r ./lib/gpsd2json.rb
|
41
|
+
# test
|
42
|
+
bundle exec rspec --color -fd spec/gpsd_client_test.rb
|
43
|
+
```
|
44
|
+
it also have a code coverage dir for you to see if your test set is about 95%
|
45
|
+
|
46
|
+
send me PR if you want changes, but only dare to do so when you added the proper tests
|
data/lib/gps2json.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'json'
|
3
|
+
require 'date'
|
4
|
+
class GPSD2JSON
|
5
|
+
VERBOSE = false
|
6
|
+
# A simple gpsd client that dump's json objects contianing all info received from the gpsd deamon
|
7
|
+
# you need to at least setup either the raw callback (on_raw_change) or position callback (on_position_change) to use GPSD2JSON. the raw callback just passes the json objects it received from the daemon on to the block you pass it. the on_position_change and on_satellites_change are a bit easier to use.
|
8
|
+
# @example Easy setup
|
9
|
+
# gps = GPSD2JSON.new()
|
10
|
+
# gps.on_satellites_change { |sats| STDERR.puts "found #{sats.length} satellites, of which #{sats.count{|sat| sat['used']} } active" }
|
11
|
+
# gps.on_position_change { |pos| STDERR.puts "lat: #{pos['lat']}, lng: #{pos['lon']}, alt: #{pos['alt']}, speed: #{pos['speed']} at #{pos['time']}, which is #{(Time.now - pos['time'].to_time) * 1000}ms old" }
|
12
|
+
# gps.start
|
13
|
+
# #when done
|
14
|
+
# gps.stop
|
15
|
+
# @example Quickest raw mode, just dumping all json packets as the are
|
16
|
+
# gps = GPSD2JSON.new()
|
17
|
+
# gps.on_raw_change { |raw| STDERR.puts raw.inspect }
|
18
|
+
# gps.start
|
19
|
+
# #when done
|
20
|
+
# gps.stop
|
21
|
+
def initialize(host: 'localhost', port: 2947)
|
22
|
+
@socket = nil
|
23
|
+
@socket_ready = false
|
24
|
+
@host = host
|
25
|
+
@port = port
|
26
|
+
@trackthread = nil
|
27
|
+
@socket_init_thread = nil
|
28
|
+
@min_speed = 0.8 # speed needs to be higher than this to make the gps info count
|
29
|
+
@last = nil #last gps info
|
30
|
+
@sats = nil # last satellites info
|
31
|
+
@json_raw_callback = nil
|
32
|
+
@json_pos_callback = nil
|
33
|
+
@json_sat_callback = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [Object] options Possible options to pass (not used yet)
|
37
|
+
# @param [Block] block Block to call when new json object comes from gpsd
|
38
|
+
def on_raw_change(options:{}, &block)
|
39
|
+
@json_raw_callback = block
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [Object] options Possible options to pass (not used yet)
|
43
|
+
# @param [Block] block Block to call when new gps position json object comes from gpsd
|
44
|
+
def on_position_change(options:{}, &block)
|
45
|
+
@json_pos_callback = block
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Object] options Possible options to pass (not used yet)
|
49
|
+
# @param [Block] block Block to call when new satellite info json object comes from gpsd
|
50
|
+
def on_satellites_change(options:{}, &block)
|
51
|
+
@json_sat_callback = block
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Float] speed The minimum speed to accept a gps update
|
55
|
+
def change_min_speed(speed:)
|
56
|
+
@min_speed = speed
|
57
|
+
end
|
58
|
+
|
59
|
+
# Open the socket and when ready request the position flow from the gps daemon
|
60
|
+
def start
|
61
|
+
# background thread that is used to open the socket and wait for it to be ready
|
62
|
+
@socket_init_thread = Thread.start do
|
63
|
+
#open the socket
|
64
|
+
while not @socket_ready
|
65
|
+
init_socket
|
66
|
+
#wait for it to be ready
|
67
|
+
sleep 0.1
|
68
|
+
end
|
69
|
+
# it's ready, tell it to start watching and passing
|
70
|
+
puts "socket ready, start watching" if VERBOSE
|
71
|
+
@socket.puts '?WATCH={"enable":true,"json":true}'
|
72
|
+
end
|
73
|
+
|
74
|
+
# background thead that is used to read info from the socket and use it
|
75
|
+
@trackthread = Thread.start do
|
76
|
+
while true do
|
77
|
+
begin
|
78
|
+
read_from_socket
|
79
|
+
rescue
|
80
|
+
"error while reading socket: #{$!}" if VERBOSE
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @return [string] status info string containing nr satellites, fix, speed
|
87
|
+
def to_status
|
88
|
+
return "lat: #{last['lat']}, lng: #{last['lon']}, speed:#{last['speed']}, sats: #{@sats.length}(#{@sats.count{|sat| sat['used']}})" if @socket_ready and @last and @sats
|
89
|
+
return "lat: #{last['lat']}, lng: #{last['lon']}, speed:#{last['speed']}" if @socket_ready and @last and @sats.nil?
|
90
|
+
return "sats: #{@sats.length}(#{@sats.count{|sat| sat['used']}}), no fix yet" if @socket_ready and @last.nil? and @sats
|
91
|
+
return "connected with gpsd, waiting for data" if @socket_ready
|
92
|
+
return "waiting for connection with gpsd" if @socket_ready == false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Stop the listening loop and close the socket. It will read the last bit of data from the socket, close it, and clean it up
|
96
|
+
def stop
|
97
|
+
# last read(s)
|
98
|
+
3.times { read_from_socket }
|
99
|
+
# then close
|
100
|
+
close_socket
|
101
|
+
# then cleanup
|
102
|
+
Thread.kill(@socket_init_thread) if @socket_init_thread
|
103
|
+
Thread.kill(@trackthread) if @trackthread
|
104
|
+
@socket_ready = false
|
105
|
+
end
|
106
|
+
|
107
|
+
# initialize gpsd socket
|
108
|
+
def init_socket
|
109
|
+
begin
|
110
|
+
puts "init_socket" if VERBOSE
|
111
|
+
close_socket if @socket
|
112
|
+
@socket = TCPSocket.new(@host, @port)
|
113
|
+
@socket.puts("w+")
|
114
|
+
puts "reading socket..." if VERBOSE
|
115
|
+
welkom = ::JSON.parse(@socket.gets)
|
116
|
+
puts "welkom: #{welkom.inspect}" if VERBOSE
|
117
|
+
@socket_ready = (welkom and welkom['class'] and welkom['class'] == 'VERSION')
|
118
|
+
puts "@socket_ready: #{@socket_ready.inspect}" if VERBOSE
|
119
|
+
rescue
|
120
|
+
@socket_ready = false
|
121
|
+
puts "#$!" if VERBOSE
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Read from socket. this should happen in a Thread as a continues loop. It should try to read data from the socket but nothing might happen if the gps deamon might not be ready. If ready it will send packets that we read and proces
|
126
|
+
def read_from_socket
|
127
|
+
if @socket_ready
|
128
|
+
begin
|
129
|
+
if input = @socket.gets.chomp and not input.to_s.empty?
|
130
|
+
parse_socket_json(json: JSON.parse(input))
|
131
|
+
else
|
132
|
+
sleep 0.1
|
133
|
+
end
|
134
|
+
rescue
|
135
|
+
puts "error reading from socket: #{$!}" if VERBOSE
|
136
|
+
end
|
137
|
+
else
|
138
|
+
sleep 0.1
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Proceses json object returned by gpsd daemon. The TPV and SKY object
|
143
|
+
# are used the most as they give info about satellites used and gps locations
|
144
|
+
# @param [JSON] json The object returned by the daemon
|
145
|
+
def parse_socket_json(json:)
|
146
|
+
case json['class']
|
147
|
+
when 'DEVICE', 'DEVICES'
|
148
|
+
# devices that are found, not needed
|
149
|
+
when 'WATCH'
|
150
|
+
# gps deamon is ready and will send other packets, not needed yet
|
151
|
+
when 'TPV'
|
152
|
+
# gps position
|
153
|
+
# "tag"=>"RMC", # "device"=>"/dev/ttyS0", # "mode"=>3,
|
154
|
+
# "time"=>"2017-11-28T12:54:54.000Z", # "ept"=>0.005, # "lat"=>52.368576667,
|
155
|
+
# "lon"=>4.901715, # "alt"=>-6.2, # "epx"=>2.738, # "epy"=>3.5,
|
156
|
+
# "epv"=>5.06, # "track"=>198.53, # "speed"=>0.19, # "climb"=>0.0,
|
157
|
+
# "eps"=>7.0, # "epc"=>10.12
|
158
|
+
if json['mode'] > 1
|
159
|
+
#we have a 2d or 3d fix
|
160
|
+
if is_new_measurement(json: json)
|
161
|
+
json['time'] = DateTime.parse(json['time'])
|
162
|
+
puts "lat: #{json['lat']}, lng: #{json['lon']}, alt: #{json['alt']}, speed: #{json['speed']} at #{json['time']}, which is #{(Time.now - json['time'].to_time) * 1000}ms old" if VERBOSE
|
163
|
+
@json_pos_callback.call(json) if @json_pos_callback
|
164
|
+
end
|
165
|
+
end
|
166
|
+
when 'SKY'
|
167
|
+
# report on found satellites
|
168
|
+
sats = json['satellites']
|
169
|
+
if satellites_changed(sats: sats)
|
170
|
+
puts "found #{sats.length} satellites, of which #{sats.count{|sat| sat['used']}} are used" if VERBOSE
|
171
|
+
@json_sat_callback.call(sats) if @json_sat_callback
|
172
|
+
end
|
173
|
+
else
|
174
|
+
puts "hey...found unknow tag: #{json.inspect}" if VERBOSE
|
175
|
+
end
|
176
|
+
@json_raw_callback.call(json) if @json_raw_callback
|
177
|
+
end
|
178
|
+
|
179
|
+
# checks if the new satellites object return by the deamon is different enough compared
|
180
|
+
# to the last one, to use it
|
181
|
+
def satellites_changed(sats:)
|
182
|
+
if @sats.nil? or (@sats.length != sats.length or @sats.count{|sat| sat['used']} != sats.count{|sat| sat['used']})
|
183
|
+
@sats = sats
|
184
|
+
return true
|
185
|
+
end
|
186
|
+
return false
|
187
|
+
end
|
188
|
+
|
189
|
+
# checks if the new location object return by the deamon is different enough compared
|
190
|
+
# to the last one, to use it. it could be disregarded for example because the speed is to low, and you don't want to have the location jumping around when you stand still
|
191
|
+
def is_new_measurement(json:)
|
192
|
+
if @last.nil? or (@last['lat'] != json['lat'] and @last['lon'] != json['lon'] and json['speed'] >= @min_speed)
|
193
|
+
@last = json
|
194
|
+
return true
|
195
|
+
end
|
196
|
+
return false
|
197
|
+
end
|
198
|
+
|
199
|
+
# Close the gps deamon socket
|
200
|
+
def close_socket
|
201
|
+
begin
|
202
|
+
if @socket
|
203
|
+
@socket.puts '?WATCH={"enable":false}'
|
204
|
+
@socket.close
|
205
|
+
end
|
206
|
+
@socket = nil
|
207
|
+
rescue
|
208
|
+
puts "#$!" if VERBOSE
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gpsd2json
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mischa Molhoek
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '12.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '12.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: juwelier
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.4.0
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '2.4'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.4.0
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.4'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: simplecov
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: bundler
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: yard-doctest
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.1'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0.1'
|
103
|
+
description: This gem can be used to talk to the gps daemon
|
104
|
+
email: mischamolhoek@gmail.com
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files:
|
108
|
+
- LICENSE
|
109
|
+
- README.md
|
110
|
+
files:
|
111
|
+
- LICENSE
|
112
|
+
- README.md
|
113
|
+
- lib/gps2json.rb
|
114
|
+
homepage: http://github.com/mmolhoek/gpsd2json
|
115
|
+
licenses:
|
116
|
+
- MIT
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.5.1
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: ruby gem to access the gpsd daemon
|
138
|
+
test_files: []
|