nmea_plus 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -6
  3. data/lib/nmea_plus/message/ais/vdm.rb +1 -0
  4. data/lib/nmea_plus/message/ais/vdm_payload/payload.rb +198 -0
  5. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg.rb +21 -147
  6. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg1.rb +28 -33
  7. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg18.rb +5 -24
  8. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg21.rb +2 -12
  9. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg27.rb +30 -0
  10. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg4.rb +2 -13
  11. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg5.rb +1 -1
  12. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb +48 -1
  13. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d1f31.rb +139 -0
  14. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d366f56.rb +29 -0
  15. data/lib/nmea_plus/message/ais/vdm_payload/vdm_msg9.rb +2 -13
  16. data/lib/nmea_plus/message/nmea/aam.rb +1 -1
  17. data/lib/nmea_plus/message/nmea/alm.rb +1 -0
  18. data/lib/nmea_plus/message/nmea/apa.rb +1 -0
  19. data/lib/nmea_plus/message/nmea/apb.rb +1 -0
  20. data/lib/nmea_plus/message/nmea/bod.rb +1 -0
  21. data/lib/nmea_plus/message/nmea/bwc.rb +1 -0
  22. data/lib/nmea_plus/message/nmea/bwr.rb +1 -0
  23. data/lib/nmea_plus/message/nmea/bww.rb +1 -0
  24. data/lib/nmea_plus/message/nmea/dbk.rb +1 -0
  25. data/lib/nmea_plus/message/nmea/dbs.rb +1 -0
  26. data/lib/nmea_plus/message/nmea/dbt.rb +1 -0
  27. data/lib/nmea_plus/message/nmea/dcn.rb +1 -0
  28. data/lib/nmea_plus/message/nmea/dpt.rb +1 -0
  29. data/lib/nmea_plus/message/nmea/dtm.rb +1 -0
  30. data/lib/nmea_plus/message/nmea/fsi.rb +1 -0
  31. data/lib/nmea_plus/message/nmea/gbs.rb +1 -0
  32. data/lib/nmea_plus/message/nmea/gga.rb +1 -1
  33. data/lib/nmea_plus/message/nmea/glc.rb +1 -0
  34. data/lib/nmea_plus/message/nmea/gll.rb +1 -0
  35. data/lib/nmea_plus/message/nmea/gns.rb +1 -0
  36. data/lib/nmea_plus/message/nmea/grs.rb +1 -0
  37. data/lib/nmea_plus/message/nmea/gsa.rb +1 -0
  38. data/lib/nmea_plus/message/nmea/gst.rb +1 -0
  39. data/lib/nmea_plus/message/nmea/gsv.rb +1 -0
  40. data/lib/nmea_plus/message/nmea/gtd.rb +1 -0
  41. data/lib/nmea_plus/message/nmea/gxa.rb +1 -0
  42. data/lib/nmea_plus/message/nmea/hdg.rb +1 -0
  43. data/lib/nmea_plus/message/nmea/hdm.rb +1 -0
  44. data/lib/nmea_plus/message/nmea/hdt.rb +1 -0
  45. data/lib/nmea_plus/message/nmea/hfb.rb +1 -0
  46. data/lib/nmea_plus/message/nmea/hsc.rb +1 -0
  47. data/lib/nmea_plus/message/nmea/its.rb +1 -0
  48. data/lib/nmea_plus/message/nmea/lcd.rb +1 -0
  49. data/lib/nmea_plus/message/nmea/msk.rb +1 -0
  50. data/lib/nmea_plus/message/nmea/mss.rb +1 -0
  51. data/lib/nmea_plus/message/nmea/mtw.rb +1 -0
  52. data/lib/nmea_plus/message/nmea/mwv.rb +1 -0
  53. data/lib/nmea_plus/message/nmea/oln.rb +1 -0
  54. data/lib/nmea_plus/message/nmea/osd.rb +1 -0
  55. data/lib/nmea_plus/message/nmea/pashr.rb +1 -0
  56. data/lib/nmea_plus/message/nmea/r00.rb +1 -0
  57. data/lib/nmea_plus/message/nmea/rma.rb +1 -0
  58. data/lib/nmea_plus/message/nmea/rmb.rb +1 -0
  59. data/lib/nmea_plus/message/nmea/rmc.rb +1 -0
  60. data/lib/nmea_plus/message/nmea/rot.rb +1 -0
  61. data/lib/nmea_plus/message/nmea/rpm.rb +1 -0
  62. data/lib/nmea_plus/message/nmea/rsa.rb +1 -0
  63. data/lib/nmea_plus/message/nmea/rsd.rb +1 -0
  64. data/lib/nmea_plus/message/nmea/rte.rb +1 -0
  65. data/lib/nmea_plus/message/nmea/sfi.rb +1 -0
  66. data/lib/nmea_plus/message/nmea/stn.rb +1 -0
  67. data/lib/nmea_plus/message/nmea/tds.rb +1 -0
  68. data/lib/nmea_plus/message/nmea/tfi.rb +1 -0
  69. data/lib/nmea_plus/message/nmea/tpc.rb +1 -0
  70. data/lib/nmea_plus/message/nmea/tpr.rb +1 -0
  71. data/lib/nmea_plus/message/nmea/tpt.rb +1 -0
  72. data/lib/nmea_plus/message/nmea/trf.rb +1 -0
  73. data/lib/nmea_plus/message/nmea/ttm.rb +1 -0
  74. data/lib/nmea_plus/message/nmea/vbw.rb +1 -0
  75. data/lib/nmea_plus/message/nmea/vdr.rb +1 -0
  76. data/lib/nmea_plus/message/nmea/vhw.rb +1 -0
  77. data/lib/nmea_plus/message/nmea/vlw.rb +1 -0
  78. data/lib/nmea_plus/message/nmea/vpw.rb +1 -0
  79. data/lib/nmea_plus/message/nmea/vtg.rb +1 -0
  80. data/lib/nmea_plus/message/nmea/vwr.rb +1 -0
  81. data/lib/nmea_plus/message/nmea/wcv.rb +1 -0
  82. data/lib/nmea_plus/message/nmea/wnc.rb +1 -0
  83. data/lib/nmea_plus/message/nmea/wpl.rb +1 -0
  84. data/lib/nmea_plus/message/nmea/xdr.rb +1 -0
  85. data/lib/nmea_plus/message/nmea/xte.rb +1 -0
  86. data/lib/nmea_plus/message/nmea/xtr.rb +1 -0
  87. data/lib/nmea_plus/message/nmea/zda.rb +1 -0
  88. data/lib/nmea_plus/message/nmea/zfo.rb +1 -0
  89. data/lib/nmea_plus/message/nmea/ztg.rb +1 -0
  90. data/lib/nmea_plus/version.rb +1 -1
  91. metadata +8 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0eca727afdb573f7ffede6594fc3d7acda16a48
