ruby-gps 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 16bbfb313f9945b300c9fd352de126e1ffe869d98ad3ae7aec7f2e196ec72e28
4
+ data.tar.gz: 8701cb221f89141aee9beea05f6c9d04860e628e97f35a314f1cd08731f71f1f
5
+ SHA512:
6
+ metadata.gz: 786fd55095f2b19cc5aef507ae50de65022b6f9b70a475564702d25be4f67767cdbc1ed4a2fa40945b7b353d4a7705c27662a0bb268e32fc4d6a6a31ca86d200
7
+ data.tar.gz: 3ce9fc1152771455ff1f4531e949c23c7e1fa1ad03a08958ef652f69909e5917743f1ae0ce22be17663d4dfc551fc5c6612836513133fb28229288b9e99873da
data/lib/gps.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative 'gps/nmea'
2
+ require_relative 'gps/device'
data/lib/gps/device.rb ADDED
@@ -0,0 +1,163 @@
1
+ module Gps
2
+ class Device
3
+ attr_accessor :last_datetime # Updates any time a sentence has a datetime
4
+
5
+ def initialize(filename, track_datetime=true)
6
+ @callbacks = {}
7
+ @track_datetime = track_datetime
8
+ @filename = filename
9
+ @file = File.open(filename, 'rb')
10
+ end
11
+
12
+ def self.open(filename, &block)
13
+ device = Device.new filename
14
+ block.call device
15
+ device.close
16
+ end
17
+
18
+ def close
19
+ @file.close if @file
20
+ end
21
+
22
+ def each_line(&block)
23
+ @file.each_line do |line|
24
+ line.strip!
25
+ next if line.empty?
26
+ block.call line
27
+ end
28
+ end
29
+
30
+ def each_sentence(&block)
31
+ each_line do |line|
32
+ s = Nmea::Sentence.parse line
33
+ next if s.nil?
34
+ update_datetime s if @track_datetime
35
+ block.call s
36
+ end
37
+ end
38
+
39
+ def each_coordinates(&block)
40
+ each_sentence do |sentence|
41
+ block.call sentence.lat_long_dec if sentence.has_coordinates?
42
+ end
43
+ end
44
+
45
+ # Rewind file if not a character device
46
+ def rewind
47
+ @file.rewind unless File.chardev?(@file.path)
48
+ end
49
+
50
+ # Begin fetch process with callbacks
51
+ def start
52
+ @stopped = false
53
+ each_sentence do |sentence|
54
+ break if @stopped
55
+
56
+ if @callbacks[:coordinates] && sentence.has_coordinates?
57
+ @callbacks[:coordinates].each do |block|
58
+ block.call sentence.lat_long_dec
59
+ end
60
+ end
61
+
62
+
63
+ if @callbacks[:datetime] && sentence.has_datetime?
64
+ @callbacks[:datetime].each do |block|
65
+ block.call sentence.datetime
66
+ end
67
+ end
68
+
69
+ if @callbacks[:speed_knots] && sentence.respond_to?(:speed_knots)
70
+ @callbacks[:speed_knots].each do |block|
71
+ block.call sentence.speed_knots if sentence.speed_knots
72
+ end
73
+ end
74
+
75
+ if @callbacks[:speed_kmh] && sentence.respond_to?(:speed_kmh)
76
+ @callbacks[:speed_kmh].each do |block|
77
+ block.call sentence.speed_kmh if sentence.speed_kmh
78
+ end
79
+ end
80
+
81
+ if @callbacks[:altitude] && sentence.respond_to?(:altitude)
82
+ @callbacks[:altitude].each do |block|
83
+ block.call sentence.altitude if sentence.altitude
84
+ end
85
+ end
86
+
87
+ if @callbacks[:true_track] && sentence.respond_to?(:true_track)
88
+ @callbacks[:true_track].each do |block|
89
+ block.call sentence.true_track if sentence.true_track
90
+ end
91
+ end
92
+
93
+ if @callbacks[:magnetic_track] && sentence.respond_to?(:magnetic_track)
94
+ @callbacks[:magnetic_track].each do |block|
95
+ block.call sentence.magnetic_track if sentence.magnetic_track
96
+ end
97
+ end
98
+ end
99
+
100
+ if @callbacks[:complete]
101
+ @callbacks[:complete].each(&:call)
102
+ end
103
+ end
104
+
105
+ def start_async
106
+ Thread.new(&method(:start))
107
+ end
108
+
109
+ def stop
110
+ @stopped = true
111
+ end
112
+
113
+ def on_coordinates(&block)
114
+ callbacks[:coordinates] ||= []
115
+ callbacks[:coordinates] << block
116
+ end
117
+
118
+ def on_datetime(&block)
119
+ callbacks[:datetime] ||= []
120
+ callbacks[:datetime] << block
121
+ end
122
+
123
+ def on_speed_knots(&block)
124
+ callbacks[:speed_knots] ||= []
125
+ callbacks[:speed_knots] << block
126
+ end
127
+
128
+ def on_speed_kmh(&block)
129
+ callbacks[:speed_kmh] ||= []
130
+ callbacks[:speed_kmh] << block
131
+ end
132
+
133
+ def on_altitude(&block)
134
+ callbacks[:altitude] ||= []
135
+ callbacks[:altitude] << block
136
+ end
137
+
138
+ def on_complete(&block)
139
+ callbacks[:complete] ||= []
140
+ callbacks[:complete] << block
141
+ end
142
+
143
+ def on_true_track(&block)
144
+ callbacks[:true_track] ||= []
145
+ callbacks[:true_track] << block
146
+ end
147
+
148
+ def on_magnetic_track(&block)
149
+ callbacks[:magnetic_track] ||= []
150
+ callbacks[:magnetic_track] << block
151
+ end
152
+
153
+ private
154
+
155
+ attr_reader :callbacks
156
+
157
+ def update_datetime(sentence)
158
+ if sentence.respond_to? :datetime
159
+ @last_datetime = sentence.datetime
160
+ end
161
+ end
162
+ end
163
+ end
data/lib/gps/nmea.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Gps
2
+ module Nmea
3
+ require_relative 'nmea/sentence'
4
+ require_relative 'nmea/gga'
5
+ require_relative 'nmea/gll'
6
+ require_relative 'nmea/gsv'
7
+ require_relative 'nmea/gsa'
8
+ require_relative 'nmea/vtg'
9
+ require_relative 'nmea/rmc'
10
+ require_relative 'nmea/zda'
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ require 'time'
2
+
3
+ module Gps
4
+ module Nmea
5
+ class Gga < Sentence
6
+ attr_accessor :time, :time_str, :latitude, :latitude_direction, :longitude,
7
+ :longitude_direction, :fix_quality, :num_satellites, :hdop,
8
+ :altitude, :altitude_unit, :age_of_diff
9
+
10
+ def initialize(line)
11
+ @line = line
12
+ fill_parts
13
+ fill_data
14
+ end
15
+
16
+ private
17
+
18
+ def fill_data
19
+ @time_str = Sentence.time_part_to_str @parts[0]
20
+ @time = Time.parse time_str
21
+ @latitude = @parts[1].to_f
22
+ @latitude_direction = @parts[2]
23
+ @longitude = @parts[3].to_f
24
+ @longitude_direction = @parts[4]
25
+ @fix_quality = @parts[5].to_i
26
+ @num_satellites = @parts[6].to_i
27
+ @hdop = @parts[7].to_f
28
+ @altitude = @parts[8].to_f
29
+ @altitude_unit = @parts[9]
30
+ @age_of_diff = @parts[10].to_f
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ module Gps
2
+ module Nmea
3
+ class Gll < Sentence
4
+ attr_accessor :latitude, :latitude_direction, :longitude, :longitude_direction
5
+
6
+ def initialize(line)
7
+ @line = line
8
+ fill_parts
9
+ fill_data
10
+ end
11
+
12
+ private
13
+
14
+ def fill_data
15
+ @latitude = @parts[0].to_f
16
+ @latitude_direction = @parts[1]
17
+ @longitude = @parts[2].to_f
18
+ @longitude_direction = @parts[3]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module Gps
2
+ module Nmea
3
+ class Gsa < Sentence
4
+ attr_accessor :forced, :mode, :sv_id, :pdop, :hdop, :vdop
5
+
6
+ def initialize(line)
7
+ @line = line
8
+ fill_parts
9
+ fill_data
10
+ end
11
+
12
+ private
13
+
14
+ def fill_data
15
+ @forced = @parts[0] == 'M'
16
+ @mode = @parts[1].to_i
17
+ @sv_id = 0
18
+ (2..13).each do |i|
19
+ @sv_id += ((12 - i) * 100) * @parts[i].to_i
20
+ end
21
+ @pdop = @parts[14].to_f
22
+ @hdop = @parts[15].to_f
23
+ @vdop = @parts[16].to_f
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ module Gps
2
+ module Nmea
3
+ class Gsv < Sentence
4
+ attr_accessor :num_messages, :message_num, :total_svs, :sv_prn,
5
+ :elevation, :azimuth, :snr
6
+
7
+ def initialize(line)
8
+ @line = line
9
+ fill_parts
10
+ fill_data
11
+ end
12
+
13
+ private
14
+
15
+ def fill_data
16
+ @num_messages = @parts[0].to_i
17
+ @message_num = @parts[1].to_i
18
+ @total_svs = @parts[2].to_i
19
+ @sv_prn = @parts[3].to_i
20
+ @elevation = @parts[4].to_i
21
+ @azimuth = @parts[5].to_i
22
+ @snr = @parts[6].to_i
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require 'date'
2
+
3
+ module Gps
4
+ module Nmea
5
+ module HasDatetime
6
+ attr_accessor :date_str, :time_str, :datetime
7
+
8
+ def update_datetime
9
+ if @date_str && @time_str
10
+ @datetime = DateTime.iso8601 "#{@date_str}T#{@time_str}Z"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'has_datetime'
2
+
3
+ module Gps
4
+ module Nmea
5
+ class Rmc < Sentence
6
+ include HasDatetime
7
+
8
+ attr_accessor :data_status, :latitude, :latitude_direction,
9
+ :longitude, :longitude_direction, :speed_knots, :true_track,
10
+ :variation, :variation_direction
11
+
12
+ def initialize(line)
13
+ @line = line
14
+ fill_parts
15
+ fill_data
16
+ update_datetime
17
+ end
18
+
19
+ private
20
+
21
+ def fill_data
22
+ @time_str = Sentence.time_part_to_str @parts[0]
23
+ @data_status = @parts[1]
24
+ @latitude = @parts[2]
25
+ @latitude_direction = @parts[3]
26
+ @longitude = @parts[4]
27
+ @longitude_direction = @parts[5]
28
+ @speed_knots = @parts[6]
29
+ @true_track = @parts[7]
30
+ @date_str = Sentence.date_part_to_str @parts[8]
31
+ @variation = @parts[9]
32
+ @variation_direction = @parts[10]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,111 @@
1
+ module Gps
2
+ module Nmea
3
+ class Sentence
4
+ attr_accessor :line
5
+
6
+ def self.parse(line)
7
+ case
8
+ when line.start_with?('$GPGGA')
9
+ Gga.new line
10
+ when line.start_with?('$GPGLL')
11
+ Gll.new line
12
+ when line.start_with?('$GPGSV')
13
+ Gsv.new line
14
+ when line.start_with?('$GPGSA')
15
+ Gsa.new line
16
+ when line.start_with?('$GPVTG')
17
+ Vtg.new line
18
+ when line.start_with?('$GPRMC')
19
+ Rmc.new line
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ def self.time_part_to_str(time_part)
26
+ "#{time_part[0..1]}:#{time_part[2..3]}:#{time_part[4..5]}"
27
+ end
28
+
29
+ def self.date_part_to_str(date_part)
30
+ century = date_part[4..5].to_i > 70 ? 1900 : 2000
31
+ "#{century + date_part[4..5].to_i}-#{date_part[2..3]}-#{date_part[0..1]}"
32
+ end
33
+
34
+ def checksum_valid?
35
+ get_checksum == generate_checksum
36
+ end
37
+
38
+ def get_checksum
39
+ line.split(',')[-1].split('*')[1].to_i 16
40
+ end
41
+
42
+ def generate_checksum
43
+ data = line.gsub('$', '').split('*')[0]
44
+ res = 0
45
+ data.split('').each do |c|
46
+ res ^= c.ord
47
+ end
48
+ res
49
+ end
50
+
51
+ def lat_dec
52
+ return nil if @latitude.nil?
53
+ return nil if @latitude_direction.nil?
54
+
55
+ lat, lat_dec = @latitude.to_s.split '.'
56
+ lat = lat.rjust 4, '0'
57
+ res = lat[0..1].to_i + ("#{lat[2..3]}.#{lat_dec}".to_f / 60.0)
58
+ res *= -1 if @latitude_direction == 'S'
59
+ res
60
+ end
61
+
62
+ def long_dec
63
+ return nil if @longitude.nil?
64
+ return nil if @longitude_direction.nil?
65
+
66
+ long, long_dec = @longitude.to_s.split '.'
67
+ long = long.rjust 5, '0'
68
+ res = long[0..2].to_i + ("#{long[3..4]}.#{long_dec}".to_f / 60.0)
69
+ res *= -1 if @longitude_direction == 'W'
70
+ res
71
+ end
72
+
73
+ def lat_long_dec
74
+ return nil unless has_coordinates?
75
+ "#{lat_dec} #{long_dec}"
76
+ end
77
+
78
+ def to_h
79
+ self.class.instance_methods(false)
80
+ .reject { |a| a.to_s.end_with? '=' }
81
+ .map { |a| [a.to_s, self.send(a)] }
82
+ .to_h.merge({ type: self.class.to_s.split('::').last.upcase })
83
+ end
84
+
85
+ def to_json(pretty=false)
86
+ require 'json'
87
+ if pretty
88
+ JSON.pretty_generate to_h
89
+ else
90
+ to_h.to_json
91
+ end
92
+ end
93
+
94
+ def has_coordinates?
95
+ !(@latitude.nil? || @longitude.nil? ||
96
+ @latitude_direction.nil? || @longitude_direction.nil?)
97
+ end
98
+
99
+ def has_datetime?
100
+ respond_to? :datetime
101
+ end
102
+
103
+ protected
104
+
105
+ def fill_parts
106
+ @parts = @line.split(',')[1..]
107
+ @parts[-1].gsub! /\*.+$/, ''
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,22 @@
1
+ module Gps
2
+ module Nmea
3
+ class Vtg < Sentence
4
+ attr_accessor :true_track, :magnetic_track, :speed_knots, :speed_kmh
5
+
6
+ def initialize(line)
7
+ @line = line
8
+ fill_parts
9
+ fill_data
10
+ end
11
+
12
+ private
13
+
14
+ def fill_data
15
+ @true_track = @parts[0].to_f
16
+ @magnetic_track = @parts[2].to_f
17
+ @speed_knots = @parts[4].to_f
18
+ @speed_kmh = @parts[6].to_f
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module Gps
2
+ module Nmea
3
+ class Zda < Sentence
4
+ attr_accessor :hour, :minute, :second, :day, :month, :year,
5
+ :local_zone_desc, :local_zone_minutes
6
+
7
+ def initialize(line)
8
+ @line = line
9
+ fill_parts
10
+ fill_data
11
+ end
12
+
13
+ private
14
+
15
+ def fill_data
16
+ hms = @parts[0]
17
+ @hour = hms[0..1].to_i
18
+ @minute = hms[2..3].to_i
19
+ @second = hms[4..5].to_i
20
+ @day = @parts[1].to_i
21
+ @month = @parts[2].to_i
22
+ @year = @parts[3].to_i
23
+ @local_zone_desc = @parts[4].to_i
24
+ @local_zone_minutes = @parts[5].to_i
25
+ end
26
+ end
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-gps
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Nikola Miljkovic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: nikomil@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/gps.rb
20
+ - lib/gps/device.rb
21
+ - lib/gps/nmea.rb
22
+ - lib/gps/nmea/gga.rb
23
+ - lib/gps/nmea/gll.rb
24
+ - lib/gps/nmea/gsa.rb
25
+ - lib/gps/nmea/gsv.rb
26
+ - lib/gps/nmea/has_datetime.rb
27
+ - lib/gps/nmea/rmc.rb
28
+ - lib/gps/nmea/sentence.rb
29
+ - lib/gps/nmea/vtg.rb
30
+ - lib/gps/nmea/zda.rb
31
+ homepage:
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.0.1
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: GPS utilities for Ruby
54
+ test_files: []