ruby-gps 0.0.3

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.
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: []