4
- data.tar.gz: 6ad4878023b3743d36954b020c8f08d97ea2ec62
3
+ metadata.gz: ad7de05d0b6299139bf89734517185b1056c2055
4
+ data.tar.gz: d77eced056aac4fb30998039e41fc9bcdc02e5a5
5
5
  SHA512:
6
- metadata.gz: 9ab5c817f001fb525e698b1e3588690bfbcf7952fd85faebe27df6e1c214cceecb5502ec0aedc39e74f33a0e1255524fe172059f1d9b870f1fec9eb245b526b1
7
- data.tar.gz: 9338e0e75eae30e96717ffa45053ae7fbd03383f49b22793a0b4b87ddfcfb17d508ae6197490093a6aab7e859a67426d329731907949b992a334ecee765a71b7
6
+ metadata.gz: 337635824791c01b604b2e8027db96711375ad5fe521568953d3c5daac1e9df5605b79434b54ce90b474ff683fd5c532a18ac969585f9cf6a3dc3fc1ae2e42be
7
+ data.tar.gz: 91dd8c299619df14559f74120e56141d790d413557cd2d8be038f2b0f4df55b21fb42e78561d865b0d4b3a04c13ddd76278fc2cfb535399a33084ce72ee3f4ab
data/README.md CHANGED
@@ -2,7 +2,7 @@
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.7)
5
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/nmea_plus/1.0.8)
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
 
