Class: GPSD2JSON

Inherits:
Object
  • Object
show all
Defined in:
lib/gps2json.rb

Constant Summary collapse

VERBOSE =
false

Instance Method Summary collapse

Constructor Details

#initialize(host: 'localhost', port: 2947) ⇒ GPSD2JSON

A simple gpsd client that dump’s json objects contianing all info received from the gpsd deamon 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. gps = GPSD2JSON.new() gps.on_satellites_change { |sats| STDERR.puts “found #GPSD2JSON.satssats.length satellites, of which #sat } active” } gps.on_position_change { |pos| STDERR.puts “lat: #'lat', lng: #'lon', alt: #'alt', speed: #'speed' at #'time', which is #- pos.to_time) * 1000ms old” } gps.start #when done gps.stop gps = GPSD2JSON.new() gps.on_raw_change { |raw| STDERR.puts raw.inspect } gps.start #when done gps.stop

Examples:

Easy setup


Quickest raw mode, just dumping all json packets as the are




21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/gps2json.rb', line 21

def initialize(host: 'localhost', port: 2947)
    @socket = nil
    @socket_ready = false
    @host = host
    @port = port
    @trackthread = nil
    @socket_init_thread = nil
    @min_speed = 0.8 # speed needs to be higher than this to make the gps info count
    @last = nil #last gps info
    @sats = nil # last satellites info
    @json_raw_callback = nil
    @json_pos_callback = nil
    @json_sat_callback = nil
end

Instance Method Details

#change_min_speed(speed:) ⇒ Object

Parameters:

  • speed (Float)

    The minimum speed to accept a gps update



55
56
57
# File 'lib/gps2json.rb', line 55

def change_min_speed(speed:)
    @min_speed = speed
end

#close_socketObject

Close the gps deamon socket



200
201
202
203
204
205
206
207
208
209
210
# File 'lib/gps2json.rb', line 200

def close_socket
    begin
        if @socket
            @socket.puts '?WATCH={"enable":false}'
            @socket.close
        end
        @socket = nil
    rescue
        puts "#$!" if VERBOSE
    end
end

#init_socketObject

initialize gpsd socket



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/gps2json.rb', line 108

def init_socket
    begin
        puts "init_socket" if VERBOSE
        close_socket if @socket
        @socket = TCPSocket.new(@host, @port)
        @socket.puts("w+")
        puts "reading socket..." if VERBOSE
        welkom = ::JSON.parse(@socket.gets)
        puts "welkom: #{welkom.inspect}" if VERBOSE
        @socket_ready = (welkom and welkom['class'] and welkom['class'] == 'VERSION')
        puts "@socket_ready: #{@socket_ready.inspect}" if VERBOSE
    rescue
        @socket_ready = false
        puts "#$!" if VERBOSE
    end
end

#is_new_measurement(json:) ⇒ Object

checks if the new location object return by the deamon is different enough compared 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
192
193
194
195
196
197
# File 'lib/gps2json.rb', line 191

def is_new_measurement(json:)
    if @last.nil? or (@last['lat'] != json['lat'] and @last['lon'] != json['lon'] and json['speed'] >= @min_speed)
        @last = json
        return true
    end
    return false
end

#on_position_change(options: {}, &block) ⇒ Object

Parameters:

  • options (Object) (defaults to: {})

    Possible options to pass (not used yet)

  • block (Block)

    Block to call when new gps position json object comes from gpsd



44
45
46
# File 'lib/gps2json.rb', line 44

def on_position_change(options:{}, &block)
    @json_pos_callback = block
end

#on_raw_change(options: {}, &block) ⇒ Object

Parameters:

  • options (Object) (defaults to: {})

    Possible options to pass (not used yet)

  • block (Block)

    Block to call when new json object comes from gpsd



38
39
40
# File 'lib/gps2json.rb', line 38

def on_raw_change(options:{}, &block)
    @json_raw_callback = block
end

#on_satellites_change(options: {}, &block) ⇒ Object

Parameters:

  • options (Object) (defaults to: {})

    Possible options to pass (not used yet)

  • block (Block)

    Block to call when new satellite info json object comes from gpsd



50
51
52
# File 'lib/gps2json.rb', line 50

def on_satellites_change(options:{}, &block)
    @json_sat_callback = block
end

#parse_socket_json(json:) ⇒ Object

Proceses json object returned by gpsd daemon. The TPV and SKY object are used the most as they give info about satellites used and gps locations

Parameters:

  • json (JSON)

    The object returned by the daemon



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/gps2json.rb', line 145

def parse_socket_json(json:)
    case json['class']
    when 'DEVICE', 'DEVICES'
        # devices that are found, not needed
    when 'WATCH'
        # gps deamon is ready and will send other packets, not needed yet
    when 'TPV'
        # gps position
        #  "tag"=>"RMC", #  "device"=>"/dev/ttyS0", #  "mode"=>3,
        #  "time"=>"2017-11-28T12:54:54.000Z", #  "ept"=>0.005, #  "lat"=>52.368576667,
        #  "lon"=>4.901715, #  "alt"=>-6.2, #  "epx"=>2.738, #  "epy"=>3.5,
        #  "epv"=>5.06, #  "track"=>198.53, #  "speed"=>0.19, #  "climb"=>0.0,
        #  "eps"=>7.0, #  "epc"=>10.12
        if json['mode'] > 1
           #we have a 2d or 3d fix
            if is_new_measurement(json: json)
                json['time'] = DateTime.parse(json['time'])
                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
                @json_pos_callback.call(json) if @json_pos_callback
            end
        end
    when 'SKY'
        # report on found satellites
        sats = json['satellites']
        if satellites_changed(sats: sats)
            puts "found #{sats.length} satellites, of which #{sats.count{|sat| sat['used']}} are used" if VERBOSE
            @json_sat_callback.call(sats) if @json_sat_callback
        end
    else
        puts "hey...found unknow tag: #{json.inspect}" if VERBOSE
    end
    @json_raw_callback.call(json) if @json_raw_callback
end

#read_from_socketObject

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/gps2json.rb', line 126

def read_from_socket
    if @socket_ready
        begin
            if input = @socket.gets.chomp and not input.to_s.empty?
                parse_socket_json(json: JSON.parse(input))
            else
                sleep 0.1
            end
        rescue
            puts "error reading from socket: #{$!}" if VERBOSE
        end
    else
        sleep 0.1
    end
end

#satellites_changed(sats:) ⇒ Object

checks if the new satellites object return by the deamon is different enough compared to the last one, to use it



181
182
183
184
185
186
187
# File 'lib/gps2json.rb', line 181

def satellites_changed(sats:)
    if @sats.nil? or (@sats.length != sats.length or @sats.count{|sat| sat['used']} != sats.count{|sat| sat['used']})
        @sats = sats
        return true
    end
    return false
end

#startObject

Open the socket and when ready request the position flow from the gps daemon



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/gps2json.rb', line 60

def start
    # background thread that is used to open the socket and wait for it to be ready
    @socket_init_thread = Thread.start do
        #open the socket
        while not @socket_ready
            init_socket
            #wait for it to be ready
            sleep 0.1
        end
        # it's ready, tell it to start watching and passing
        puts "socket ready, start watching" if VERBOSE
        @socket.puts '?WATCH={"enable":true,"json":true}'
    end

    # background thead that is used to read info from the socket and use it
    @trackthread = Thread.start do
        while true do
            begin
                read_from_socket
            rescue
                "error while reading socket: #{$!}" if VERBOSE
            end
        end
    end
end

#stopObject

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
97
98
99
100
101
102
103
104
105
# File 'lib/gps2json.rb', line 96

def stop
    # last read(s)
    3.times { read_from_socket }
    # then close
    close_socket
    # then cleanup
    Thread.kill(@socket_init_thread) if @socket_init_thread
    Thread.kill(@trackthread) if @trackthread
    @socket_ready = false
end

#to_statusstring

Returns status info string containing nr satellites, fix, speed.

Returns:

  • (string)

    status info string containing nr satellites, fix, speed



87
88
89
90
91
92
93
# File 'lib/gps2json.rb', line 87

def to_status
    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
    return "lat: #{last['lat']}, lng: #{last['lon']}, speed:#{last['speed']}" if @socket_ready and @last and @sats.nil?
    return "sats: #{@sats.length}(#{@sats.count{|sat| sat['used']}}), no fix yet" if @socket_ready and @last.nil? and @sats
    return "connected with gpsd, waiting for data" if @socket_ready
    return "waiting for connection with gpsd" if @socket_ready == false
end