nmea_plus 1.0.10 → 1.0.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4995804e2eba3842192e6ca02309efe1867b439a
4
- data.tar.gz: ff77b5f6dee8dc838fd7a62c321c0655f4c81033
3
+ metadata.gz: 49d631a6794e0a47ff2daef5fb5b4dac87623a5d
4
+ data.tar.gz: 16636c7450ccb964cedd1a3d76462999af63e967
5
5
  SHA512:
6
- metadata.gz: c0354acd88c7ca3c349d12d6be23e127ec8468e3db846eb99156ce8a15be2b6f461a60d3cd4473542f0ce6303ebdd6f15dcbcc8ac1aa9c0c27f6306843cf88a3
7
- data.tar.gz: fdbdb7dfbbc1164242a9226d0d19583f25180141b554db6681c4573219039a9a7fa0ec4198b1684dea6f8d47b86e9ea90935b2dbcd39a2a6cbfac434bda0f4ad
6
+ metadata.gz: 5250074e41c49831996adbcc4e9f36990257e157c3980d3bb04b48db678e2c5e7c4bd26563c0a5174854f86605c5db5427b5961e697e35882c8d7afc40cd41bb
7
+ data.tar.gz: 47e7032aaddbdb141fd4fd8175affc119ece1bd9d961f0434b8b6ff1ee7e7c47c79036187a5db93c262799218da2e37f66397386b8e342b85eab4c56689d2c2e
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # NMEA (GPS) and AIS Parser / Decoder for Ruby (nmea_plus)
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/nmea_plus.svg)](https://rubygems.org/gems/nmea_plus)
3
+ [![Gem Version](https://badge.fury.io/rb/nmea_plus.png)](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.10)
5
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/nmea_plus/1.0.11)
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
 
@@ -101,13 +101,16 @@ Support for proprietary NMEA messages is also possible. PASHR is included as pr
101
101
  AIS message type definitions were implemented from the unofficial spec found here:
102
102
  http://catb.org/gpsd/AIVDM.html
103
103
 
104
+ And some binary subtypes from the ITU spec found here:
105
+ https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1371-4-201004-S!!PDF-E.pdf
106
+
104
107
  The AIS payload can be found in the payload field of a `VDM` message (aka `!AIVDM`, `!ABVDM`, `!SAVDM`). Currently, the following AIS message types are supported:
105
108
 
106
109
  > 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 18, 19, 20, 21, 24, 27
107
110
 
108
- > Type 6 subtypes for DAC/FID: 235/10, 1022/61
111
+ > Type 6 subtypes for DAC/FID: 1/0, 1/2, 1/3, 1/4, 1/5, 235/10, 1022/61
109
112
 
110
- > Type 8 subtypes for DAC/FID: 1/22, 1/31, 366/56, 366/57
113
+ > Type 8 subtypes for DAC/FID: 1/0, 1/22, 1/31, 366/56, 366/57
111
114
 
112
115
 
113
116
  ## Disclaimer
@@ -6,7 +6,7 @@
6
6
 
7
7
 
8
8
  module NMEAPlus
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 `!`
9
+ class Decoder < Parser # The source file is in .rex format -- indentation and most yard documentation is impossible. This 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 `!`. In pratice, you should be using {NMEAPlus::SourceDecoder} (which wraps this class) to parse messages from an IO object; this class can only parse individual strings.
10
10
  require 'strscan'
11
11
 
12
12
  class ScanError < StandardError ; end
@@ -66,8 +66,9 @@ module NMEAPlus
66
66
  ptr = self
67
67
  ret = ""
68
68
  loop do
69
+ break if ptr.raw_ais_payload.nil? # guard against rare instances of message corruption
69
70
  ret << ptr.raw_ais_payload
70
- break if ptr.next_part.nil?
71
+ break if ptr.next_part.nil? # stop when we run out of messages in the chain
71
72
  ptr = ptr.next_part
72
73
  end
73
74
  ret
@@ -105,11 +106,7 @@ module NMEAPlus
105
106
  # @return [String] a binary encoded string
106
107
  def _dearmor6b(c, len = 6)
107
108
  val = c.ord
108
- if val >= 96
109
- ret = val - 56
110
- else
111
- ret = val - 48
112
- end
109
+ ret = val - (val >= 96 ? 56 : 48) # Mapped to 2 separate contiguous blocks of ascii, so choose which
113
110
  ret.to_s(2).rjust(6, "0")[0..(len - 1)]
114
111
  end
115
112
 
@@ -0,0 +1,393 @@
1
+
2
+ module NMEAPlus
3
+ module Message
4
+ module AIS
5
+ module VDMPayload
6
+
7
+ # A container for MMSI (Maritime Mobile Service Identity) information,
8
+ # all of which is indicated from the MMSI integer value itself.
9
+ class MMSIInfo
10
+
11
+ # The MMSI
12
+ # @return [Integer]
13
+ attr_reader :id
14
+
15
+ # @param mmsi [Integer] The MMSI
16
+ def initialize(mmsi)
17
+ @id = mmsi
18
+ end
19
+
20
+ # The MMSI category as defined by ITU-R M.585-7
21
+ # @!parse attr_reader :category
22
+ # @return [Symbol] The symbol for the MMSI category
23
+ def category
24
+ case id.to_s.rjust(9, '0') # formatted as 9 digit string with leading 0s
25
+ when /[2-7]......../ then :individual_ship
26
+ when /00...1.../ then :coast_station
27
+ when /00...2.../ then :harbor_station
28
+ when /00...3.../ then :pilot_station
29
+ when /00...4.../ then :ais_repeater_station
30
+ when /00......./ then :coast_station
31
+ when /111...1../ then :sar_aircraft_fixed
32
+ when /111...5../ then :sar_aircraft_helicopter
33
+ when /1......../ then :sar_aircraft
34
+ when /8......../ then :handheld
35
+ when /98......./ then :auxiliary_craft
36
+ when /970....../ then :sar_transmitter
37
+ when /972....../ then :man_overboard
38
+ when /974....../ then :epirb
39
+ when /99...1.../ then :aton_physical
40
+ when /99...6.../ then :aton_virtual
41
+ when /99......./ then :aton
42
+ else
43
+ :unknown_mmsi_category
44
+ end
45
+ end
46
+
47
+ # The MMSI category as defined by ITU-R M.585-7
48
+ # @!parse attr_reader :category_description
49
+ # @return [String] the human-readable description the MMSI category
50
+ def category_description
51
+ case category
52
+ when :individual_ship then "Individual ship"
53
+ when :coast_station then "Coast station"
54
+ when :harbor_station then "Harbor station"
55
+ when :pilot_station then "Pilot station"
56
+ when :ais_repeater_station then "AIS repeater station"
57
+ when :sar_aircraft then "SAR aircraft"
58
+ when :sar_aircraft_fixed then "SAR fixed-wing aircraft"
59
+ when :sar_aircraft_helicopter then "SAR helicopter"
60
+ when :aton_physical then "Physical AIS AtoN"
61
+ when :aton_virtual then "Virtual AIS AtoN"
62
+ when :aton then "AIS Aid to Navigation"
63
+ when :auxiliary_craft then "Auxiliary craft"
64
+ when :handheld then "Handheld transceiver"
65
+ when :sar_transmitter then "AIS-SART"
66
+ when :man_overboard then "MOB (Man Overboard)"
67
+ when :epirb then "EPIRB"
68
+ else
69
+ category.to_s
70
+ end
71
+ end
72
+
73
+ # The MMSI Maritime Identification Digits (MID), indicating country codes as specified by the ITU.
74
+ # http://www.itu.int/online/mms/glad/cga_mids.sh
75
+ # @!parse attr_reader :mid
76
+ # @return [Integer] the MID
77
+ def mid
78
+ range = case category
79
+ when :individual_ship then 0..2
80
+ when :coast_station, :harbor_station, :pilot_station, :ais_repeater_station then 2..4
81
+ when :sar_aircraft, :sar_aircraft_fixed, :sar_aircraft_helicopter then 3..5
82
+ when :aton_physical, :aton_virtual, :aton then 2..4
83
+ when :auxiliary_craft then 2..4
84
+ when :handheld then 1..3
85
+ when :sar_transmitter, :man_overboard, :epirb then 3..5
86
+ end
87
+ return nil if range.nil?
88
+ id.to_s.rjust(9, '0')[range].to_i
89
+ end
90
+
91
+ # The ISO 3166-1 country ID indicated by the MMSI Maritime Identification Digits (MID)
92
+ # @!parse attr_reader :country_id
93
+ # @return [Integer] the MID
94
+ def country_id
95
+ # https://github.com/S73417H/MIDs
96
+ # https://github.com/alexrabarts/iso_country_codes
97
+ # JSON.parse(IO.read("mids.json")).each {|k, v| puts "when #{k} then #{IsoCountryCodes.find(v[1]).numeric.to_i}" }
98
+ case mid
99
+ when 201 then 8
100
+ when 202 then 20
101
+ when 203 then 40
102
+ when 204 then 620
103
+ when 205 then 56
104
+ when 206 then 112
105
+ when 207 then 100
106
+ when 208 then 336
107
+ when 209 then 196
108
+ when 210 then 196
109
+ when 211 then 276
110
+ when 212 then 196
111
+ when 213 then 268
112
+ when 214 then 498
113
+ when 215 then 470
114
+ when 216 then 51
115
+ when 218 then 276
116
+ when 219 then 208
117
+ when 220 then 208
118
+ when 224 then 724
119
+ when 225 then 724
120
+ when 226 then 250
121
+ when 227 then 250
122
+ when 228 then 250
123
+ when 229 then 470
124
+ when 230 then 246
125
+ when 231 then 234
126
+ when 232 then 826
127
+ when 233 then 826
128
+ when 234 then 826
129
+ when 235 then 826
130
+ when 236 then 292
131
+ when 237 then 300
132
+ when 238 then 191
133
+ when 239 then 300
134
+ when 240 then 300
135
+ when 241 then 300
136
+ when 242 then 504
137
+ when 243 then 348
138
+ when 244 then 528
139
+ when 245 then 528
140
+ when 246 then 528
141
+ when 247 then 380
142
+ when 248 then 470
143
+ when 250 then 372
144
+ when 251 then 352
145
+ when 252 then 438
146
+ when 253 then 442
147
+ when 254 then 492
148
+ when 255 then 620
149
+ when 256 then 470
150
+ when 257 then 578
151
+ when 258 then 578
152
+ when 259 then 578
153
+ when 261 then 616
154
+ when 262 then 499
155
+ when 263 then 620
156
+ when 264 then 642
157
+ when 265 then 752
158
+ when 266 then 752
159
+ when 267 then 703
160
+ when 268 then 674
161
+ when 269 then 756
162
+ when 270 then 203
163
+ when 271 then 792
164
+ when 272 then 804
165
+ when 273 then 643
166
+ when 274 then 807
167
+ when 275 then 428
168
+ when 276 then 233
169
+ when 277 then 440
170
+ when 278 then 705
171
+ when 279 then 688
172
+ when 301 then 660
173
+ when 303 then 840
174
+ when 304 then 28
175
+ when 305 then 28
176
+ when 306 then 531
177
+ when 307 then 533
178
+ when 308 then 44
179
+ when 309 then 44
180
+ when 310 then 60
181
+ when 311 then 60
182
+ when 312 then 84
183
+ when 314 then 52
184
+ when 316 then 124
185
+ when 319 then 136
186
+ when 321 then 188
187
+ when 323 then 192
188
+ when 325 then 212
189
+ when 327 then 214
190
+ when 329 then 312
191
+ when 330 then 308
192
+ when 331 then 304
193
+ when 332 then 320
194
+ when 335 then 340
195
+ when 336 then 332
196
+ when 338 then 840
197
+ when 339 then 388
198
+ when 341 then 659
199
+ when 343 then 662
200
+ when 345 then 484
201
+ when 347 then 474
202
+ when 348 then 500
203
+ when 350 then 558
204
+ when 351 then 591
205
+ when 352 then 591
206
+ when 353 then 591
207
+ when 354 then 591
208
+ when 355 then 591
209
+ when 356 then 591
210
+ when 357 then 591
211
+ when 358 then 630
212
+ when 359 then 222
213
+ when 361 then 666
214
+ when 362 then 780
215
+ when 364 then 796
216
+ when 366 then 840
217
+ when 367 then 840
218
+ when 368 then 840
219
+ when 369 then 840
220
+ when 370 then 591
221
+ when 371 then 591
222
+ when 372 then 591
223
+ when 373 then 591
224
+ when 375 then 670
225
+ when 376 then 670
226
+ when 377 then 670
227
+ when 378 then 92
228
+ when 379 then 850
229
+ when 401 then 4
230
+ when 403 then 682
231
+ when 405 then 50
232
+ when 408 then 48
233
+ when 410 then 64
234
+ when 412 then 156
235
+ when 413 then 156
236
+ when 414 then 156
237
+ when 416 then 158
238
+ when 417 then 144
239
+ when 419 then 356
240
+ when 422 then 364
241
+ when 423 then 31
242
+ when 425 then 368
243
+ when 428 then 376
244
+ when 431 then 392
245
+ when 432 then 392
246
+ when 434 then 795
247
+ when 436 then 398
248
+ when 437 then 860
249
+ when 438 then 400
250
+ when 440 then 410
251
+ when 441 then 410
252
+ when 443 then 275
253
+ when 445 then 408
254
+ when 447 then 414
255
+ when 450 then 422
256
+ when 451 then 417
257
+ when 453 then 446
258
+ when 455 then 462
259
+ when 457 then 496
260
+ when 459 then 524
261
+ when 461 then 512
262
+ when 463 then 586
263
+ when 466 then 634
264
+ when 468 then 760
265
+ when 470 then 784
266
+ when 472 then 762
267
+ when 473 then 887
268
+ when 475 then 887
269
+ when 477 then 344
270
+ when 478 then 70
271
+ when 501 then 250
272
+ when 503 then 36
273
+ when 506 then 104
274
+ when 508 then 96
275
+ when 510 then 583
276
+ when 511 then 585
277
+ when 512 then 554
278
+ when 514 then 116
279
+ when 515 then 116
280
+ when 516 then 162
281
+ when 518 then 184
282
+ when 520 then 242
283
+ when 523 then 166
284
+ when 525 then 360
285
+ when 529 then 296
286
+ when 531 then 418
287
+ when 533 then 458
288
+ when 536 then 580
289
+ when 538 then 584
290
+ when 540 then 540
291
+ when 542 then 570
292
+ when 544 then 520
293
+ when 546 then 258
294
+ when 548 then 608
295
+ when 553 then 598
296
+ when 555 then 612
297
+ when 557 then 90
298
+ when 559 then 16
299
+ when 561 then 882
300
+ when 563 then 702
301
+ when 564 then 702
302
+ when 565 then 702
303
+ when 566 then 702
304
+ when 567 then 764
305
+ when 570 then 776
306
+ when 572 then 798
307
+ when 574 then 704
308
+ when 576 then 548
309
+ when 577 then 548
310
+ when 578 then 876
311
+ when 601 then 710
312
+ when 603 then 24
313
+ when 605 then 12
314
+ when 607 then 250
315
+ when 608 then 826
316
+ when 609 then 108
317
+ when 610 then 204
318
+ when 611 then 72
319
+ when 621 then 262
320
+ when 613 then 120
321
+ when 615 then 178
322
+ when 616 then 174
323
+ when 617 then 132
324
+ when 618 then 250
325
+ when 619 then 384
326
+ when 620 then 174
327
+ when 622 then 818
328
+ when 624 then 231
329
+ when 625 then 232
330
+ when 626 then 266
331
+ when 627 then 288
332
+ when 629 then 270
333
+ when 630 then 624
334
+ when 631 then 226
335
+ when 632 then 324
336
+ when 633 then 854
337
+ when 634 then 404
338
+ when 635 then 250
339
+ when 636 then 430
340
+ when 637 then 430
341
+ when 638 then 728
342
+ when 642 then 434
343
+ when 644 then 426
344
+ when 645 then 480
345
+ when 647 then 450
346
+ when 649 then 466
347
+ when 650 then 508
348
+ when 654 then 478
349
+ when 655 then 454
350
+ when 656 then 566
351
+ when 659 then 516
352
+ when 660 then 638
353
+ when 661 then 646
354
+ when 662 then 729
355
+ when 663 then 686
356
+ when 664 then 690
357
+ when 665 then 654
358
+ when 666 then 706
359
+ when 667 then 694
360
+ when 668 then 678
361
+ when 669 then 748
362
+ when 670 then 148
363
+ when 671 then 768
364
+ when 672 then 788
365
+ when 674 then 834
366
+ when 675 then 800
367
+ when 676 then 180
368
+ when 677 then 834
369
+ when 678 then 894
370
+ when 679 then 716
371
+ when 701 then 32
372
+ when 710 then 76
373
+ when 720 then 68
374
+ when 725 then 152
375
+ when 730 then 170
376
+ when 735 then 218
377
+ when 740 then 238
378
+ when 745 then 254
379
+ when 750 then 328
380
+ when 755 then 600
381
+ when 760 then 604
382
+ when 765 then 740
383
+ when 770 then 858
384
+ when 775 then 862
385
+ end
386
+ end
387
+
388
+ end
389
+
390
+ end
391
+ end
392
+ end
393
+ end
@@ -216,19 +216,19 @@ module NMEAPlus
216
216
  end
217
217
 
218
218
  # use shorthand for data types as defined in http://catb.org/gpsd/AIVDM.html
219
- alias_method :_u, :_6b_unsigned_integer
220
- alias_method :_U, :_6b_unsigned_integer_scaled
221
- alias_method :_i, :_6b_integer
222
- alias_method :_I, :_6b_integer_scaled
223
- alias_method :_b, :_6b_boolean
224
- alias_method :_nb, :_6b_negated_boolean
225
- alias_method :_e, :_6b_unsigned_integer
226
- alias_method :_t, :_6b_string_nullterminated
227
- alias_method :_tt, :_6b_string
228
- alias_method :_T, :_8b_data_string
229
- alias_method :_d, :_2b_data_string
230
- alias_method :_UU, :_6b_unsigned_integer_scaled_shifted
231
- alias_method :_II, :_6b_integer_scaled_shifted
219
+ alias _u _6b_unsigned_integer
220
+ alias _U _6b_unsigned_integer_scaled
221
+ alias _i _6b_integer
222
+ alias _I _6b_integer_scaled
223
+ alias _b _6b_boolean
224
+ alias _nb _6b_negated_boolean
225
+ alias _e _6b_unsigned_integer
226
+ alias _t _6b_string_nullterminated
227
+ alias _tt _6b_string
228
+ alias _T _8b_data_string
229
+ alias _d _2b_data_string
230
+ alias _UU _6b_unsigned_integer_scaled_shifted
231
+ alias _II _6b_integer_scaled_shifted
232
232
 
233
233
  end
234
234
  end
@@ -102,14 +102,21 @@ module NMEAPlus
102
102
  payload_reader :bearing4, 65, 10, :_u, 720
103
103
  payload_reader :distance4, 75, 10, :_u
104
104
 
105
+ # Container for bearing / distance
105
106
  class ShapePoint
106
107
  attr_accessor :bearing
107
108
  attr_accessor :distance
108
- attr_accessor :distance_meters
109
+ attr_accessor :scale_factor
110
+
111
+ # @!parse attr_reader :distance_meters
112
+ # @return [Integer] The scaled distance in meters
113
+ def distance_meters
114
+ distance * (10**scale_factor)
115
+ end
109
116
  end
110
117
 
111
118
  # @!parse attr_reader :points
112
- # @return [Array] Array of points
119
+ # @return [Array] Array of {ShapePoint} objects
113
120
  def points
114
121
  ret = []
115
122
 
@@ -119,7 +126,7 @@ module NMEAPlus
119
126
  sp = ShapePoint.new
120
127
  sp.bearing = send("bearing#{i}")
121
128
  sp.distance = d
122
- sp.distance_meters = d * scale_meters
129
+ sp.scale_factor = scale_Factor
123
130
  ret << sp
124
131
  end
125
132
  ret
@@ -1,4 +1,5 @@
1
1
  require_relative "payload"
2
+ require_relative "mmsi_info"
2
3
 
3
4
  module NMEAPlus
4
5
  module Message
@@ -12,368 +13,11 @@ module NMEAPlus
12
13
  payload_reader :repeat_indicator, 6, 2, :_u
13
14
  payload_reader :source_mmsi, 8, 30, :_u
14
15
 
15
- # The MMSI category as defined by ITU-R M.585-7
16
- # @!parse attr_reader :mmsi_category
17
- # @return [Symbol] The symbol for the MMSI category
18
- def mmsi_category
19
- case source_mmsi.to_s.rjust(9, '0') # formatted as 9 digit string with leading 0s
20
- when /[2-7]......../ then :individual_ship
21
- when /00...1.../ then :coast_station
22
- when /00...2.../ then :harbor_station
23
- when /00...3.../ then :pilot_station
24
- when /00...4.../ then :ais_repeater_station
25
- when /00......./ then :coast_station
26
- when /111...1../ then :sar_aircraft_fixed
27
- when /111...5../ then :sar_aircraft_helicopter
28
- when /1......../ then :sar_aircraft
29
- when /8......../ then :handheld
30
- when /98......./ then :auxiliary_craft
31
- when /970....../ then :sar_transmitter
32
- when /972....../ then :man_overboard
33
- when /974....../ then :epirb
34
- when /99...1.../ then :aton_physical
35
- when /99...6.../ then :aton_virtual
36
- when /99......./ then :aton
37
- else
38
- :unknown_mmsi_category
39
- end
40
- end
41
-
42
- # The MMSI Maritime Identification Digits (MID)
43
- # @!parse attr_reader :mid
44
- # @return [Integer] the MID
45
- def mid
46
- range = case mmsi_category
47
- when :individual_ship then 0..2
48
- when :coast_station, :harbor_station, :pilot_station, :ais_repeater_station then 2..4
49
- when :sar_aircraft, :sar_aircraft_fixed, :sar_aircraft_helicopter then 3..5
50
- when :aton_physical, :aton_virtual, :aton then 2..4
51
- when :auxiliary_craft then 2..4
52
- when :handheld then 1..3
53
- when :sar_transmitter, :man_overboard, :epirb then 3..5
54
- end
55
- return nil if range.nil?
56
- source_mmsi.to_s.rjust(9, '0')[range].to_i
57
- end
58
-
59
- # The ISO 3166-1 indicated by the MMSI Maritime Identification Digits (MID)
60
- # @!parse attr_reader :mid_country
61
- # @return [Integer] the MID
62
- def mid_country
63
- # https://github.com/S73417H/MIDs
64
- # https://github.com/alexrabarts/iso_country_codes
65
- # JSON.parse(IO.read("mids.json")).each {|k, v| puts "when #{k} then #{IsoCountryCodes.find(v[1]).numeric.to_i}" }
66
- case mid
67
- when 201 then 8
68
- when 202 then 20
69
- when 203 then 40
70
- when 204 then 620
71
- when 205 then 56
72
- when 206 then 112
73
- when 207 then 100
74
- when 208 then 336
75
- when 209 then 196
76
- when 210 then 196
77
- when 211 then 276
78
- when 212 then 196
79
- when 213 then 268
80
- when 214 then 498
81
- when 215 then 470
82
- when 216 then 51
83
- when 218 then 276
84
- when 219 then 208
85
- when 220 then 208
86
- when 224 then 724
87
- when 225 then 724
88
- when 226 then 250
89
- when 227 then 250
90
- when 228 then 250
91
- when 229 then 470
92
- when 230 then 246
93
- when 231 then 234
94
- when 232 then 826
95
- when 233 then 826
96
- when 234 then 826
97
- when 235 then 826
98
- when 236 then 292
99
- when 237 then 300
100
- when 238 then 191
101
- when 239 then 300
102
- when 240 then 300
103
- when 241 then 300
104
- when 242 then 504
105
- when 243 then 348
106
- when 244 then 528
107
- when 245 then 528
108
- when 246 then 528
109
- when 247 then 380
110
- when 248 then 470
111
- when 250 then 372
112
- when 251 then 352
113
- when 252 then 438
114
- when 253 then 442
115
- when 254 then 492
116
- when 255 then 620
117
- when 256 then 470
118
- when 257 then 578
119
- when 258 then 578
120
- when 259 then 578
121
- when 261 then 616
122
- when 262 then 499
123
- when 263 then 620
124
- when 264 then 642
125
- when 265 then 752
126
- when 266 then 752
127
- when 267 then 703
128
- when 268 then 674
129
- when 269 then 756
130
- when 270 then 203
131
- when 271 then 792
132
- when 272 then 804
133
- when 273 then 643
134
- when 274 then 807
135
- when 275 then 428
136
- when 276 then 233
137
- when 277 then 440
138
- when 278 then 705
139
- when 279 then 688
140
- when 301 then 660
141
- when 303 then 840
142
- when 304 then 28
143
- when 305 then 28
144
- when 306 then 531
145
- when 307 then 533
146
- when 308 then 44
147
- when 309 then 44
148
- when 310 then 60
149
- when 311 then 60
150
- when 312 then 84
151
- when 314 then 52
152
- when 316 then 124
153
- when 319 then 136
154
- when 321 then 188
155
- when 323 then 192
156
- when 325 then 212
157
- when 327 then 214
158
- when 329 then 312
159
- when 330 then 308
160
- when 331 then 304
161
- when 332 then 320
162
- when 335 then 340
163
- when 336 then 332
164
- when 338 then 840
165
- when 339 then 388
166
- when 341 then 659
167
- when 343 then 662
168
- when 345 then 484
169
- when 347 then 474
170
- when 348 then 500
171
- when 350 then 558
172
- when 351 then 591
173
- when 352 then 591
174
- when 353 then 591
175
- when 354 then 591
176
- when 358 then 630
177
- when 359 then 222
178
- when 361 then 666
179
- when 362 then 780
180
- when 364 then 796
181
- when 366 then 840
182
- when 367 then 840
183
- when 368 then 840
184
- when 369 then 840
185
- when 370 then 591
186
- when 371 then 591
187
- when 372 then 591
188
- when 373 then 591
189
- when 375 then 670
190
- when 376 then 670
191
- when 377 then 670
192
- when 378 then 92
193
- when 379 then 850
194
- when 401 then 4
195
- when 403 then 682
196
- when 405 then 50
197
- when 408 then 48
198
- when 410 then 64
199
- when 412 then 156
200
- when 413 then 156
201
- when 414 then 156
202
- when 416 then 158
203
- when 417 then 144
204
- when 419 then 356
205
- when 422 then 364
206
- when 423 then 31
207
- when 425 then 368
208
- when 428 then 376
209
- when 431 then 392
210
- when 432 then 392
211
- when 434 then 795
212
- when 436 then 398
213
- when 437 then 860
214
- when 438 then 400
215
- when 440 then 410
216
- when 441 then 410
217
- when 443 then 275
218
- when 445 then 408
219
- when 447 then 414
220
- when 450 then 422
221
- when 451 then 417
222
- when 453 then 446
223
- when 455 then 462
224
- when 457 then 496
225
- when 459 then 524
226
- when 461 then 512
227
- when 463 then 586
228
- when 466 then 634
229
- when 468 then 760
230
- when 470 then 784
231
- when 472 then 762
232
- when 473 then 887
233
- when 475 then 887
234
- when 477 then 344
235
- when 478 then 70
236
- when 501 then 250
237
- when 503 then 36
238
- when 506 then 104
239
- when 508 then 96
240
- when 510 then 583
241
- when 511 then 585
242
- when 512 then 554
243
- when 514 then 116
244
- when 515 then 116
245
- when 516 then 162
246
- when 518 then 184
247
- when 520 then 242
248
- when 523 then 166
249
- when 525 then 360
250
- when 529 then 296
251
- when 531 then 418
252
- when 533 then 458
253
- when 536 then 580
254
- when 538 then 584
255
- when 540 then 540
256
- when 542 then 570
257
- when 544 then 520
258
- when 546 then 258
259
- when 548 then 608
260
- when 553 then 598
261
- when 555 then 612
262
- when 557 then 90
263
- when 559 then 16
264
- when 561 then 882
265
- when 563 then 702
266
- when 564 then 702
267
- when 565 then 702
268
- when 566 then 702
269
- when 567 then 764
270
- when 570 then 776
271
- when 572 then 798
272
- when 574 then 704
273
- when 576 then 548
274
- when 577 then 548
275
- when 578 then 876
276
- when 601 then 710
277
- when 603 then 24
278
- when 605 then 12
279
- when 607 then 250
280
- when 608 then 826
281
- when 609 then 108
282
- when 610 then 204
283
- when 611 then 72
284
- when 621 then 262
285
- when 613 then 120
286
- when 615 then 178
287
- when 616 then 174
288
- when 617 then 132
289
- when 618 then 250
290
- when 619 then 384
291
- when 620 then 174
292
- when 622 then 818
293
- when 624 then 231
294
- when 625 then 232
295
- when 626 then 266
296
- when 627 then 288
297
- when 629 then 270
298
- when 630 then 624
299
- when 631 then 226
300
- when 632 then 324
301
- when 633 then 854
302
- when 634 then 404
303
- when 635 then 250
304
- when 636 then 430
305
- when 637 then 430
306
- when 638 then 728
307
- when 642 then 434
308
- when 644 then 426
309
- when 645 then 480
310
- when 647 then 450
311
- when 649 then 466
312
- when 650 then 508
313
- when 654 then 478
314
- when 655 then 454
315
- when 656 then 566
316
- when 659 then 516
317
- when 660 then 638
318
- when 661 then 646
319
- when 662 then 729
320
- when 663 then 686
321
- when 664 then 690
322
- when 665 then 654
323
- when 666 then 706
324
- when 667 then 694
325
- when 668 then 678
326
- when 669 then 748
327
- when 670 then 148
328
- when 671 then 768
329
- when 672 then 788
330
- when 674 then 834
331
- when 675 then 800
332
- when 676 then 180
333
- when 677 then 834
334
- when 678 then 894
335
- when 679 then 716
336
- when 701 then 32
337
- when 710 then 76
338
- when 720 then 68
339
- when 725 then 152
340
- when 730 then 170
341
- when 735 then 218
342
- when 740 then 238
343
- when 745 then 254
344
- when 750 then 328
345
- when 755 then 600
346
- when 760 then 604
347
- when 765 then 740
348
- when 770 then 858
349
- when 775 then 862
350
- end
351
- end
352
-
353
- # The MMSI category as defined by ITU-R M.585-7
354
- # @!parse attr_reader :mmsi_category_description
355
- # @return [String] the human-readable description the MMSI category
356
- def mmsi_category_description
357
- case mmsi_category
358
- when :individual_ship then "Individual ship"
359
- when :coast_station then "Coast station"
360
- when :harbor_station then "Harbor station"
361
- when :pilot_station then "Pilot station"
362
- when :ais_repeater_station then "AIS repeater station"
363
- when :sar_aircraft then "SAR aircraft"
364
- when :sar_aircraft_fixed then "SAR fixed-wing aircraft"
365
- when :sar_aircraft_helicopter then "SAR helicopter"
366
- when :aton_physical then "Physical AIS AtoN"
367
- when :aton_virtual then "Virtual AIS AtoN"
368
- when :aton then "AIS Aid to Navigation"
369
- when :auxiliary_craft then "Auxiliary craft"
370
- when :handheld then "Handheld transceiver"
371
- when :sar_transmitter then "AIS-SART"
372
- when :man_overboard then "MOB (Man Overboard)"
373
- when :epirb then "EPIRB"
374
- else
375
- mmsi_category.to_s
376
- end
16
+ # Detailed information produced from the MMSI
17
+ # @!parse attr_reader :source_mmsi_info
18
+ # @return [MMSIInfo] MMSI information structure
19
+ def source_mmsi_info
20
+ MMSIInfo.new(source_mmsi)
377
21
  end
378
22
 
379
23
  # The ship cargo type description lookup table
@@ -447,7 +91,7 @@ module NMEAPlus
447
91
 
448
92
  # An MMSI is associated with an auxiliary craft when it is of the form 98XXXYYYY
449
93
  def auxiliary_craft?
450
- mmsi_category == :auxiliary_craft
94
+ source_mmsi_info.category == :auxiliary_craft
451
95
  end
452
96
 
453
97
  # @param code [Integer] The navigational status id
@@ -1,4 +1,9 @@
1
1
  require_relative 'vdm_msg'
2
+ require_relative 'vdm_msg6d1f0'
3
+ require_relative 'vdm_msg6d1f2'
4
+ require_relative 'vdm_msg6d1f3'
5
+ require_relative 'vdm_msg6d1f4'
6
+ require_relative 'vdm_msg6d1f5'
2
7
  require_relative 'vdm_msg6d235f10'
3
8
  require_relative 'vdm_msg6d1022f61'
4
9
 
@@ -0,0 +1,17 @@
1
+ require_relative 'vdm_msg6_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 6: Binary Addressed Message Subtype: IFM 0: Text using 6-bit ASCII
9
+ class VDMMsg6d1f0 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg6DynamicPayload
10
+ payload_reader :acknowledge_required?, 88, 1, :_b
11
+ payload_reader :sequence_number, 89, 11, :_u
12
+ payload_reader :text, 100, 906, :_t
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'vdm_msg6_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 6: Binary Addressed Message Subtype: IFM 2: Interrogation for a specific FM
9
+ class VDMMsg6d1f2 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg6DynamicPayload
10
+ payload_reader :requested_dac, 88, 10, :_u
11
+ payload_reader :requested_fid, 98, 6, :_u
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'vdm_msg6_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 6: Binary Addressed Message Subtype: IFM 3: Capability interrogation
9
+ class VDMMsg6d1f3 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg6DynamicPayload
10
+ payload_reader :requested_dac, 88, 10, :_u
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'vdm_msg6_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 6: Binary Addressed Message Subtype: IFM 4: Capability reply
9
+ class VDMMsg6d1f4 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg6DynamicPayload
10
+
11
+ # The availibility table for functional IDs; there are 64 boolean entries
12
+ # @!parse attr_reader :fid_availability
13
+ # @return [Array] An array of booleans
14
+ def fid_availability
15
+ (0...128).step(2).to_a.map do |pos|
16
+ _6b_boolean(88 + pos, 1)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'vdm_msg6_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 6: Binary Addressed Message Subtype: IFM 5: Application acknowledgement to an addressed binary message
9
+ class VDMMsg6d1f5 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg6DynamicPayload
10
+ payload_reader :received_fm_dac, 88, 10, :_u
11
+ payload_reader :received_fm_fid, 98, 6, :_u
12
+ payload_reader :sequence_number, 104, 11, :_u
13
+ payload_reader :ai_available?, 115, 1, :_b
14
+ payload_reader :ai_response, 116, 3, :_e
15
+
16
+ # The AI response description
17
+ # @!parse attr_reader :ai_response_description
18
+ # @return [String] The description of the AI response
19
+ def ai_response_description
20
+ case ai_response
21
+ when 0 then "Unable to respond"
22
+ when 1 then "Reception acknowledged"
23
+ when 2 then "Response to follow"
24
+ when 3 then "Able to respond but currently inhibited"
25
+ else
26
+ "Reserved for future use"
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative 'vdm_msg'
2
+ require_relative 'vdm_msg8d1f0'
2
3
  require_relative 'vdm_msg8d1f22'
3
4
  require_relative 'vdm_msg8d1f31'
4
5
  require_relative 'vdm_msg8d366f56'
@@ -0,0 +1,17 @@
1
+ require_relative 'vdm_msg8_dynamic_payload'
2
+
3
+ module NMEAPlus
4
+ module Message
5
+ module AIS
6
+ module VDMPayload
7
+
8
+ # Type 8: Binary Addressed Message Subtype: IFM 0: Text using 6-bit ASCII
9
+ class VDMMsg8d1f0 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg8DynamicPayload
10
+ payload_reader :acknowledge_required?, 56, 1, :_b
11
+ payload_reader :sequence_number, 57, 11, :_u
12
+ payload_reader :text, 68, 906, :_t
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -63,7 +63,7 @@ module NMEAPlus
63
63
 
64
64
  # @return [bool] Whether the checksum calculated from the payload matches the checksum given in the message
65
65
  def checksum_ok?
66
- calculated_checksum.upcase == checksum.upcase
66
+ 0 == calculated_checksum.casecmp(checksum)
67
67
  end
68
68
 
69
69
  # return [bool] Whether the checksums for all available message parts are OK
@@ -1,3 +1,3 @@
1
1
  module NMEAPlus
2
- VERSION = '1.0.10'
2
+ VERSION = '1.0.11'.freeze
3
3
  end
data/lib/nmea_plus.rb CHANGED
@@ -3,14 +3,16 @@ require 'nmea_plus/version'
3
3
  require 'nmea_plus/generated_parser/parser'
4
4
  require 'nmea_plus/generated_parser/tokenizer'
5
5
 
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}.
6
+ # NMEAPlus contains classes for parsing and decoding NMEA and AIS messages, of which the {NMEAPlus::SourceDecoder}
7
+ # is most relevant. Parsed messages extend from the {Message::NMEA::NMEAMessage} object, and any binary
8
+ # AIS playloads are decoded into objects that extend from {Message::AIS::AISMessage}.
9
9
  # @author Ian Katz
10
10
  module NMEAPlus
11
11
 
12
- # The NMEA source decoder wraps an IO object, converting each_line functionality
13
- # to {#each_message} or {#each_complete_message}
12
+ # The SourceDecoder is meant as the primary entry point into the {NMEAPlus} module.
13
+ # It wraps a {Decoder} object and an IO object, converting IO's #each_line functionality
14
+ # to {#each_message} and/or {#each_complete_message},
15
+ # which yield {NMEAPlus::Message} objects representing the parsed data.
14
16
  class SourceDecoder
15
17
  # False by default.
16
18
  # @return [bool] whether to throw an exception on lines that don't properly parse
@@ -30,9 +32,20 @@ module NMEAPlus
30
32
  @decoder = NMEAPlus::Decoder.new
31
33
  end
32
34
 
33
- # Executes the block for every valid NMEA message in the source stream
35
+ # Executes the block for every valid NMEA message in the source stream.
36
+ # In practice, you should use {#each_complete_message} unless you have a very compelling reason not to.
34
37
  # @yield [NMEAPlus::Message] A parsed message
35
38
  # @return [void]
39
+ # @example
40
+ # input = "$GPGGA,123519,4807.038,N,01131.000,W,1,08,0.9,545.4,M,46.9,M,2.2,123*4b"
41
+ # io_source = StringIO.new(input) # source decoder works on any IO object
42
+ # source_decoder = NMEAPlus::SourceDecoder.new(io_source)
43
+ # source_decoder.each_message do |message|
44
+ # if "GGA" == message.interpreted_data_type
45
+ # puts "Latitude: #{message.latitude} / Longitude: #{message.longtitude}"
46
+ # # prints "Latitude: 48.1173 / Longitude: -11.516666666666666666"
47
+ # end
48
+ # end
36
49
  def each_message
37
50
  @source.each_line do |line|
38
51
  if @throw_on_parse_fail
@@ -49,25 +62,34 @@ module NMEAPlus
49
62
  end
50
63
  end
51
64
 
52
- # Executes the block for every valid NMEA message in the source stream, attempting to
53
- # group multipart messages into message chains.
65
+ # Attempts to group multipart NMEA messages into chains, and executes the block once for every complete chain.
66
+ #
54
67
  # @yield [NMEAPlus::Message] A parsed message that may contain subsequent parts
55
68
  # @return [void]
69
+ # @example
70
+ # input1 = "!AIVDM,2,1,0,A,58wt8Ui`g??r21`7S=:22058<v05Htp000000015>8OA;0sk,0*7B"
71
+ # input2 = "!AIVDM,2,2,0,A,eQ8823mDm3kP00000000000,2*5D"
72
+ # io_source = StringIO.new("#{input1}\n#{input2}") # source decoder works on any IO object
73
+ # source_decoder = NMEAPlus::SourceDecoder.new(io_source)
74
+ # source_decoder.each_complete_message do |message|
75
+ # if message.ais && message.ais.message_type == 5
76
+ # ais = message.ais # ais payload shortcut
77
+ # puts "Ship with MMSI #{ais.source_mmsi} (#{ais.name.strip}) is going to #{ais.destination.strip}"
78
+ # # prints "Ship with MMSI 603916439 (ARCO AVON) is going to HOUSTON"
79
+ # end
80
+ # end
56
81
  def each_complete_message
57
- partials = {}
82
+ partials = {} # hash of message type to message-chain-in-progress
58
83
  each_message do |msg|
59
- slot = msg.data_type
84
+ slot = msg.data_type # the slot in the hash
60
85
 
61
- if partials[slot].nil?
86
+ if partials[slot].nil? # no message in there
62
87
  partials[slot] = msg
63
- else
64
- # the message was already in there
65
- if 1 != (msg.message_number - partials[slot].message_number)
66
- # error! just overwrite what was there
67
- partials[slot] = msg
68
- else
69
- partials[slot].add_message_part(msg)
70
- end
88
+ elsif 1 != (msg.message_number - partials[slot].message_number) # broken sequence
89
+ # error! just overwrite what was there
90
+ partials[slot] = msg
91
+ else # chain on to what's there
92
+ partials[slot].add_message_part(msg)
71
93
  end
72
94
 
73
95
  # take action if we've completed the chain
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nmea_plus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Katz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-01-26 00:00:00.000000000 Z
11
+ date: 2016-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: racc
@@ -70,7 +70,7 @@ dependencies:
70
70
  requirements:
71
71
  - - ~>
72
72
  - !ruby/object:Gem::Version
73
- version: '0.31'
73
+ version: '0.36'
74
74
  - - '>='
75
75
  - !ruby/object:Gem::Version
76
76
  version: 0.31.0
@@ -80,7 +80,7 @@ dependencies:
80
80
  requirements:
81
81
  - - ~>
82
82
  - !ruby/object:Gem::Version
83
- version: '0.31'
83
+ version: '0.36'
84
84
  - - '>='
85
85
  - !ruby/object:Gem::Version
86
86
  version: 0.31.0
@@ -196,6 +196,7 @@ files:
196
196
  - lib/nmea_plus/generated_parser/tokenizer.rb
197
197
  - lib/nmea_plus/message/ais/base_ais.rb
198
198
  - lib/nmea_plus/message/ais/vdm.rb
199
+ - lib/nmea_plus/message/ais/vdm_payload/mmsi_info.rb
199
200
  - lib/nmea_plus/message/ais/vdm_payload/payload.rb
200
201
  - lib/nmea_plus/message/ais/vdm_payload/sub_area.rb
201
202
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg.rb
@@ -212,10 +213,16 @@ files:
212
213
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6.rb
213
214
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6_dynamic_payload.rb
214
215
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1022f61.rb
216
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1f0.rb
217
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1f2.rb
218
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1f3.rb
219
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1f4.rb
220
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d1f5.rb
215
221
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg6d235f10.rb
216
222
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg7.rb
217
223
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb
218
224
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8_dynamic_payload.rb
225
+ - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d1f0.rb
219
226
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d1f22.rb
220
227
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d1f31.rb
221
228
  - lib/nmea_plus/message/ais/vdm_payload/vdm_msg8d366f56.rb