@@ -48,7 +48,7 @@ end
48
48
  ```
49
49
 
50
50
 
51
- Of course, decoding in practice is more complex than that. Some messages can have multiple parts, and AIS messages have their own complicated payload. NMEAPlus provides a SourceDecoder object that operates on IO objects (anything with `each_line` support) -- a File, SerialPort, etc. You can iterate over each message (literally `each_message`), or receive only fully assembled multipart messages by iterating over `each_complete_message`.
51
+ Of course, decoding in practice is more complex than that. Some messages can have multiple parts, and AIS messages have their own complicated payload. NMEAPlus provides a `SourceDecoder` class that operates on `IO` objects (anything with `each_line` support) -- a `File`, `SerialPort`, etc. You can iterate over each message (literally `each_message`), or receive only fully assembled multipart messages by iterating over `each_complete_message`.
52
52
 
53
53
  ```ruby
54
54
  require 'nmea_plus'
@@ -86,6 +86,8 @@ end
86
86
 
87
87
  ## Design documents
88
88
 
89
+ ### NMEA
90
+
89
91
  This gem was coded to accept the standard NMEA messages defined in the unoffical spec found here:
90
92
  http://www.catb.org/gpsd/NMEA.txt
91
93
 
@@ -93,18 +95,23 @@ Because the message types are standard, if no override is found for a particular
93
95
 
94
96
  > AAM, ALM, APA, APB, BOD, BWC, BWR, BWW, DBK, DBS, DBT, DCN, DPT, DTM, FSI, GBS, GGA, GLC, GLL, GNS, GRS, GSA, GST, GSV, GTD, GXA, HDG, HDM, HDT, HFB, HSC, ITS, LCD, MSK, MSS, MTW, MWV, OLN, OSD, R00, RMA, RMB, RMC, ROT, RPM, RSA, RSD, RTE, SFI, STN, TDS, TFI, TPC, TPR, TPT, TRF, TTM, VBW, VDR, VHW, VLW, VPW, VTG, VWR, WCV, WNC, WPL, XDR, XTE, XTR, ZDA, ZFO, ZTG
95
97
 
98
+ Support for proprietary NMEA messages is also possible. PASHR is included as proof-of-concept.
99
+
100
+ ### AIS
96
101
 
97
- Additionally, AIS message type definitions were implemented from the unofficial spec found here:
102
+ AIS message type definitions were implemented from the unofficial spec found here:
98
103
  http://catb.org/gpsd/AIVDM.html
99
104
 
100
- > Currently AIVDM messages types 1, 2, 3, 4, 5, 8, 9, 12, 14, 18, 19, 20, 21, and 24 are implemented.
105
+ Currently, the following AIVDM message types are supported:
106
+
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
101
109
 
102
- Support for proprietary messages is also possible. PASHR is included as proof-of-concept.
103
110
 
104
111
 
105
112
  ## Disclaimer
106
113
 
107
- This module was written from information scraped together on the web, not from testing on actual devices. Please don't entrust your life or the safety of your ship to this code without doing your own rigorous testing.
114
+ This module was written from information scraped together on the web, not from testing on actual devices. A lack of test cases -- especially for more obscure message types -- is a barrier to completeness. Please don't entrust your life or the safety of a ship to this code without doing your own rigorous testing.
108
115
 
109
116
 
110
117
  ## Author
@@ -28,6 +28,7 @@ require_relative "vdm_payload/vdm_msg18" # also includes 19
28
28
  require_relative "vdm_payload/vdm_msg20"
29
29
  require_relative "vdm_payload/vdm_msg21"
30
30
  require_relative "vdm_payload/vdm_msg24"
31
+ require_relative "vdm_payload/vdm_msg27"
31
32
 
32
33
  module NMEAPlus
33
34
  module Message
