nmea_plus 1.0.3 → 1.0.4
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 +4 -4
- data/README.md +1 -0
- data/lib/nmea_plus.rb +2 -0
- data/lib/nmea_plus/message/ais/base_ais.rb +7 -0
- data/lib/nmea_plus/message/ais/vdm.rb +17 -4
- data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg.rb +72 -13
- data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg1.rb +12 -0
- data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb +2 -0
- data/lib/nmea_plus/message/base.rb +10 -10
- data/lib/nmea_plus/message/nmea/base_nmea.rb +19 -4
- data/lib/nmea_plus/message/nmea/bwr.rb +4 -0
- data/lib/nmea_plus/message/nmea/dtm.rb +4 -0
- data/lib/nmea_plus/message/nmea/gga.rb +4 -0
- data/lib/nmea_plus/message/nmea/gll.rb +5 -0
- data/lib/nmea_plus/message/nmea/gns.rb +4 -0
- data/lib/nmea_plus/message/nmea/gxa.rb +5 -0
- data/lib/nmea_plus/message/nmea/hdg.rb +4 -0
- data/lib/nmea_plus/message/nmea/rma.rb +6 -0
- data/lib/nmea_plus/message/nmea/rmb.rb +4 -0
- data/lib/nmea_plus/message/nmea/rmc.rb +8 -0
- data/lib/nmea_plus/message/nmea/rte.rb +3 -0
- data/lib/nmea_plus/message/nmea/sfi.rb +2 -0
- data/lib/nmea_plus/message/nmea/trf.rb +6 -0
- data/lib/nmea_plus/message/nmea/vtg.rb +9 -0
- data/lib/nmea_plus/message/nmea/wpl.rb +4 -0
- data/lib/nmea_plus/message/nmea/zda.rb +3 -1
- data/lib/nmea_plus/nmea_message_factory.rb +2 -2
- data/lib/nmea_plus/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a207c93360fededdabcaff54e1728e9ab275b344
|
4
|
+
data.tar.gz: 1085b632c37d05ef034c64804283d376417f55d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8a9716472c3c20a86cf6daaadecc78aa06301d8b3f37b1e9141ec2d05c5135f2dd5f5bc70e5e2f09339e59a0e2fe30c8773805ea18e3aa59887dd5b54f109cf
|
7
|
+
data.tar.gz: 005a20dcb935adb5fd9d0762ed40b94a9c6950ea7dc2b1c8b914fe7631bbb005cc305a825f74b931c25bcb2d97908d87e657393f38c585e0e0973549ad6c1c7c
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/nmea_plus)
|
4
4
|
[](https://travis-ci.org/ifreecarve/nmea_plus)
|
5
|
+
[](http://rubydoc.org/gems/nmea_plus/frames)
|
5
6
|
|
6
7
|
[NMEA Plus](https://github.com/ifreecarve/nmea_plus) is a Ruby gem for parsing and decoding "GPS" messages: NMEA, AIS, and any other similar formats of short messaging typically used by marine equipment. It provides convenient access (by name) to the fields of each message type, and a stream reader designed for use with Ruby Blocks.
|
7
8
|
|
data/lib/nmea_plus.rb
CHANGED
@@ -4,6 +4,8 @@ require 'nmea_plus/generated_parser/parser'
|
|
4
4
|
require 'nmea_plus/generated_parser/tokenizer'
|
5
5
|
|
6
6
|
# NMEAPlus contains classes for parsing and decoding NMEA and AIS messages.
|
7
|
+
# You probably want to check out {NMEAPlus::Message::NMEA::NMEAMessage}
|
8
|
+
# and {NMEAPlus::Message::AIS::AISMessage}.
|
7
9
|
# @author Ian Katz
|
8
10
|
module NMEAPlus
|
9
11
|
|
@@ -3,13 +3,20 @@ require_relative "../base"
|
|
3
3
|
|
4
4
|
module NMEAPlus
|
5
5
|
module Message
|
6
|
+
# A container for all {AISMessage} types.
|
6
7
|
module AIS
|
7
8
|
|
8
9
|
class AISMessage < NMEAPlus::Message::Base
|
10
|
+
# NMEA (AIS) message types are 5 characters, the first 2 of which are the talker ID
|
11
|
+
# @!parse attr_accessor :talker
|
12
|
+
# @return [String] The two-character "talker ID" of the message
|
9
13
|
def talker
|
10
14
|
data_type[0..1]
|
11
15
|
end
|
12
16
|
|
17
|
+
# NMEA (AIS) message types are 5 characters (or so), the last of which are the message type
|
18
|
+
# @!parse attr_accessor :message_type
|
19
|
+
# @return [String] The two-character "talker ID" of the message
|
13
20
|
def message_type
|
14
21
|
data_type[2..-1]
|
15
22
|
end
|
@@ -21,7 +21,6 @@ require_relative "vdm_payload/vdm_msg1"
|
|
21
21
|
require_relative "vdm_payload/vdm_msg5"
|
22
22
|
require_relative "vdm_payload/vdm_msg8"
|
23
23
|
|
24
|
-
|
25
24
|
module NMEAPlus
|
26
25
|
module Message
|
27
26
|
module AIS
|
@@ -33,8 +32,10 @@ module NMEAPlus
|
|
33
32
|
field_reader :raw_ais_payload, 5, :_string
|
34
33
|
field_reader :ais_payload_fill_bits, 6, :_integer
|
35
34
|
|
35
|
+
# factory method: find the appropriate message type class and instantiate it
|
36
|
+
# @!parse attr_reader :ais
|
37
|
+
# @return [VDMPayload::VDMMsg]
|
36
38
|
def ais
|
37
|
-
# factory method: find the appropriate message type class and instantiate it
|
38
39
|
p = full_dearmored_ais_payload
|
39
40
|
ret = _payload_container(p[0, 6].to_i(2))
|
40
41
|
ret.payload_bitstring = p
|
@@ -42,7 +43,9 @@ module NMEAPlus
|
|
42
43
|
ret
|
43
44
|
end
|
44
45
|
|
45
|
-
# the full encoded payload as it was received
|
46
|
+
# the full encoded payload as it was received -- spanning multiple messages
|
47
|
+
# @!parse attr_reader :full_armored_ais_payload
|
48
|
+
# @return [String]
|
46
49
|
def full_armored_ais_payload
|
47
50
|
# get the full message and fill bits for the last one
|
48
51
|
ptr = self
|
@@ -56,6 +59,8 @@ module NMEAPlus
|
|
56
59
|
end
|
57
60
|
|
58
61
|
# a binary string ("0010101110110") representing the dearmored payload
|
62
|
+
# @!parse attr_reader :full_dearmored_ais_payload
|
63
|
+
# @return [String]
|
59
64
|
def full_dearmored_ais_payload
|
60
65
|
data = full_armored_ais_payload
|
61
66
|
out = ""
|
@@ -65,8 +70,10 @@ module NMEAPlus
|
|
65
70
|
out
|
66
71
|
end
|
67
72
|
|
73
|
+
# Get the fill bits for the last message in the sequence -- the only one that matters
|
74
|
+
# @!parse attr_reader :last_ais_fill_bits
|
75
|
+
# @return [Integer]
|
68
76
|
def last_ais_fill_bits
|
69
|
-
# get the fill bits for the last message in the sequence
|
70
77
|
ptr = self
|
71
78
|
fill_bits = nil
|
72
79
|
loop do
|
@@ -78,6 +85,9 @@ module NMEAPlus
|
|
78
85
|
end
|
79
86
|
|
80
87
|
# perform the 6-bit to 8-bit conversion defined in the spec
|
88
|
+
# @param c [String] a character
|
89
|
+
# @param len [Integer] The number of bits to consider
|
90
|
+
# @return [String] a binary encoded string
|
81
91
|
def _dearmor6b(c, len = 6)
|
82
92
|
val = c.ord
|
83
93
|
if val >= 96
|
@@ -88,6 +98,9 @@ module NMEAPlus
|
|
88
98
|
ret.to_s(2).rjust(6, "0")[0..(len - 1)]
|
89
99
|
end
|
90
100
|
|
101
|
+
# Find an appropriate payload container for the payload type, based on its stated message ID
|
102
|
+
# @param message_type_id [String]
|
103
|
+
# @return [NMEAPlus::Message::AIS::VDMPayload::VDMMsg] The parsed payload
|
91
104
|
def _payload_container(message_type_id)
|
92
105
|
class_identifier = "NMEAPlus::Message::AIS::VDMPayload::VDMMsg#{message_type_id}"
|
93
106
|
Object::const_get(class_identifier).new
|
@@ -1,69 +1,111 @@
|
|
1
1
|
class Class
|
2
|
-
# make our own shortcut syntax for payload attributes
|
3
|
-
def payload_reader(name, start_bit, length, formatter, formatter_arg = nil)
|
4
|
-
if formatter_arg.nil?
|
5
|
-
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length});end")
|
6
|
-
else
|
7
|
-
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length}, #{formatter_arg});end")
|
8
|
-
end
|
9
|
-
end
|
10
2
|
end
|
11
3
|
|
12
4
|
module NMEAPlus
|
13
5
|
module Message
|
14
6
|
module AIS
|
7
|
+
# There are many VDM payload types, and this is their container. See {VDMMsg}.
|
15
8
|
module VDMPayload
|
9
|
+
# The base class for the AIS payload (of {NMEAPlus::Message::AIS::VDM}) which can be of many types.
|
16
10
|
class VDMMsg
|
11
|
+
# @return [String] The raw "armored payload" in the original message
|
17
12
|
attr_accessor :payload_bitstring
|
13
|
+
|
14
|
+
# @return [Integer] The number of padding characters required to bring the payload to a 6 bit boundary
|
18
15
|
attr_accessor :fill_bits
|
19
16
|
|
17
|
+
# make our own shortcut syntax for payload attributes
|
18
|
+
# @param name [String] What the accessor will be called
|
19
|
+
# @param start_bit [Integer] The index of first bit of this field in the payload
|
20
|
+
# @param length [Integer] The number of bits in this field
|
21
|
+
# @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional)
|
22
|
+
# @param formatter_arg Any argument necessary for the formatting function
|
23
|
+
# @macro [attach] payload_reader
|
24
|
+
# @!attribute [r] $1
|
25
|
+
# @return The field defined by $3 bits starting at bit $2 of the payload, formatted with the function {#$4}($5)
|
26
|
+
def self.payload_reader(name, start_bit, length, formatter, formatter_arg = nil)
|
27
|
+
if formatter_arg.nil?
|
28
|
+
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length});end")
|
29
|
+
else
|
30
|
+
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length}, #{formatter_arg});end")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
20
34
|
payload_reader :message_type, 0, 6, :_u
|
21
35
|
payload_reader :repeat_indicator, 6, 2, :_u
|
22
36
|
payload_reader :source_mmsi, 8, 30, :_u
|
23
37
|
|
24
|
-
#
|
38
|
+
# Convert 6-bit ascii to a character, according to http://catb.org/gpsd/AIVDM.html#_ais_payload_data_types
|
39
|
+
# @param ord [Integer] The 6-bit ascii code
|
40
|
+
# @return [String] the character for that code
|
25
41
|
def _6b_ascii(ord)
|
26
42
|
'@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
|
27
43
|
end
|
28
44
|
|
29
|
-
#
|
45
|
+
# Access part of the payload. If there aren't bytes, there, return nil
|
30
46
|
# else execute a block
|
47
|
+
# @param start [Integer] The index of the first bit in the payload field
|
48
|
+
# @param length [Integer] The number of bits in the payload field
|
49
|
+
# @return Nil or whatever is yielded by the block
|
50
|
+
# @yield [String] A binary coded string ("010010101" etc)
|
31
51
|
def _access(start, length)
|
32
52
|
part = @payload_bitstring[start, length]
|
33
53
|
return nil if part.nil? || part.empty?
|
34
54
|
yield part
|
35
55
|
end
|
36
56
|
|
37
|
-
#
|
57
|
+
# pull out 6b chunks from the payload, then convert those to their more familar characters
|
58
|
+
# @param start [Integer] The index of the first bit in the payload field
|
59
|
+
# @param length [Integer] The number of bits in the payload field
|
60
|
+
# @return [String]
|
38
61
|
def _6b_string(start, length)
|
39
|
-
# pull out 6b chunks from the string, use their value as a lookup into the ascii array
|
40
62
|
_bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
|
41
63
|
end
|
42
64
|
|
65
|
+
# pull out 8b chunks from the payload, then convert those to their more familar characters
|
66
|
+
# @param start [Integer] The index of the first bit in the payload field
|
67
|
+
# @param length [Integer] The number of bits in the payload field
|
68
|
+
# @return [String]
|
43
69
|
def _8b_data_string(start, length)
|
44
70
|
_bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
|
45
71
|
end
|
46
72
|
|
73
|
+
# Slice a part of the payload into binary chunks of a given size
|
74
|
+
# @param start [Integer] The index of the first bit in the payload field
|
75
|
+
# @param length [Integer] The number of bits in the payload field
|
76
|
+
# @return [Array<String>] Strings representing binary ("01010101" etc) for each slice
|
47
77
|
def _bit_slices(start, length, chunk_size)
|
48
78
|
_access(start, length) { |bits| bits.chars.each_slice(chunk_size) }
|
49
79
|
end
|
50
80
|
|
51
|
-
# convert a string but trim off the 0s ('@')
|
81
|
+
# convert a 6b string but trim off the 0s ('@')
|
82
|
+
# @param start [Integer] The index of the first bit in the payload field
|
83
|
+
# @param length [Integer] The number of bits in the payload field
|
84
|
+
# @return [String]
|
52
85
|
def _6b_string_nullterminated(start, length)
|
53
86
|
_6b_string(start, length).split("@", 2)[0]
|
54
87
|
end
|
55
88
|
|
56
89
|
# directly convert a string to a binary number as you'd read it
|
90
|
+
# @param start [Integer] The index of the first bit in the payload field
|
91
|
+
# @param length [Integer] The number of bits in the payload field
|
92
|
+
# @return [Integer] an unsigned integer value
|
57
93
|
def _6b_unsigned_integer(start, length)
|
58
94
|
_access(start, length) { |bits| bits.to_i(2) }
|
59
95
|
end
|
60
96
|
|
61
97
|
# perform a twos complement operation on part of the payload
|
98
|
+
# @param start [Integer] The index of the first bit in the payload field
|
99
|
+
# @param length [Integer] The number of bits in the payload field
|
100
|
+
# @return [Integer] an integer value
|
62
101
|
def _6b_twoscomplement(start, length)
|
63
102
|
# two's complement: flip bits, then add 1
|
64
103
|
_access(start, length) { |bits| bits.tr("01", "10").to_i(2) + 1 }
|
65
104
|
end
|
66
105
|
|
106
|
+
# @param start [Integer] The index of the first bit in the payload field
|
107
|
+
# @param length [Integer] The number of bits in the payload field
|
108
|
+
# @return [Integer] an integer value
|
67
109
|
def _6b_integer(start, length)
|
68
110
|
# MSB is 1 for negative
|
69
111
|
twoc = _6b_twoscomplement(start, length)
|
@@ -71,19 +113,35 @@ module NMEAPlus
|
|
71
113
|
end
|
72
114
|
|
73
115
|
# scale an integer by dividing it by 10^decimal_places
|
116
|
+
# @param start [Integer] The index of the first bit in the payload field
|
117
|
+
# @param length [Integer] The number of bits in the payload field
|
118
|
+
# @param decimal_places [Integer] The power of ten to use in scaling the result
|
119
|
+
# @return [Integer] an integer value
|
74
120
|
def _6b_integer_scaled(start, length, decimal_places)
|
75
121
|
_6b_integer(start, length).to_f / (10 ** decimal_places)
|
76
122
|
end
|
77
123
|
|
78
124
|
# scale an unsigned integer by dividing it by 10^decimal_places
|
125
|
+
# @param start [Integer] The index of the first bit in the payload field
|
126
|
+
# @param length [Integer] The number of bits in the payload field
|
127
|
+
# @param decimal_places [Integer] The power of ten to use in scaling the result
|
128
|
+
# @return [Integer] an integer value
|
79
129
|
def _6b_unsigned_integer_scaled(start, length, decimal_places)
|
80
130
|
_6b_unsigned_integer(start, length).to_f / (10.0 ** decimal_places)
|
81
131
|
end
|
82
132
|
|
133
|
+
# Get the value of a bit in the payload
|
134
|
+
# @param start [Integer] The index of the first bit in the payload field
|
135
|
+
# @param _ [Integer] Doesn't matter. Here so signatures match; we hard-code 1 because it's 1 bit.
|
136
|
+
# @return [bool]
|
83
137
|
def _6b_boolean(start, _)
|
84
138
|
_access(start, 1) { |bits| bits.to_i == 1 }
|
85
139
|
end
|
86
140
|
|
141
|
+
# Return a string representing binary
|
142
|
+
# @param start [Integer] The index of the first bit in the payload field
|
143
|
+
# @param length [Integer] The number of bits in the payload field
|
144
|
+
# @return [String] e.g. "0101010101011000"
|
87
145
|
def _2b_data_string(start, length)
|
88
146
|
_access(start, length)
|
89
147
|
end
|
@@ -100,6 +158,7 @@ module NMEAPlus
|
|
100
158
|
|
101
159
|
end
|
102
160
|
|
161
|
+
# We haven't defined all the AIS payload types, so this is a catch-all
|
103
162
|
class VDMMsgUndefined < VDMMsg; end
|
104
163
|
|
105
164
|
end
|
@@ -4,10 +4,13 @@ module NMEAPlus
|
|
4
4
|
module Message
|
5
5
|
module AIS
|
6
6
|
module VDMPayload
|
7
|
+
# CNB - The Common Navigation Block, transmitted by AIS messages 1, 2, and 3.
|
7
8
|
class VDMMsgCNB < NMEAPlus::Message::AIS::VDMPayload::VDMMsg
|
8
9
|
|
9
10
|
payload_reader :navigational_status, 38, 4, :_u
|
10
11
|
|
12
|
+
# @!parse attr_reader :navigational_status_description
|
13
|
+
# @return [String] the human-readable description of navigational status
|
11
14
|
def navigational_status_description
|
12
15
|
case navigational_status
|
13
16
|
when 0 then return "Under way using engine"
|
@@ -25,6 +28,9 @@ module NMEAPlus
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
# The rate of turn in degrees per minute
|
32
|
+
# @!parse attr_reader :rate_of_turn
|
33
|
+
# @return [Float]
|
28
34
|
def rate_of_turn
|
29
35
|
ret = _i(42, 8)
|
30
36
|
return nil if ret == -128
|
@@ -34,16 +40,22 @@ module NMEAPlus
|
|
34
40
|
payload_reader :speed_over_ground, 50, 10, :_U, 1
|
35
41
|
payload_reader :position_10m_accuracy?, 60, 1, :_b
|
36
42
|
|
43
|
+
# @!parse attr_reader :longitude
|
44
|
+
# @return [Float]
|
37
45
|
def longitude
|
38
46
|
_I(61, 28, 4) / 60
|
39
47
|
end
|
40
48
|
|
49
|
+
# @!parse attr_reader :latitude
|
50
|
+
# @return [Float]
|
41
51
|
def latitude
|
42
52
|
_U(89, 27, 4) / 60
|
43
53
|
end
|
44
54
|
|
45
55
|
payload_reader :course_over_ground, 116, 12, :_U, 1
|
46
56
|
|
57
|
+
# @!parse attr_reader :true_heading
|
58
|
+
# @return [Float]
|
47
59
|
def true_heading
|
48
60
|
ret = _u(128, 9)
|
49
61
|
return nil if ret == 511 # means "not available"
|
@@ -12,8 +12,8 @@ module NMEAPlus
|
|
12
12
|
# @param name [String] What the accessor will be called
|
13
13
|
# @param field_num [Integer] The index of the field in the payload
|
14
14
|
# @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional)
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# @macro [attach] field_reader
|
16
|
+
# @!attribute [r] $1
|
17
17
|
# @return field $2 of the payload, formatted with the function {#$3}
|
18
18
|
def self.field_reader(name, field_num, formatter = nil)
|
19
19
|
if formatter.nil?
|
@@ -149,7 +149,7 @@ module NMEAPlus
|
|
149
149
|
end
|
150
150
|
|
151
151
|
# integer or nil
|
152
|
-
# @param field [String] the field
|
152
|
+
# @param field [String] the value in the field to be checked
|
153
153
|
# @return [Integer] The value in the field or nil
|
154
154
|
def _integer(field)
|
155
155
|
return nil if field.nil? || field.empty?
|
@@ -157,7 +157,7 @@ module NMEAPlus
|
|
157
157
|
end
|
158
158
|
|
159
159
|
# float or nil
|
160
|
-
# @param field [String] the field
|
160
|
+
# @param field [String] the value in the field to be checked
|
161
161
|
# @return [Float] The value in the field or nil
|
162
162
|
def _float(field)
|
163
163
|
return nil if field.nil? || field.empty?
|
@@ -165,7 +165,7 @@ module NMEAPlus
|
|
165
165
|
end
|
166
166
|
|
167
167
|
# string or nil
|
168
|
-
# @param field [String] the field
|
168
|
+
# @param field [String] the value in the field to be checked
|
169
169
|
# @return [String] The value in the field or nil
|
170
170
|
def _string(field)
|
171
171
|
return nil if field.nil? || field.empty?
|
@@ -173,7 +173,7 @@ module NMEAPlus
|
|
173
173
|
end
|
174
174
|
|
175
175
|
# hex to int or nil
|
176
|
-
# @param field [String] the field
|
176
|
+
# @param field [String] the value in the field to be checked
|
177
177
|
# @return [Integer] The value in the field or nil
|
178
178
|
def _hex_to_integer(field)
|
179
179
|
return nil if field.nil? || field.empty?
|
@@ -181,7 +181,7 @@ module NMEAPlus
|
|
181
181
|
end
|
182
182
|
|
183
183
|
# utc time or nil (HHMMSS or HHMMSS.SS)
|
184
|
-
# @param field [String] the field
|
184
|
+
# @param field [String] the value in the field to be checked
|
185
185
|
# @return [Time] The value in the field or nil
|
186
186
|
def _utctime_hms(field)
|
187
187
|
return nil if field.nil? || field.empty?
|
@@ -196,7 +196,7 @@ module NMEAPlus
|
|
196
196
|
end
|
197
197
|
|
198
198
|
# time interval or nil (HHMMSS or HHMMSS.SS)
|
199
|
-
# @param field [String] the field
|
199
|
+
# @param field [String] the value in the field to be checked
|
200
200
|
# @return [Time] The value in the field or nil
|
201
201
|
def _interval_hms(field)
|
202
202
|
return nil if field.nil? || field.empty?
|
@@ -209,8 +209,8 @@ module NMEAPlus
|
|
209
209
|
end
|
210
210
|
end
|
211
211
|
|
212
|
-
# @param d_field [String] the date field
|
213
|
-
# @param t_field [String] the time field
|
212
|
+
# @param d_field [String] the date value in the field to be checked
|
213
|
+
# @param t_field [String] the time value in the field to be checked
|
214
214
|
# @return [Time] The value in the fields, or nil if either is not provided
|
215
215
|
def _utc_date_time(d_field, t_field)
|
216
216
|
return nil if t_field.nil? || t_field.empty?
|
@@ -3,27 +3,42 @@ require_relative "../base"
|
|
3
3
|
|
4
4
|
module NMEAPlus
|
5
5
|
module Message
|
6
|
+
# A container for all {NMEAMessage} typs.
|
6
7
|
module NMEA
|
7
8
|
|
9
|
+
# The base class for NMEA messages includes a few more NMEA-specific parsing functions
|
8
10
|
class NMEAMessage < NMEAPlus::Message::Base
|
11
|
+
|
12
|
+
# NMEA message types are 5 characters, the first 2 of which are the talker ID
|
13
|
+
# @!parse attr_accessor :talker
|
14
|
+
# @return [String] The two-character "talker ID" of the message
|
9
15
|
def talker
|
10
16
|
data_type[0..1]
|
11
17
|
end
|
12
18
|
|
19
|
+
# NMEA message types are 5 characters (or so), the last of which are the message type
|
20
|
+
# @!parse attr_accessor :message_type
|
21
|
+
# @return [String] The two-character "talker ID" of the message
|
13
22
|
def message_type
|
14
23
|
data_type[2..-1]
|
15
24
|
end
|
16
25
|
|
17
|
-
|
18
|
-
|
26
|
+
# Convert a string true/false (encoded as A=true, V=false) to boolean
|
27
|
+
# @param field [String] the value in the field to be checked
|
28
|
+
# @return [bool] The value in the field or nil
|
29
|
+
def _av_boolean(field)
|
30
|
+
case field
|
19
31
|
when 'A' then return true
|
20
32
|
when 'V' then return false
|
21
33
|
end
|
22
34
|
nil
|
23
35
|
end
|
24
36
|
|
25
|
-
|
26
|
-
|
37
|
+
# Convert a string true/false (encoded as 1=true, 0=false) to boolean
|
38
|
+
# @param field [String] the value in the field to be checked
|
39
|
+
# @return [bool] The value in the field or nil
|
40
|
+
def _10_boolean(field)
|
41
|
+
case field
|
27
42
|
when '1' then return true
|
28
43
|
when '0' then return false
|
29
44
|
end
|
@@ -6,10 +6,14 @@ module NMEAPlus
|
|
6
6
|
class BWR < NMEAPlus::Message::NMEA::NMEAMessage
|
7
7
|
field_reader :utc_time, 1, :_utctime_hms
|
8
8
|
|
9
|
+
# @!parse attr_reader :waypoint_latitude
|
10
|
+
# @return [Float]
|
9
11
|
def waypoint_latitude
|
10
12
|
_degrees_minutes_to_decimal(@fields[2], @fields[3])
|
11
13
|
end
|
12
14
|
|
15
|
+
# @!parse attr_reader :waypoint_longitude
|
16
|
+
# @return [Float]
|
13
17
|
def waypoint_longitude
|
14
18
|
_degrees_minutes_to_decimal(@fields[4], @fields[5])
|
15
19
|
end
|
@@ -7,10 +7,14 @@ module NMEAPlus
|
|
7
7
|
field_reader :code, 1, :_string
|
8
8
|
field_reader :subcode, 2, :_string
|
9
9
|
|
10
|
+
# @!parse attr_reader :latitude_offset
|
11
|
+
# @return [Float]
|
10
12
|
def latitude_offset
|
11
13
|
_nsew_signed_float(@fields[3], @fields[4])
|
12
14
|
end
|
13
15
|
|
16
|
+
# @!parse attr_reader :longitude_offset
|
17
|
+
# @return [Float]
|
14
18
|
def longitude_offset
|
15
19
|
_nsew_signed_float(@fields[5], @fields[6])
|
16
20
|
end
|
@@ -8,10 +8,14 @@ module NMEAPlus
|
|
8
8
|
class GGA < NMEAPlus::Message::NMEA::NMEAMessage
|
9
9
|
field_reader :fix_time, 1, :_utctime_hms
|
10
10
|
|
11
|
+
# @!parse attr_reader :latitude
|
12
|
+
# @return [Float]
|
11
13
|
def latitude
|
12
14
|
_degrees_minutes_to_decimal(@fields[2], @fields[3])
|
13
15
|
end
|
14
16
|
|
17
|
+
# @!parse attr_reader :longitude
|
18
|
+
# @return [Float]
|
15
19
|
def longitude
|
16
20
|
_degrees_minutes_to_decimal(@fields[4], @fields[5])
|
17
21
|
end
|
@@ -4,10 +4,15 @@ module NMEAPlus
|
|
4
4
|
module Message
|
5
5
|
module NMEA
|
6
6
|
class GLL < NMEAPlus::Message::NMEA::NMEAMessage
|
7
|
+
|
8
|
+
# @!parse attr_reader :latitude
|
9
|
+
# @return [Float]
|
7
10
|
def latitude
|
8
11
|
_degrees_minutes_to_decimal(@fields[1], @fields[2])
|
9
12
|
end
|
10
13
|
|
14
|
+
# @!parse attr_reader :longitude
|
15
|
+
# @return [Float]
|
11
16
|
def longitude
|
12
17
|
_degrees_minutes_to_decimal(@fields[3], @fields[4])
|
13
18
|
end
|
@@ -7,10 +7,14 @@ module NMEAPlus
|
|
7
7
|
class GNS < NMEAPlus::Message::NMEA::NMEAMessage
|
8
8
|
field_reader :fix_time, 1, :_utctime_hms
|
9
9
|
|
10
|
+
# @!parse attr_reader :latitude
|
11
|
+
# @return [Float]
|
10
12
|
def latitude
|
11
13
|
_degrees_minutes_to_decimal(@fields[2], @fields[3])
|
12
14
|
end
|
13
15
|
|
16
|
+
# @!parse attr_reader :longitude
|
17
|
+
# @return [Float]
|
14
18
|
def longitude
|
15
19
|
_degrees_minutes_to_decimal(@fields[4], @fields[5])
|
16
20
|
end
|
@@ -6,10 +6,15 @@ module NMEAPlus
|
|
6
6
|
class GXA < NMEAPlus::Message::NMEA::NMEAMessage
|
7
7
|
|
8
8
|
field_reader :fix_time, 1, :_utctime_hms
|
9
|
+
|
10
|
+
# @!parse attr_reader :latitude
|
11
|
+
# @return [Float]
|
9
12
|
def latitude
|
10
13
|
_degrees_minutes_to_decimal(@fields[2], @fields[3])
|
11
14
|
end
|
12
15
|
|
16
|
+
# @!parse attr_reader :longitude
|
17
|
+
# @return [Float]
|
13
18
|
def longitude
|
14
19
|
_degrees_minutes_to_decimal(@fields[4], @fields[5])
|
15
20
|
end
|
@@ -6,10 +6,14 @@ module NMEAPlus
|
|
6
6
|
module NMEA
|
7
7
|
class HDG < NMEAPlus::Message::NMEA::HDM
|
8
8
|
|
9
|
+
# @!parse attr_reader :magnetic_deviation_degrees
|
10
|
+
# @return [Float]
|
9
11
|
def magnetic_deviation_degrees
|
10
12
|
_nsew_signed_float(@fields[2], @fields[3])
|
11
13
|
end
|
12
14
|
|
15
|
+
# @!parse attr_reader :magnetic_variation_degrees
|
16
|
+
# @return [Float]
|
13
17
|
def magnetic_variation_degrees
|
14
18
|
_nsew_signed_float(@fields[4], @fields[5])
|
15
19
|
end
|
@@ -7,10 +7,14 @@ module NMEAPlus
|
|
7
7
|
|
8
8
|
field_reader :blink_warning, 1, :_av_boolean
|
9
9
|
|
10
|
+
# @!parse attr_reader :latitude
|
11
|
+
# @return [Float]
|
10
12
|
def latitude
|
11
13
|
_degrees_minutes_to_decimal(@fields[2], @fields[3])
|
12
14
|
end
|
13
15
|
|
16
|
+
# @!parse attr_reader :longitude
|
17
|
+
# @return [Float]
|
14
18
|
def longitude
|
15
19
|
_degrees_minutes_to_decimal(@fields[4], @fields[5])
|
16
20
|
end
|
@@ -20,6 +24,8 @@ module NMEAPlus
|
|
20
24
|
field_reader :speed_over_ground_knots, 8, :_float
|
21
25
|
field_reader :track_made_good_degrees_true, 9, :_float
|
22
26
|
|
27
|
+
# @!parse attr_reader :magnetic_variation_degrees
|
28
|
+
# @return [Float]
|
23
29
|
def magnetic_variation_degrees
|
24
30
|
_nsew_signed_float(@fields[10], @fields[11])
|
25
31
|
end
|
@@ -11,10 +11,14 @@ module NMEAPlus
|
|
11
11
|
field_reader :waypoint_to, 4, :_integer
|
12
12
|
field_reader :waypoint_from, 5, :_integer
|
13
13
|
|
14
|
+
# @!parse attr_reader :waypoint_latitude
|
15
|
+
# @return [Float]
|
14
16
|
def waypoint_latitude
|
15
17
|
_degrees_minutes_to_decimal(@fields[6], @fields[7])
|
16
18
|
end
|
17
19
|
|
20
|
+
# @!parse attr_reader :waypoint_longitude
|
21
|
+
# @return [Float]
|
18
22
|
def waypoint_longitude
|
19
23
|
_degrees_minutes_to_decimal(@fields[8], @fields[9])
|
20
24
|
end
|
@@ -5,16 +5,22 @@ module NMEAPlus
|
|
5
5
|
module NMEA
|
6
6
|
class RMC < NMEAPlus::Message::NMEA::NMEAMessage
|
7
7
|
|
8
|
+
# @!parse attr_reader :utc_time
|
9
|
+
# @return [Time]
|
8
10
|
def utc_time
|
9
11
|
_utc_date_time(@fields[9], @fields[1])
|
10
12
|
end
|
11
13
|
|
12
14
|
field_reader :active?, 2, :_av_boolean
|
13
15
|
|
16
|
+
# @!parse attr_reader :latitude
|
17
|
+
# @return [Float]
|
14
18
|
def latitude
|
15
19
|
_degrees_minutes_to_decimal(@fields[3], @fields[4])
|
16
20
|
end
|
17
21
|
|
22
|
+
# @!parse attr_reader :longitude
|
23
|
+
# @return [Float]
|
18
24
|
def longitude
|
19
25
|
_degrees_minutes_to_decimal(@fields[5], @fields[6])
|
20
26
|
end
|
@@ -22,6 +28,8 @@ module NMEAPlus
|
|
22
28
|
field_reader :speed_over_ground_knots, 7, :_float
|
23
29
|
field_reader :track_made_good_degrees_true, 8, :_float
|
24
30
|
|
31
|
+
# @!parse attr_reader :magnetic_variation_degrees
|
32
|
+
# @return [Float]
|
25
33
|
def magnetic_variation_degrees
|
26
34
|
_nsew_signed_float(@fields[10], @fields[11])
|
27
35
|
end
|
@@ -4,14 +4,20 @@ module NMEAPlus
|
|
4
4
|
module Message
|
5
5
|
module NMEA
|
6
6
|
class TRF < NMEAPlus::Message::NMEA::NMEAMessage
|
7
|
+
# @!parse attr_reader :utc_time
|
8
|
+
# @return [Time]
|
7
9
|
def utc_time
|
8
10
|
_utc_date_time(@fields[2], @fields[1])
|
9
11
|
end
|
10
12
|
|
13
|
+
# @!parse attr_reader :latitude
|
14
|
+
# @return [Float]
|
11
15
|
def latitude
|
12
16
|
_degrees_minutes_to_decimal(@fields[3], @fields[4])
|
13
17
|
end
|
14
18
|
|
19
|
+
# @!parse attr_reader :longitude
|
20
|
+
# @return [Float]
|
15
21
|
def longitude
|
16
22
|
_degrees_minutes_to_decimal(@fields[5], @fields[6])
|
17
23
|
end
|
@@ -5,30 +5,39 @@ module NMEAPlus
|
|
5
5
|
module NMEA
|
6
6
|
class VTG < NMEAPlus::Message::NMEA::NMEAMessage
|
7
7
|
# whether this is the new format. docs say check field #2 for value "2"
|
8
|
+
# @return [bool]
|
8
9
|
def new_format?
|
9
10
|
'T' == @fields[2]
|
10
11
|
end
|
11
12
|
|
12
13
|
field_reader :track_degrees_true, 1, :_float
|
13
14
|
|
15
|
+
# @!parse attr_reader :track_degrees_magnetic
|
16
|
+
# @return [Float]
|
14
17
|
def track_degrees_magnetic
|
15
18
|
f = new_format? ? 3 : 2
|
16
19
|
return nil if @fields[f].nil? || @fields[f].empty?
|
17
20
|
@fields[f].to_f
|
18
21
|
end
|
19
22
|
|
23
|
+
# @!parse attr_reader :speed_knots
|
24
|
+
# @return [Float]
|
20
25
|
def speed_knots
|
21
26
|
f = new_format? ? 5 : 3
|
22
27
|
return nil if @fields[f].nil? || @fields[f].empty?
|
23
28
|
@fields[f].to_f
|
24
29
|
end
|
25
30
|
|
31
|
+
# @!parse attr_reader :speed_kmh
|
32
|
+
# @return [Float]
|
26
33
|
def speed_kmh
|
27
34
|
f = new_format? ? 7 : 4
|
28
35
|
return nil if @fields[f].nil? || @fields[f].empty?
|
29
36
|
@fields[f].to_f
|
30
37
|
end
|
31
38
|
|
39
|
+
# @!parse attr_reader :faa_mode
|
40
|
+
# @return [String]
|
32
41
|
def faa_mode
|
33
42
|
f = new_format? ? 9 : 100
|
34
43
|
return nil if @fields[f].nil? || @fields[f].empty?
|
@@ -4,10 +4,14 @@ module NMEAPlus
|
|
4
4
|
module Message
|
5
5
|
module NMEA
|
6
6
|
class WPL < NMEAPlus::Message::NMEA::NMEAMessage
|
7
|
+
# @!parse attr_reader :latitude
|
8
|
+
# @return [Float]
|
7
9
|
def latitude
|
8
10
|
_degrees_minutes_to_decimal(@fields[1], @fields[2])
|
9
11
|
end
|
10
12
|
|
13
|
+
# @!parse attr_reader :longitude
|
14
|
+
# @return [Float]
|
11
15
|
def longitude
|
12
16
|
_degrees_minutes_to_decimal(@fields[3], @fields[4])
|
13
17
|
end
|
@@ -4,8 +4,10 @@ module NMEAPlus
|
|
4
4
|
module Message
|
5
5
|
module NMEA
|
6
6
|
class ZDA < NMEAPlus::Message::NMEA::NMEAMessage
|
7
|
-
def utc_time
|
8
7
|
|
8
|
+
# @!parse attr_reader :utc_time
|
9
|
+
# @return [Time]
|
10
|
+
def utc_time
|
9
11
|
re_format = /(\d{2})(\d{2})(\d{2}(\.\d+)?)/
|
10
12
|
hms = re_format.match(@fields[1])
|
11
13
|
begin
|
@@ -79,6 +79,8 @@ require_relative 'message/nmea/ztg'
|
|
79
79
|
# proprietary
|
80
80
|
require_relative 'message/nmea/pashr'
|
81
81
|
|
82
|
+
module NMEAPlus
|
83
|
+
|
82
84
|
=begin boilerplate for message definitions
|
83
85
|
require_relative 'base_nmea'
|
84
86
|
|
@@ -93,8 +95,6 @@ module NMEAPlus
|
|
93
95
|
end
|
94
96
|
=end
|
95
97
|
|
96
|
-
module NMEAPlus
|
97
|
-
|
98
98
|
# Defines a factory for NMEA messages, which will all use {NMEAPlus::Message::NMEA::NMEAMessage} as their base
|
99
99
|
class NMEAMessageFactory < MessageFactory
|
100
100
|
|
data/lib/nmea_plus/version.rb
CHANGED