nmea_plus 1.0.8 → 1.0.9

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -9
  3. data/lib/nmea_plus/ais_message_factory.rb +3 -1
  4. data/lib/nmea_plus/generated_parser/tokenizer.rb +1 -1
  5. data/lib/nmea_plus/message/ais/base_ais.rb +9 -2
  6. data/lib/nmea_plus/message/ais/vdm.rb +8 -2
  7. data/lib/nmea_plus/message/ais/vdm_payload/payload.rb +59 -23
  8. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg.rb +444 -76
  9. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg1.rb +6 -5
  10. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg12.rb +5 -2
  11. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg14.rb +1 -1
  12. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg18.rb +5 -3
  13. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg20.rb +2 -1
  14. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg21.rb +2 -1
  15. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg24.rb +2 -1
  16. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg27.rb +2 -1
  17. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg4.rb +15 -2
  18. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg5.rb +3 -2
  19. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg6.rb +55 -0
  20. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1022f61.rb +122 -0
  21. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d235f10.rb +54 -0
  22. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg7.rb +36 -0
  23. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb +2 -9
  24. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg9.rb +5 -5
  25. data/lib/nmea_plus/message/base.rb +27 -14
  26. data/lib/nmea_plus/message/nmea/base_nmea.rb +13 -7
  27. data/lib/nmea_plus/message/nmea/bwr.rb +2 -2
  28. data/lib/nmea_plus/message/nmea/dtm.rb +2 -2
  29. data/lib/nmea_plus/message/nmea/gga.rb +2 -2
  30. data/lib/nmea_plus/message/nmea/gll.rb +2 -2
  31. data/lib/nmea_plus/message/nmea/gns.rb +2 -2
  32. data/lib/nmea_plus/message/nmea/gxa.rb +2 -2
  33. data/lib/nmea_plus/message/nmea/hdg.rb +2 -2
  34. data/lib/nmea_plus/message/nmea/rma.rb +3 -3
  35. data/lib/nmea_plus/message/nmea/rmb.rb +2 -2
  36. data/lib/nmea_plus/message/nmea/rmc.rb +3 -3
  37. data/lib/nmea_plus/message/nmea/trf.rb +2 -2
  38. data/lib/nmea_plus/message/nmea/wpl.rb +2 -2
  39. data/lib/nmea_plus/nmea_message_factory.rb +3 -1
  40. data/lib/nmea_plus/version.rb +1 -1
  41. metadata +9 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad7de05d0b6299139bf89734517185b1056c2055
4
- data.tar.gz: d77eced056aac4fb30998039e41fc9bcdc02e5a5
3
+ metadata.gz: 8b9332e5d45ba59ff6572c20801c15205fcfc674
4
+ data.tar.gz: ddc8d5117a0b4c4774eb5ffe552b07a1ce2873ed
5
5
  SHA512:
6
- metadata.gz: 337635824791c01b604b2e8027db96711375ad5fe521568953d3c5daac1e9df5605b79434b54ce90b474ff683fd5c532a18ac969585f9cf6a3dc3fc1ae2e42be
7
- data.tar.gz: 91dd8c299619df14559f74120e56141d790d413557cd2d8be038f2b0f4df55b21fb42e78561d865b0d4b3a04c13ddd76278fc2cfb535399a33084ce72ee3f4ab
6
+ metadata.gz: 4163c6ee8e9d1ba684cd30f18a94e4d8b42b11c5401a23c57ff0d82b27ce18b9b0ce2a21090e95f9fe96316bda9034f4224ae1b0d12176e75b90309f4369e09b
7
+ data.tar.gz: 538f7e47902a954c116d1c7d630c51b32a821b5d2cdc1b95e54ad9148ddb3e4b6ef2546b550bda734680e6f0da806d0bf3a87edc947d67ebdc47f1b2ce2d15d5
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # NMEA Ruby Gem (nmea_plus)
1
+ # NMEA (GPS) and AIS Parser / Decoder for Ruby (nmea_plus)
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/nmea_plus.svg)](https://rubygems.org/gems/nmea_plus)
4
4
  [![Build Status](https://travis-ci.org/ifreecarve/nmea_plus.svg)](https://travis-ci.org/ifreecarve/nmea_plus)
5
- [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/nmea_plus/1.0.8)
5
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/nmea_plus/1.0.9)
6
6
 
7
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.
8
8
 
@@ -84,9 +84,7 @@ end
84
84
 
85
85
  ```
86
86
 
87
- ## Design documents
88
-
89
- ### NMEA
87
+ ## NMEA (GPS) Parsing
90
88
 
91
89
  This gem was coded to accept the standard NMEA messages defined in the unoffical spec found here:
92
90
  http://www.catb.org/gpsd/NMEA.txt
@@ -97,16 +95,19 @@ Because the message types are standard, if no override is found for a particular
97
95
 
98
96
  Support for proprietary NMEA messages is also possible. PASHR is included as proof-of-concept.
99
97
 
100
- ### AIS
98
+
99
+ ## AIS Decoding
101
100
 
102
101
  AIS message type definitions were implemented from the unofficial spec found here:
103
102
  http://catb.org/gpsd/AIVDM.html
104
103
 
105
- Currently, the following AIVDM message types are supported:
104
+ The AIS payload can be found in the AIVDM message's payload field. Currently, the following AIS message types are supported:
106
105
 
107
- > 1, 2, 3, 4, 5, 8, 9, 12, 14, 18, 19, 20, 21, 24, 27
108
- > Type 8 subtypes for DAC/FID: 1/31, 366/56, 366/57
106
+ > 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 18, 19, 20, 21, 24, 27
109
107
 
108
+ > Type 6 subtypes for DAC/FID: 235/10, 1022/61
109
+
110
+ > Type 8 subtypes for DAC/FID: 1/31, 366/56, 366/57
110
111
 
111
112
 
112
113
  ## Disclaimer
@@ -5,7 +5,9 @@ require_relative 'message/ais/vdm'
5
5
 
6
6
  module NMEAPlus
7
7
 
8
- # Defines a factory for AIS messages, which will all use {NMEAPlus::Message::AIS::AISMessage} as their base
8
+ # Defines a factory for AIS messages, which will all use {NMEAPlus::Message::AIS::AISMessage} as their base.
9
+ # The factory extracts the NMEA data type (prefixed by a 2-character "talker ID"), and looks for a class with
10
+ # that name within the NMEA message namespace.
9
11
  class AISMessageFactory < MessageFactory
10
12
  # @return [String] The name of the parent module: AIS
11
13
  def self.parent_module
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
  module NMEAPlus
9
- class Decoder < Parser # This file is in .rex format, so no indenting. And yard docs are impossible.
9
+ class Decoder < Parser # The source file is in .rex format -- indentation and most yard documentation is impossible. The class does a very basic parse of an input line, calling {NMEAPlus::MessageFactory.create} on the result. In parser.y, this is currently defined to be a {NMEAPlus::NMEAMessageFactory} if the line begins with `$` and {NMEAPlus::AISMessageFactory} if the line begins with `!`
10
10
  require 'strscan'
11
11
 
12
12
  class ScanError < StandardError ; end
@@ -3,18 +3,25 @@ require_relative "../base"
3
3
 
4
4
  module NMEAPlus
5
5
  module Message
6
+
6
7
  # A container for all {AISMessage} types.
8
+ # Most definitions were sourced from http://catb.org/gpsd/AIVDM.html
9
+ # @see NMEAPlus::Message::AIS::AISMessage Base class for all AIS messages
10
+ # @see NMEAPlus::Message::AIS::VDMPayload::VDMMsg Base class for all AIS binary payloads
7
11
  module AIS
8
12
 
13
+ # The base NMEA message type for AIS. This is currently a thin wrapper, as {VDM} is the only defined message type.
9
14
  class AISMessage < NMEAPlus::Message::Base
10
- # NMEA (AIS) message types are 5 characters, the first 2 of which are the talker ID
15
+ # The first two characters of the NMEA message type
11
16
  # @!parse attr_accessor :talker
12
17
  # @return [String] The two-character "talker ID" of the message
13
18
  def talker
14
19
  data_type[0..1]
15
20
  end
16
21
 
17
- # NMEA (AIS) message types are 5 characters (or so), the last of which are the message type
22
+ # The generic type of the NMEA message.
23
+ # NMEA message types are 5 characters (or so): the first 2 are the talker ID, and the
24
+ # remaining characters are the generic message type.
18
25
  # @!parse attr_accessor :message_type
19
26
  # @return [String] The two-character "talker ID" of the message
20
27
  def message_type
@@ -18,8 +18,10 @@ end
18
18
  =end
19
19
 
20
20
  require_relative "vdm_payload/vdm_msg1" # also incldues 2 and 3
21
- require_relative "vdm_payload/vdm_msg4"
21
+ require_relative "vdm_payload/vdm_msg4" # also includes 11
22
22
  require_relative "vdm_payload/vdm_msg5"
23
+ require_relative "vdm_payload/vdm_msg6"
24
+ require_relative "vdm_payload/vdm_msg7" # also includes 13
23
25
  require_relative "vdm_payload/vdm_msg8"
24
26
  require_relative "vdm_payload/vdm_msg9"
25
27
  require_relative "vdm_payload/vdm_msg12"
@@ -33,6 +35,10 @@ require_relative "vdm_payload/vdm_msg27"
33
35
  module NMEAPlus
34
36
  module Message
35
37
  module AIS
38
+
39
+ # VDM - Vessel Data Message
40
+ # This message type thinly wraps AIS payloads.
41
+ # @see NMEAPlus::Message::AIS::VDMPayload::VDMMsg
36
42
  class VDM < NMEAPlus::Message::AIS::AISMessage
37
43
  field_reader :total_messages, 1, :_integer
38
44
  field_reader :message_number, 2, :_integer
@@ -41,7 +47,7 @@ module NMEAPlus
41
47
  field_reader :raw_ais_payload, 5, :_string
42
48
  field_reader :ais_payload_fill_bits, 6, :_integer
43
49
 
44
- # factory method: find the appropriate message type class and instantiate it
50
+ # factory method: find the appropriate class for this AIS message type and instantiate it
45
51
  # @!parse attr_reader :ais
46
52
  # @return [VDMPayload::VDMMsg]
47
53
  def ais
@@ -3,7 +3,9 @@ module NMEAPlus
3
3
  module Message
4
4
  module AIS
5
5
  module VDMPayload
6
- # Basic tools for interpreting the armored payload encoding
6
+ # Basic tools for interpreting the armored (binary) payload encoding of AIS.
7
+ # This class provides convenience functions for accessing the fields as the appropriate data type,
8
+ # as well as logic for AIS bit-level formats
7
9
  class Payload
8
10
 
9
11
  def initialize; end
@@ -14,7 +16,9 @@ module NMEAPlus
14
16
  # @return [Integer] The number of padding characters required to bring the payload to a 6 bit boundary
15
17
  attr_accessor :fill_bits
16
18
 
17
- # make our own shortcut syntax for payload attributes
19
+ # Enable a shortcut syntax for AIS payload attributes, in the style of `attr_accessor` metaprogramming.
20
+ # This is used to create a named field pointing to a specific bit range in the payload, applying
21
+ # a specific formatting function with up to 3 arguments as necessary
18
22
  # @param name [String] What the accessor will be called
19
23
  # @param start_bit [Integer] The index of first bit of this field in the payload
20
24
  # @param length [Integer] The number of bits in this field
@@ -22,6 +26,7 @@ module NMEAPlus
22
26
  # @param fmt_arg Any argument necessary for the formatting function
23
27
  # @param fmt_arg2 Any other argument necessary for the formatting function
24
28
  # @param fmt_arg3 Any other argument necessary for the formatting function
29
+ # @return [void]
25
30
  # @macro [attach] payload_reader
26
31
  # @!attribute [r] $1
27
32
  # @return The field defined by the $3 bits starting at payload bit $2, formatted with the function {#$4}($5, $6, $7)
@@ -33,6 +38,13 @@ module NMEAPlus
33
38
  self.class_eval("def #{name};#{formatter}(#{args.join(', ')});end")
34
39
  end
35
40
 
41
+ # Return an object by its class name, or nil if it isn't defined
42
+ def _object_by_name(class_identifier)
43
+ Object::const_get(class_identifier).new
44
+ rescue ::NameError
45
+ nil
46
+ end
47
+
36
48
  # Convert 6-bit ascii to a character, according to http://catb.org/gpsd/AIVDM.html#_ais_payload_data_types
37
49
  # @param ord [Integer] The 6-bit ascii code
38
50
  # @return [String] the character for that code
@@ -40,8 +52,8 @@ module NMEAPlus
40
52
  '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
41
53
  end
42
54
 
43
- # Access part of the payload. If there aren't bytes, there, return nil
44
- # else execute a block
55
+ # Access part of the payload.
56
+ # If there aren't bytes, there, return nil. Else, execute a block
45
57
  # @param start [Integer] The index of the first bit in the payload field
46
58
  # @param length [Integer] The number of bits in the payload field
47
59
  # @return Nil or whatever is yielded by the block
@@ -53,6 +65,7 @@ module NMEAPlus
53
65
  end
54
66
 
55
67
  # pull out 6b chunks from the payload, then convert those to their more familar characters
68
+ # This function is meant to be passed as a formatter to {payload_reader}.
56
69
  # @param start [Integer] The index of the first bit in the payload field
57
70
  # @param length [Integer] The number of bits in the payload field
58
71
  # @return [String]
@@ -60,7 +73,8 @@ module NMEAPlus
60
73
  _bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
61
74
  end
62
75
 
63
- # pull out 8b chunks from the payload, then convert those to their more familar characters
76
+ # pull out 8b chunks from the payload, then convert those to their more familar characters.
77
+ # This function is meant to be passed as a formatter to {payload_reader}.
64
78
  # @param start [Integer] The index of the first bit in the payload field
65
79
  # @param length [Integer] The number of bits in the payload field
66
80
  # @return [String]
@@ -68,7 +82,8 @@ module NMEAPlus
68
82
  _bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
69
83
  end
70
84
 
71
- # Slice a part of the payload into binary chunks of a given size
85
+ # Slice a part of the payload into binary chunks of a given size.
86
+ # This function is meant to be passed as a formatter to {payload_reader}.
72
87
  # @param start [Integer] The index of the first bit in the payload field
73
88
  # @param length [Integer] The number of bits in the payload field
74
89
  # @return [Array<String>] Strings representing binary ("01010101" etc) for each slice
@@ -76,7 +91,8 @@ module NMEAPlus
76
91
  _access(start, length) { |bits| bits.chars.each_slice(chunk_size) }
77
92
  end
78
93
 
79
- # convert a 6b string but trim off the 0s ('@')
94
+ # convert a 6b string but trim off the 0s ('@').
95
+ # This function is meant to be passed as a formatter to {payload_reader}.
80
96
  # @param start [Integer] The index of the first bit in the payload field
81
97
  # @param length [Integer] The number of bits in the payload field
82
98
  # @return [String]
@@ -84,7 +100,8 @@ module NMEAPlus
84
100
  _6b_string(start, length).split("@", 2)[0]
85
101
  end
86
102
 
87
- # directly convert a string to a binary number as you'd read it
103
+ # directly convert a string to a binary number as you'd read it.
104
+ # This function is meant to be passed as a formatter to {payload_reader}.
88
105
  # @param start [Integer] The index of the first bit in the payload field
89
106
  # @param length [Integer] The number of bits in the payload field
90
107
  # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
@@ -95,7 +112,8 @@ module NMEAPlus
95
112
  ret
96
113
  end
97
114
 
98
- # perform a twos complement operation on part of the payload
115
+ # perform a twos complement operation on part of the payload.
116
+ # This function is meant to be passed as a formatter to {payload_reader}.
99
117
  # @param start [Integer] The index of the first bit in the payload field
100
118
  # @param length [Integer] The number of bits in the payload field
101
119
  # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
@@ -113,7 +131,8 @@ module NMEAPlus
113
131
  end
114
132
  end
115
133
 
116
- # scale an integer by dividing it by a denominator
134
+ # scale an integer by dividing it by a denominator.
135
+ # This function is meant to be passed as a formatter to {payload_reader}.
117
136
  # @param start [Integer] The index of the first bit in the payload field
118
137
  # @param length [Integer] The number of bits in the payload field
119
138
  # @param denominator [Integer] The divisor to use in scaling down the result
@@ -125,7 +144,8 @@ module NMEAPlus
125
144
  ret.to_f / denominator
126
145
  end
127
146
 
128
- # scale an unsigned integer by dividing it by a denominator
147
+ # scale an unsigned integer by dividing it by a denominator.
148
+ # This function is meant to be passed as a formatter to {payload_reader}.
129
149
  # @param start [Integer] The index of the first bit in the payload field
130
150
  # @param length [Integer] The number of bits in the payload field
131
151
  # @param denominator [Integer] The divisor to use in scaling down the result
@@ -137,7 +157,8 @@ module NMEAPlus
137
157
  ret.to_f / denominator
138
158
  end
139
159
 
140
- # scale an integer by dividing it by a denominator
160
+ # scale an integer by dividing it by a denominator.
161
+ # This function is meant to be passed as a formatter to {payload_reader}.
141
162
  # @param start [Integer] The index of the first bit in the payload field
142
163
  # @param length [Integer] The number of bits in the payload field
143
164
  # @param denominator [Integer] The divisor to use in scaling down the result
@@ -150,7 +171,8 @@ module NMEAPlus
150
171
  ret + shift
151
172
  end
152
173
 
153
- # scale an unsigned integer by dividing it by a denominator
174
+ # scale an unsigned integer by dividing it by a denominator.
175
+ # This function is meant to be passed as a formatter to {payload_reader}.
154
176
  # @param start [Integer] The index of the first bit in the payload field
155
177
  # @param length [Integer] The number of bits in the payload field
156
178
  # @param denominator [Integer] The divisor to use in scaling down the result
@@ -163,7 +185,8 @@ module NMEAPlus
163
185
  ret + shift
164
186
  end
165
187
 
166
- # Get the value of a bit in the payload
188
+ # Get the value of a bit in the payload.
189
+ # This function is meant to be passed as a formatter to {payload_reader}.
167
190
  # @param start [Integer] The index of the first bit in the payload field
168
191
  # @param _ [Integer] Doesn't matter. Here so signatures match; we hard-code 1 because it's 1 bit.
169
192
  # @return [bool]
@@ -171,7 +194,17 @@ module NMEAPlus
171
194
  _access(start, 1) { |bits| bits.to_i == 1 }
172
195
  end
173
196
 
174
- # Return a string representing binary
197
+ # Get the flipped value of a bit in the payload.
198
+ # This function is meant to be passed as a formatter to {payload_reader}.
199
+ # @param start [Integer] The index of the first bit in the payload field
200
+ # @param _ [Integer] Doesn't matter. Here so signatures match; we hard-code 1 because it's 1 bit.
201
+ # @return [bool]
202
+ def _6b_negated_boolean(start, _)
203
+ !_6b_boolean(start, 1)
204
+ end
205
+
206
+ # Return a string representing binary digits.
207
+ # This function is meant to be passed as a formatter to {payload_reader}.
175
208
  # @param start [Integer] The index of the first bit in the payload field
176
209
  # @param length [Integer] The number of bits in the payload field
177
210
  # @return [String] e.g. "0101010101011000"
@@ -180,14 +213,17 @@ module NMEAPlus
180
213
  end
181
214
 
182
215
  # use shorthand for data types as defined in http://catb.org/gpsd/AIVDM.html
183
- alias_method :_u, :_6b_unsigned_integer
184
- alias_method :_U, :_6b_unsigned_integer_scaled
185
- alias_method :_i, :_6b_integer
186
- alias_method :_I, :_6b_integer_scaled
187
- alias_method :_b, :_6b_boolean
188
- alias_method :_e, :_6b_unsigned_integer
189
- alias_method :_t, :_6b_string_nullterminated
190
- alias_method :_d, :_2b_data_string
216
+ alias_method :_u, :_6b_unsigned_integer
217
+ alias_method :_U, :_6b_unsigned_integer_scaled
218
+ alias_method :_i, :_6b_integer
219
+ alias_method :_I, :_6b_integer_scaled
220
+ alias_method :_b, :_6b_boolean
221
+ alias_method :_nb, :_6b_negated_boolean
222
+ alias_method :_e, :_6b_unsigned_integer
223
+ alias_method :_t, :_6b_string_nullterminated
224
+ alias_method :_tt, :_6b_string
225
+ alias_method :_T, :_8b_data_string
226
+ alias_method :_d, :_2b_data_string
191
227
  alias_method :_UU, :_6b_unsigned_integer_scaled_shifted
192
228
  alias_method :_II, :_6b_integer_scaled_shifted
193
229
 
@@ -3,87 +3,453 @@ require_relative "payload"
3
3
  module NMEAPlus
4
4
  module Message
5
5
  module AIS
6
- # There are many VDM payload types, and this is their container. See {VDMMsg}.
6
+ # This module contains all the VDM payload types and subtypes.
7
+ # @see {VDMMsg}
7
8
  module VDMPayload
8
- # The base class for the AIS payload (of {NMEAPlus::Message::AIS::VDM}), which uses its own encoding for its own subtypes
9
+
10
+ # The base class for the {NMEAPlus::Message::AIS::VDM#ais AIS payload}, which uses its own encoding for its own subtypes
9
11
  class VDMMsg < NMEAPlus::Message::AIS::VDMPayload::Payload
10
12
 
11
13
  payload_reader :message_type, 0, 6, :_u
12
14
  payload_reader :repeat_indicator, 6, 2, :_u
13
15
  payload_reader :source_mmsi, 8, 30, :_u
14
16
 
17
+ # The MMSI category as defined by ITU-R M.585-7
18
+ # @!parse attr_reader :mmsi_category
19
+ # @return [Symbol] The symbol for the MMSI category
20
+ def mmsi_category
21
+ case source_mmsi.to_s.rjust(9, '0') # formatted as 9 digit string with leading 0s
22
+ when /[2-7]......../ then :individual_ship
23
+ when /00...1.../ then :coast_station
24
+ when /00...2.../ then :harbor_station
25
+ when /00...3.../ then :pilot_station
26
+ when /00...4.../ then :ais_repeater_station
27
+ when /00......./ then :coast_station
28
+ when /111...1../ then :sar_aircraft_fixed
29
+ when /111...5../ then :sar_aircraft_helicopter
30
+ when /1......../ then :sar_aircraft
31
+ when /8......../ then :handheld
32
+ when /98......./ then :auxiliary_craft
33
+ when /970....../ then :sar_transmitter
34
+ when /972....../ then :man_overboard
35
+ when /974....../ then :epirb
36
+ when /99...1.../ then :aton_physical
37
+ when /99...6.../ then :aton_virtual
38
+ when /99......./ then :aton
39
+ else
40
+ :unknown_mmsi_category
41
+ end
42
+ end
43
+
44
+ # The MMSI Maritime Identification Digits (MID)
45
+ # @!parse attr_reader :mid
46
+ # @return [Integer] the MID
47
+ def mid
48
+ range = case mmsi_category
49
+ when :individual_ship then 0..2
50
+ when :coast_station, :harbor_station, :pilot_station, :ais_repeater_station then 2..4
51
+ when :sar_aircraft, :sar_aircraft_fixed, :sar_aircraft_helicopter then 3..5
52
+ when :aton_physical, :aton_virtual, :aton then 2..4
53
+ when :auxiliary_craft then 2..4
54
+ when :handheld then 1..3
55
+ when :sar_transmitter, :man_overboard, :epirb then 3..5
56
+ end
57
+ return nil if range.nil?
58
+ source_mmsi.to_s.rjust(9, '0')[range].to_i
59
+ end
60
+
61
+ # The ISO 3166-1 indicated by the MMSI Maritime Identification Digits (MID)
62
+ # @!parse attr_reader :mid_country
63
+ # @return [Integer] the MID
64
+ def mid_country
65
+ # https://github.com/S73417H/MIDs
66
+ # https://github.com/alexrabarts/iso_country_codes
67
+ # JSON.parse(IO.read("mids.json")).each {|k, v| puts "when #{k} then #{IsoCountryCodes.find(v[1]).numeric.to_i}" }
68
+ case mid
69
+ when 201 then 8
70
+ when 202 then 20
71
+ when 203 then 40
72
+ when 204 then 620
73
+ when 205 then 56
74
+ when 206 then 112
75
+ when 207 then 100
76
+ when 208 then 336
77
+ when 209 then 196
78
+ when 210 then 196
79
+ when 211 then 276
80
+ when 212 then 196
81
+ when 213 then 268
82
+ when 214 then 498
83
+ when 215 then 470
84
+ when 216 then 51
85
+ when 218 then 276
86
+ when 219 then 208
87
+ when 220 then 208
88
+ when 224 then 724
89
+ when 225 then 724
90
+ when 226 then 250
91
+ when 227 then 250
92
+ when 228 then 250
93
+ when 229 then 470
94
+ when 230 then 246
95
+ when 231 then 234
96
+ when 232 then 826
97
+ when 233 then 826
98
+ when 234 then 826
99
+ when 235 then 826
100
+ when 236 then 292
101
+ when 237 then 300
102
+ when 238 then 191
103
+ when 239 then 300
104
+ when 240 then 300
105
+ when 241 then 300
106
+ when 242 then 504
107
+ when 243 then 348
108
+ when 244 then 528
109
+ when 245 then 528
110
+ when 246 then 528
111
+ when 247 then 380
112
+ when 248 then 470
113
+ when 250 then 372
114
+ when 251 then 352
115
+ when 252 then 438
116
+ when 253 then 442
117
+ when 254 then 492
118
+ when 255 then 620
119
+ when 256 then 470
120
+ when 257 then 578
121
+ when 258 then 578
122
+ when 259 then 578
123
+ when 261 then 616
124
+ when 262 then 499
125
+ when 263 then 620
126
+ when 264 then 642
127
+ when 265 then 752
128
+ when 266 then 752
129
+ when 267 then 703
130
+ when 268 then 674
131
+ when 269 then 756
132
+ when 270 then 203
133
+ when 271 then 792
134
+ when 272 then 804
135
+ when 273 then 643
136
+ when 274 then 807
137
+ when 275 then 428
138
+ when 276 then 233
139
+ when 277 then 440
140
+ when 278 then 705
141
+ when 279 then 688
142
+ when 301 then 660
143
+ when 303 then 840
144
+ when 304 then 28
145
+ when 305 then 28
146
+ when 306 then 531
147
+ when 307 then 533
148
+ when 308 then 44
149
+ when 309 then 44
150
+ when 310 then 60
151
+ when 311 then 60
152
+ when 312 then 84
153
+ when 314 then 52
154
+ when 316 then 124
155
+ when 319 then 136
156
+ when 321 then 188
157
+ when 323 then 192
158
+ when 325 then 212
159
+ when 327 then 214
160
+ when 329 then 312
161
+ when 330 then 308
162
+ when 331 then 304
163
+ when 332 then 320
164
+ when 335 then 340
165
+ when 336 then 332
166
+ when 338 then 840
167
+ when 339 then 388
168
+ when 341 then 659
169
+ when 343 then 662
170
+ when 345 then 484
171
+ when 347 then 474
172
+ when 348 then 500
173
+ when 350 then 558
174
+ when 351 then 591
175
+ when 352 then 591
176
+ when 353 then 591
177
+ when 354 then 591
178
+ when 358 then 630
179
+ when 359 then 222
180
+ when 361 then 666
181
+ when 362 then 780
182
+ when 364 then 796
183
+ when 366 then 840
184
+ when 367 then 840
185
+ when 368 then 840
186
+ when 369 then 840
187
+ when 370 then 591
188
+ when 371 then 591
189
+ when 372 then 591
190
+ when 373 then 591
191
+ when 375 then 670
192
+ when 376 then 670
193
+ when 377 then 670
194
+ when 378 then 92
195
+ when 379 then 850
196
+ when 401 then 4
197
+ when 403 then 682
198
+ when 405 then 50
199
+ when 408 then 48
200
+ when 410 then 64
201
+ when 412 then 156
202
+ when 413 then 156
203
+ when 414 then 156
204
+ when 416 then 158
205
+ when 417 then 144
206
+ when 419 then 356
207
+ when 422 then 364
208
+ when 423 then 31
209
+ when 425 then 368
210
+ when 428 then 376
211
+ when 431 then 392
212
+ when 432 then 392
213
+ when 434 then 795
214
+ when 436 then 398
215
+ when 437 then 860
216
+ when 438 then 400
217
+ when 440 then 410
218
+ when 441 then 410
219
+ when 443 then 275
220
+ when 445 then 408
221
+ when 447 then 414
222
+ when 450 then 422
223
+ when 451 then 417
224
+ when 453 then 446
225
+ when 455 then 462
226
+ when 457 then 496
227
+ when 459 then 524
228
+ when 461 then 512
229
+ when 463 then 586
230
+ when 466 then 634
231
+ when 468 then 760
232
+ when 470 then 784
233
+ when 472 then 762
234
+ when 473 then 887
235
+ when 475 then 887
236
+ when 477 then 344
237
+ when 478 then 70
238
+ when 501 then 250
239
+ when 503 then 36
240
+ when 506 then 104
241
+ when 508 then 96
242
+ when 510 then 583
243
+ when 511 then 585
244
+ when 512 then 554
245
+ when 514 then 116
246
+ when 515 then 116
247
+ when 516 then 162
248
+ when 518 then 184
249
+ when 520 then 242
250
+ when 523 then 166
251
+ when 525 then 360
252
+ when 529 then 296
253
+ when 531 then 418
254
+ when 533 then 458
255
+ when 536 then 580
256
+ when 538 then 584
257
+ when 540 then 540
258
+ when 542 then 570
259
+ when 544 then 520
260
+ when 546 then 258
261
+ when 548 then 608
262
+ when 553 then 598
263
+ when 555 then 612
264
+ when 557 then 90
265
+ when 559 then 16
266
+ when 561 then 882
267
+ when 563 then 702
268
+ when 564 then 702
269
+ when 565 then 702
270
+ when 566 then 702
271
+ when 567 then 764
272
+ when 570 then 776
273
+ when 572 then 798
274
+ when 574 then 704
275
+ when 576 then 548
276
+ when 577 then 548
277
+ when 578 then 876
278
+ when 601 then 710
279
+ when 603 then 24
280
+ when 605 then 12
281
+ when 607 then 250
282
+ when 608 then 826
283
+ when 609 then 108
284
+ when 610 then 204
285
+ when 611 then 72
286
+ when 621 then 262
287
+ when 613 then 120
288
+ when 615 then 178
289
+ when 616 then 174
290
+ when 617 then 132
291
+ when 618 then 250
292
+ when 619 then 384
293
+ when 620 then 174
294
+ when 622 then 818
295
+ when 624 then 231
296
+ when 625 then 232
297
+ when 626 then 266
298
+ when 627 then 288
299
+ when 629 then 270
300
+ when 630 then 624
301
+ when 631 then 226
302
+ when 632 then 324
303
+ when 633 then 854
304
+ when 634 then 404
305
+ when 635 then 250
306
+ when 636 then 430
307
+ when 637 then 430
308
+ when 638 then 728
309
+ when 642 then 434
310
+ when 644 then 426
311
+ when 645 then 480
312
+ when 647 then 450
313
+ when 649 then 466
314
+ when 650 then 508
315
+ when 654 then 478
316
+ when 655 then 454
317
+ when 656 then 566
318
+ when 659 then 516
319
+ when 660 then 638
320
+ when 661 then 646
321
+ when 662 then 729
322
+ when 663 then 686
323
+ when 664 then 690
324
+ when 665 then 654
325
+ when 666 then 706
326
+ when 667 then 694
327
+ when 668 then 678
328
+ when 669 then 748
329
+ when 670 then 148
330
+ when 671 then 768
331
+ when 672 then 788
332
+ when 674 then 834
333
+ when 675 then 800
334
+ when 676 then 180
335
+ when 677 then 834
336
+ when 678 then 894
337
+ when 679 then 716
338
+ when 701 then 32
339
+ when 710 then 76
340
+ when 720 then 68
341
+ when 725 then 152
342
+ when 730 then 170
343
+ when 735 then 218
344
+ when 740 then 238
345
+ when 745 then 254
346
+ when 750 then 328
347
+ when 755 then 600
348
+ when 760 then 604
349
+ when 765 then 740
350
+ when 770 then 858
351
+ when 775 then 862
352
+ end
353
+ end
354
+
355
+ # The MMSI category as defined by ITU-R M.585-7
356
+ # @!parse attr_reader :mmsi_category_description
357
+ # @return [String] the human-readable description the MMSI category
358
+ def mmsi_category_description
359
+ case mmsi_category
360
+ when :individual_ship then "Individual ship"
361
+ when :coast_station then "Coast station"
362
+ when :harbor_station then "Harbor station"
363
+ when :pilot_station then "Pilot station"
364
+ when :ais_repeater_station then "AIS repeater station"
365
+ when :sar_aircraft then "SAR aircraft"
366
+ when :sar_aircraft_fixed then "SAR fixed-wing aircraft"
367
+ when :sar_aircraft_helicopter then "SAR helicopter"
368
+ when :aton_physical then "Physical AIS AtoN"
369
+ when :aton_virtual then "Virtual AIS AtoN"
370
+ when :aton then "AIS Aid to Navigation"
371
+ when :auxiliary_craft then "Auxiliary craft"
372
+ when :handheld then "Handheld transceiver"
373
+ when :sar_transmitter then "AIS-SART"
374
+ when :man_overboard then "MOB (Man Overboard)"
375
+ when :epirb then "EPIRB"
376
+ else
377
+ mmsi_category.to_s
378
+ end
379
+ end
380
+
15
381
  # The ship cargo type description lookup table
16
382
  # @param code [Integer] The cargo type id
17
383
  # @return [String] Cargo type description
18
384
  def get_ship_cargo_type_description(code)
19
385
  case code
20
- when 0 then return nil
21
- when 1...19 then return "(future use)"
22
- when 20 then return "WIG (any)"
23
- when 21 then return "WIG Hazardous category A"
24
- when 22 then return "WIG Hazardous category B"
25
- when 23 then return "WIG Hazardous category C"
26
- when 24 then return "WIG Hazardous category D"
27
- when 25...29 then return "WIG (future use)"
28
- when 30 then return "Fishing"
29
- when 31 then return "Towing"
30
- when 32 then return "Towing (large)"
31
- when 33 then return "Dredging/underwater ops"
32
- when 34 then return "Diving ops"
33
- when 35 then return "Military ops"
34
- when 36 then return "Sailing"
35
- when 37 then return "Pleasure craft"
36
- when 38, 39 then return "Reserved"
37
- when 40 then return "High Speed Craft"
38
- when 41 then return "HSC Hazardous category A"
39
- when 42 then return "HSC Hazardous category B"
40
- when 43 then return "HSC Hazardous category C"
41
- when 44 then return "HSC Hazardous category D"
42
- when 45...48 then return "HSC (reserved)"
43
- when 49 then return "HSC (no additional information)"
44
- when 50 then return "Pilot Vessel"
45
- when 51 then return "Search and Rescue Vessel"
46
- when 52 then return "Tug"
47
- when 53 then return "Port Tender"
48
- when 54 then return "Anti-pollution equipment"
49
- when 55 then return "Law Enforcement"
50
- when 56, 57 then return "Spare - Local Vessel"
51
- when 58 then return "Medical Transport"
52
- when 59 then return "Noncombatant ship according to RR Resolution No. 18"
53
- when 60 then return "Passenger"
54
- when 61 then return "Passenger, Hazardous category A"
55
- when 62 then return "Passenger, Hazardous category B"
56
- when 63 then return "Passenger, Hazardous category C"
57
- when 64 then return "Passenger, Hazardous category D"
58
- when 65..68 then return "Passenger, Reserved for future use"
59
- when 69 then return "Passenger, No additional information"
60
- when 70 then return "Cargo"
61
- when 71 then return "Cargo, Hazardous category A"
62
- when 72 then return "Cargo, Hazardous category B"
63
- when 73 then return "Cargo, Hazardous category C"
64
- when 74 then return "Cargo, Hazardous category D"
65
- when 75..78 then return "Cargo, Reserved for future use"
66
- when 79 then return "Cargo, No additional information"
67
- when 80 then return "Tanker"
68
- when 81 then return "Tanker, Hazardous category A"
69
- when 82 then return "Tanker, Hazardous category B"
70
- when 83 then return "Tanker, Hazardous category C"
71
- when 84 then return "Tanker, Hazardous category D"
72
- when 85.88 then return "Tanker, Reserved for future use"
73
- when 89 then return "Tanker, No additional information"
74
- when 90 then return "Other Type"
75
- when 91 then return "Other Type, Hazardous category A"
76
- when 92 then return "Other Type, Hazardous category B"
77
- when 93 then return "Other Type, Hazardous category C"
78
- when 94 then return "Other Type, Hazardous category D"
79
- when 95..98 then return "Other Type, Reserved for future use"
80
- when 99 then return "Other Type, no additional information"
386
+ when 0 then nil
387
+ when 1...19 then "(future use)"
388
+ when 20 then "WIG (any)"
389
+ when 21 then "WIG Hazardous category A"
390
+ when 22 then "WIG Hazardous category B"
391
+ when 23 then "WIG Hazardous category C"
392
+ when 24 then "WIG Hazardous category D"
393
+ when 25...29 then "WIG (future use)"
394
+ when 30 then "Fishing"
395
+ when 31 then "Towing"
396
+ when 32 then "Towing (large)"
397
+ when 33 then "Dredging/underwater ops"
398
+ when 34 then "Diving ops"
399
+ when 35 then "Military ops"
400
+ when 36 then "Sailing"
401
+ when 37 then "Pleasure craft"
402
+ when 38, 39 then "Reserved"
403
+ when 40 then "High Speed Craft"
404
+ when 41 then "HSC Hazardous category A"
405
+ when 42 then "HSC Hazardous category B"
406
+ when 43 then "HSC Hazardous category C"
407
+ when 44 then "HSC Hazardous category D"
408
+ when 45...48 then "HSC (reserved)"
409
+ when 49 then "HSC (no additional information)"
410
+ when 50 then "Pilot Vessel"
411
+ when 51 then "Search and Rescue Vessel"
412
+ when 52 then "Tug"
413
+ when 53 then "Port Tender"
414
+ when 54 then "Anti-pollution equipment"
415
+ when 55 then "Law Enforcement"
416
+ when 56, 57 then "Spare - Local Vessel"
417
+ when 58 then "Medical Transport"
418
+ when 59 then "Noncombatant ship according to RR Resolution No. 18"
419
+ when 60 then "Passenger"
420
+ when 61 then "Passenger, Hazardous category A"
421
+ when 62 then "Passenger, Hazardous category B"
422
+ when 63 then "Passenger, Hazardous category C"
423
+ when 64 then "Passenger, Hazardous category D"
424
+ when 65..68 then "Passenger, Reserved for future use"
425
+ when 69 then "Passenger, No additional information"
426
+ when 70 then "Cargo"
427
+ when 71 then "Cargo, Hazardous category A"
428
+ when 72 then "Cargo, Hazardous category B"
429
+ when 73 then "Cargo, Hazardous category C"
430
+ when 74 then "Cargo, Hazardous category D"
431
+ when 75..78 then "Cargo, Reserved for future use"
432
+ when 79 then "Cargo, No additional information"
433
+ when 80 then "Tanker"
434
+ when 81 then "Tanker, Hazardous category A"
435
+ when 82 then "Tanker, Hazardous category B"
436
+ when 83 then "Tanker, Hazardous category C"
437
+ when 84 then "Tanker, Hazardous category D"
438
+ when 85.88 then "Tanker, Reserved for future use"
439
+ when 89 then "Tanker, No additional information"
440
+ when 90 then "Other Type"
441
+ when 91 then "Other Type, Hazardous category A"
442
+ when 92 then "Other Type, Hazardous category B"
443
+ when 93 then "Other Type, Hazardous category C"
444
+ when 94 then "Other Type, Hazardous category D"
445
+ when 95..98 then "Other Type, Reserved for future use"
446
+ when 99 then "Other Type, no additional information"
81
447
  end
82
448
  end
83
449
 
84
450
  # An MMSI is associated with an auxiliary craft when it is of the form 98XXXYYYY
85
451
  def auxiliary_craft?
86
- 980_000_000 < source_mmsi && source_mmsi < 990_000_000
452
+ mmsi_category == :auxiliary_craft
87
453
  end
88
454
 
89
455
  # @param code [Integer] The navigational status id
@@ -91,23 +457,25 @@ module NMEAPlus
91
457
  def get_navigational_status_description(code)
92
458
  return nil if code.nil?
93
459
  case code
94
- when 0 then return "Under way using engine"
95
- when 1 then return "At anchor"
96
- when 2 then return "Not under command"
97
- when 3 then return "Restricted manoeuverability"
98
- when 4 then return "Constrained by her draught"
99
- when 5 then return "Moored"
100
- when 6 then return "Aground"
101
- when 7 then return "Engaged in Fishing"
102
- when 8 then return "Under way sailing"
103
- when 14 then return "AIS-SART active"
460
+ when 0 then "Under way using engine"
461
+ when 1 then "At anchor"
462
+ when 2 then "Not under command"
463
+ when 3 then "Restricted manoeuverability"
464
+ when 4 then "Constrained by her draught"
465
+ when 5 then "Moored"
466
+ when 6 then "Aground"
467
+ when 7 then "Engaged in Fishing"
468
+ when 8 then "Under way sailing"
469
+ when 9...13 then "Reserved for future use"
470
+ when 14 then "AIS-SART active"
471
+ else
472
+ "Not defined"
104
473
  end
105
- "Reserved for future use"
106
474
  end
107
475
 
108
476
  end
109
477
 
110
- # We haven't defined all the AIS payload types, so this is a catch-all
478
+ # We haven't defined all the {NMEAPlus::Message::AIS::VDM#ais AIS payload} types, so this is a catch-all
111
479
  class VDMMsgUndefined < VDMMsg; end
112
480
 
113
481
  end