@@ -0,0 +1,198 @@
1
+
2
+ module NMEAPlus
3
+ module Message
4
+ module AIS
5
+ module VDMPayload
6
+ # Basic tools for interpreting the armored payload encoding
7
+ class Payload
8
+
9
+ def initialize; end
10
+
11
+ # @return [String] The raw "armored payload" in the original message
12
+ attr_accessor :payload_bitstring
13
+
14
+ # @return [Integer] The number of padding characters required to bring the payload to a 6 bit boundary
15
+ attr_accessor :fill_bits
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 fmt_arg Any argument necessary for the formatting function
23
+ # @param fmt_arg2 Any other argument necessary for the formatting function
24
+ # @param fmt_arg3 Any other argument necessary for the formatting function
25
+ # @macro [attach] payload_reader
26
+ # @!attribute [r] $1
27
+ # @return The field defined by the $3 bits starting at payload bit $2, formatted with the function {#$4}($5, $6, $7)
28
+ def self.payload_reader(name, start_bit, length, formatter, fmt_arg = nil, fmt_arg2 = nil, fmt_arg3 = nil)
29
+ args = [start_bit, length]
30
+ args << fmt_arg unless fmt_arg.nil?
31
+ args << fmt_arg2 unless fmt_arg2.nil?
32
+ args << fmt_arg3 unless fmt_arg3.nil?
33
+ self.class_eval("def #{name};#{formatter}(#{args.join(', ')});end")
34
+ end
35
+
36
+ # Convert 6-bit ascii to a character, according to http://catb.org/gpsd/AIVDM.html#_ais_payload_data_types
37
+ # @param ord [Integer] The 6-bit ascii code
38
+ # @return [String] the character for that code
39
+ def _6b_ascii(ord)
40
+ '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
41
+ end
42
+
43
+ # Access part of the payload. If there aren't bytes, there, return nil
44
+ # else execute a block
45
+ # @param start [Integer] The index of the first bit in the payload field
46
+ # @param length [Integer] The number of bits in the payload field
47
+ # @return Nil or whatever is yielded by the block
48
+ # @yield [String] A binary coded string ("010010101" etc)
49
+ def _access(start, length)
50
+ part = @payload_bitstring[start, length]
51
+ return nil if part.nil? || part.empty?
52
+ yield part
53
+ end
54
+
55
+ # pull out 6b chunks from the payload, then convert those to their more familar characters
56
+ # @param start [Integer] The index of the first bit in the payload field
57
+ # @param length [Integer] The number of bits in the payload field
58
+ # @return [String]
59
+ def _6b_string(start, length)
60
+ _bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
61
+ end
62
+
63
+ # pull out 8b chunks from the payload, then convert those to their more familar characters
64
+ # @param start [Integer] The index of the first bit in the payload field
65
+ # @param length [Integer] The number of bits in the payload field
66
+ # @return [String]
67
+ def _8b_data_string(start, length)
68
+ _bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
69
+ end
70
+
71
+ # Slice a part of the payload into binary chunks of a given size
72
+ # @param start [Integer] The index of the first bit in the payload field
73
+ # @param length [Integer] The number of bits in the payload field
74
+ # @return [Array<String>] Strings representing binary ("01010101" etc) for each slice
75
+ def _bit_slices(start, length, chunk_size)
76
+ _access(start, length) { |bits| bits.chars.each_slice(chunk_size) }
77
+ end
78
+
79
+ # convert a 6b string but trim off the 0s ('@')
80
+ # @param start [Integer] The index of the first bit in the payload field
81
+ # @param length [Integer] The number of bits in the payload field
82
+ # @return [String]
83
+ def _6b_string_nullterminated(start, length)
84
+ _6b_string(start, length).split("@", 2)[0]
85
+ end
86
+
87
+ # directly convert a string to a binary number as you'd read it
88
+ # @param start [Integer] The index of the first bit in the payload field
89
+ # @param length [Integer] The number of bits in the payload field
90
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
91
+ # @return [Integer] an unsigned integer value
92
+ def _6b_unsigned_integer(start, length, equiv_nil = nil)
93
+ ret = _access(start, length) { |bits| bits.to_i(2) }
94
+ return nil if ret == equiv_nil
95
+ ret
96
+ end
97
+
98
+ # perform a twos complement operation on part of the payload
99
+ # @param start [Integer] The index of the first bit in the payload field
100
+ # @param length [Integer] The number of bits in the payload field
101
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
102
+ # @return [Integer] an integer value
103
+ def _6b_integer(start, length, equiv_nil = nil)
104
+ case @payload_bitstring[start]
105
+ when "0"
106
+ _6b_unsigned_integer(start, length, equiv_nil)
107
+ when "1"
108
+ # MSB is 1 for negative
109
+ # two's complement: flip bits, then add 1
110
+ ret = _access(start, length) { |bits| (bits.tr("01", "10").to_i(2) + 1) * -1 }
111
+ return nil if ret == equiv_nil
112
+ ret
113
+ end
114
+ end
115
+
116
+ # scale an integer by dividing it by a denominator
117
+ # @param start [Integer] The index of the first bit in the payload field
118
+ # @param length [Integer] The number of bits in the payload field
119
+ # @param denominator [Integer] The divisor to use in scaling down the result
120
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
121
+ # @return [Integer] an integer value
122
+ def _6b_integer_scaled(start, length, denominator, equiv_nil = nil)
123
+ ret = _6b_integer(start, length, equiv_nil)
124
+ return nil if ret.nil?
125
+ ret.to_f / denominator
126
+ end
127
+
128
+ # scale an unsigned integer by dividing it by a denominator
129
+ # @param start [Integer] The index of the first bit in the payload field
130
+ # @param length [Integer] The number of bits in the payload field
131
+ # @param denominator [Integer] The divisor to use in scaling down the result
132
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
133
+ # @return [Integer] an integer value
134
+ def _6b_unsigned_integer_scaled(start, length, denominator, equiv_nil = nil)
135
+ ret = _6b_unsigned_integer(start, length, equiv_nil)
136
+ return nil if ret.nil?
137
+ ret.to_f / denominator
138
+ end
139
+
140
+ # scale an integer by dividing it by a denominator
141
+ # @param start [Integer] The index of the first bit in the payload field
142
+ # @param length [Integer] The number of bits in the payload field
143
+ # @param denominator [Integer] The divisor to use in scaling down the result
144
+ # @param shift [Float] the amount to shift (up) the result by
145
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
146
+ # @return [Integer] an integer value
147
+ def _6b_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil)
148
+ ret = _6b_integer_scaled(start, length, denominator, equiv_nil)
149
+ return nil if ret.nil?
150
+ ret + shift
151
+ end
152
+
153
+ # scale an unsigned integer by dividing it by a denominator
154
+ # @param start [Integer] The index of the first bit in the payload field
155
+ # @param length [Integer] The number of bits in the payload field
156
+ # @param denominator [Integer] The divisor to use in scaling down the result
157
+ # @param shift [Float] the amount to shift (up) the result by
158
+ # @param equiv_nil [Integer] If applicable, the value for this field that would indicate nil
159
+ # @return [Integer] an integer value
160
+ def _6b_unsigned_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil)
161
+ ret = _6b_unsigned_integer_scaled(start, length, denominator, equiv_nil)
162
+ return nil if ret.nil?
163
+ ret + shift
164
+ end
165
+
166
+ # Get the value of a bit in the payload
167
+ # @param start [Integer] The index of the first bit in the payload field
168
+ # @param _ [Integer] Doesn't matter. Here so signatures match; we hard-code 1 because it's 1 bit.
169
+ # @return [bool]
170
+ def _6b_boolean(start, _)
171
+ _access(start, 1) { |bits| bits.to_i == 1 }
172
+ end
173
+
174
+ # Return a string representing binary
175
+ # @param start [Integer] The index of the first bit in the payload field
176
+ # @param length [Integer] The number of bits in the payload field
177
+ # @return [String] e.g. "0101010101011000"
178
+ def _2b_data_string(start, length)
179
+ _access(start, length)
180
+ end
181
+
182
+ # 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
191
+ alias_method :_UU, :_6b_unsigned_integer_scaled_shifted
192
+ alias_method :_II, :_6b_integer_scaled_shifted
193
+
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
@@ -1,3 +1,4 @@
1
+ require_relative "payload"
1
2
 
2
3
  module NMEAPlus
3
4
  module Message
@@ -5,158 +6,12 @@ module NMEAPlus
5
6
  # There are many VDM payload types, and this is their container. See {VDMMsg}.
6
7
  module VDMPayload
7
8
  # The base class for the AIS payload (of {NMEAPlus::Message::AIS::VDM}), which uses its own encoding for its own subtypes
8
- class VDMMsg
9
-
10
- def initialize; end
11
-
12
- # @return [String] The raw "armored payload" in the original message
13
- attr_accessor :payload_bitstring
14
-
15
- # @return [Integer] The number of padding characters required to bring the payload to a 6 bit boundary
16
- attr_accessor :fill_bits
17
-
18
- # make our own shortcut syntax for payload attributes
19
- # @param name [String] What the accessor will be called
20
- # @param start_bit [Integer] The index of first bit of this field in the payload
21
- # @param length [Integer] The number of bits in this field
22
- # @param formatter [Symbol] The symbol for the formatting function to apply to the field (optional)
23
- # @param formatter_arg Any argument necessary for the formatting function
24
- # @macro [attach] payload_reader
25
- # @!attribute [r] $1
26
- # @return The field defined by $3 bits starting at bit $2 of the payload, formatted with the function {#$4}($5)
27
- def self.payload_reader(name, start_bit, length, formatter, formatter_arg = nil)
28
- if formatter_arg.nil?
29
- self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length});end")
30
- else
31
- self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length}, #{formatter_arg});end")
32
- end
33
- end
9
+ class VDMMsg < NMEAPlus::Message::AIS::VDMPayload::Payload
34
10
 
35
11
  payload_reader :message_type, 0, 6, :_u
36
12
  payload_reader :repeat_indicator, 6, 2, :_u
37
13
  payload_reader :source_mmsi, 8, 30, :_u
38
14
 
39
- # Convert 6-bit ascii to a character, according to http://catb.org/gpsd/AIVDM.html#_ais_payload_data_types
40
- # @param ord [Integer] The 6-bit ascii code
41
- # @return [String] the character for that code
42
- def _6b_ascii(ord)
43
- '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
44
- end
45
-
46
- # Access part of the payload. If there aren't bytes, there, return nil
47
- # else execute a block
48
- # @param start [Integer] The index of the first bit in the payload field
49
- # @param length [Integer] The number of bits in the payload field
50
- # @return Nil or whatever is yielded by the block
51
- # @yield [String] A binary coded string ("010010101" etc)
52
- def _access(start, length)
53
- part = @payload_bitstring[start, length]
54
- return nil if part.nil? || part.empty?
55
- yield part
56
- end
57
-
58
- # pull out 6b chunks from the payload, then convert those to their more familar characters
59
- # @param start [Integer] The index of the first bit in the payload field
60
- # @param length [Integer] The number of bits in the payload field
61
- # @return [String]
62
- def _6b_string(start, length)
63
- _bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
64
- end
65
-
66
- # pull out 8b chunks from the payload, then convert those to their more familar characters
67
- # @param start [Integer] The index of the first bit in the payload field
68
- # @param length [Integer] The number of bits in the payload field
69
- # @return [String]
70
- def _8b_data_string(start, length)
71
- _bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
72
- end
73
-
74
- # Slice a part of the payload into binary chunks of a given size
75
- # @param start [Integer] The index of the first bit in the payload field
76
- # @param length [Integer] The number of bits in the payload field
77
- # @return [Array<String>] Strings representing binary ("01010101" etc) for each slice
78
- def _bit_slices(start, length, chunk_size)
79
- _access(start, length) { |bits| bits.chars.each_slice(chunk_size) }
80
- end
81
-
82
- # convert a 6b string but trim off the 0s ('@')
83
- # @param start [Integer] The index of the first bit in the payload field
84
- # @param length [Integer] The number of bits in the payload field
85
- # @return [String]
86
- def _6b_string_nullterminated(start, length)
87
- _6b_string(start, length).split("@", 2)[0]
88
- end
89
-
90
- # directly convert a string to a binary number as you'd read it
91
- # @param start [Integer] The index of the first bit in the payload field
92
- # @param length [Integer] The number of bits in the payload field
93
- # @return [Integer] an unsigned integer value
94
- def _6b_unsigned_integer(start, length)
95
- _access(start, length) { |bits| bits.to_i(2) }
96
- end
97
-
98
- # perform a twos complement operation on part of the payload
99
- # @param start [Integer] The index of the first bit in the payload field
100
- # @param length [Integer] The number of bits in the payload field
101
- # @return [Integer] an integer value
102
- def _6b_twoscomplement(start, length)
103
- # two's complement: flip bits, then add 1
104
- _access(start, length) { |bits| bits.tr("01", "10").to_i(2) + 1 }
105
- end
106
-
107
- # @param start [Integer] The index of the first bit in the payload field
108
- # @param length [Integer] The number of bits in the payload field
109
- # @return [Integer] an integer value
110
- def _6b_integer(start, length)
111
- # MSB is 1 for negative
112
- twoc = _6b_twoscomplement(start, length)
113
- twoc && twoc * (@payload_bitstring[start] == 0 ? 1 : -1)
114
- end
115
-
116
- # scale an integer by dividing it by 10^decimal_places
117
- # @param start [Integer] The index of the first bit in the payload field
118
- # @param length [Integer] The number of bits in the payload field
119
- # @param decimal_places [Integer] The power of ten to use in scaling down the result
120
- # @return [Integer] an integer value
121
- def _6b_integer_scaled(start, length, decimal_places)
122
- _6b_integer(start, length).to_f / (10 ** decimal_places)
123
- end
124
-
125
- # scale an unsigned integer by dividing it by 10^decimal_places
126
- # @param start [Integer] The index of the first bit in the payload field
127
- # @param length [Integer] The number of bits in the payload field
128
- # @param decimal_places [Integer] The power of ten to use in scaling the result
129
- # @return [Integer] an integer value
130
- def _6b_unsigned_integer_scaled(start, length, decimal_places)
131
- _6b_unsigned_integer(start, length).to_f / (10.0 ** decimal_places)
132
- end
133
-
134
- # Get the value of a bit in the payload
135
- # @param start [Integer] The index of the first bit in the payload field
136
- # @param _ [Integer] Doesn't matter. Here so signatures match; we hard-code 1 because it's 1 bit.
137
- # @return [bool]
138
- def _6b_boolean(start, _)
139
- _access(start, 1) { |bits| bits.to_i == 1 }
140
- end
141
-
142
- # Return a string representing binary
143
- # @param start [Integer] The index of the first bit in the payload field
144
- # @param length [Integer] The number of bits in the payload field
145
- # @return [String] e.g. "0101010101011000"
146
- def _2b_data_string(start, length)
147
- _access(start, length)
148
- end
149
-
150
- # use shorthand for data types as defined in http://catb.org/gpsd/AIVDM.html
151
- alias_method :_u, :_6b_unsigned_integer
152
- alias_method :_U, :_6b_unsigned_integer_scaled
153
- alias_method :_i, :_6b_integer
154
- alias_method :_I, :_6b_integer_scaled
155
- alias_method :_b, :_6b_boolean
156
- alias_method :_e, :_6b_unsigned_integer
157
- alias_method :_t, :_6b_string_nullterminated
158
- alias_method :_d, :_2b_data_string
159
-
160
15
  # The ship cargo type description lookup table
161
16
  # @param code [Integer] The cargo type id
162
17
  # @return [String] Cargo type description
@@ -231,6 +86,25 @@ module NMEAPlus
231
86
  980_000_000 < source_mmsi && source_mmsi < 990_000_000
232
87
  end
233
88
 
89
+ # @param code [Integer] The navigational status id
90
+ # @return [String] Navigational status description
91
+ def get_navigational_status_description(code)
92
+ return nil if code.nil?
93
+ 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"
104
+ end
105
+ "Reserved for future use"
106
+ end
107
+
234
108
  end
235
109
 
236
110
  # We haven't defined all the AIS payload types, so this is a catch-all