nmea_plus 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/gem/lib/nmea_plus.rb +63 -0
- data/gem/lib/nmea_plus/ais_message_factory.rb +17 -0
- data/gem/lib/nmea_plus/generated_parser/parser.rb +127 -0
- data/gem/lib/nmea_plus/generated_parser/tokenizer.rb +100 -0
- data/gem/lib/nmea_plus/message/ais/base_ais.rb +20 -0
- data/gem/lib/nmea_plus/message/ais/vdm.rb +100 -0
- data/gem/lib/nmea_plus/message/ais/vdm_payload/vdm_msg.rb +99 -0
- data/gem/lib/nmea_plus/message/ais/vdm_payload/vdm_msg1.rb +65 -0
- data/gem/lib/nmea_plus/message/ais/vdm_payload/vdm_msg5.rb +38 -0
- data/gem/lib/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb +43 -0
- data/gem/lib/nmea_plus/message/base.rb +172 -0
- data/gem/lib/nmea_plus/message/nmea/aam.rb +17 -0
- data/gem/lib/nmea_plus/message/nmea/alm.rb +25 -0
- data/gem/lib/nmea_plus/message/nmea/apa.rb +20 -0
- data/gem/lib/nmea_plus/message/nmea/apb.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/base_nmea.rb +36 -0
- data/gem/lib/nmea_plus/message/nmea/bod.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/bwc.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/bwr.rb +25 -0
- data/gem/lib/nmea_plus/message/nmea/bww.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/dbk.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/dbs.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/dbt.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/dcn.rb +26 -0
- data/gem/lib/nmea_plus/message/nmea/dpt.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/dtm.rb +24 -0
- data/gem/lib/nmea_plus/message/nmea/fsi.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/gbs.rb +18 -0
- data/gem/lib/nmea_plus/message/nmea/gga.rb +32 -0
- data/gem/lib/nmea_plus/message/nmea/glc.rb +23 -0
- data/gem/lib/nmea_plus/message/nmea/gll.rb +21 -0
- data/gem/lib/nmea_plus/message/nmea/gns.rb +28 -0
- data/gem/lib/nmea_plus/message/nmea/grs.rb +24 -0
- data/gem/lib/nmea_plus/message/nmea/gsa.rb +27 -0
- data/gem/lib/nmea_plus/message/nmea/gst.rb +18 -0
- data/gem/lib/nmea_plus/message/nmea/gsv.rb +29 -0
- data/gem/lib/nmea_plus/message/nmea/gtd.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/gxa.rb +22 -0
- data/gem/lib/nmea_plus/message/nmea/hdg.rb +20 -0
- data/gem/lib/nmea_plus/message/nmea/hdm.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/hdt.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/hfb.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/hsc.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/its.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/lcd.rb +23 -0
- data/gem/lib/nmea_plus/message/nmea/msk.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/mss.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/mtw.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/mwv.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/oln.rb +22 -0
- data/gem/lib/nmea_plus/message/nmea/osd.rb +19 -0
- data/gem/lib/nmea_plus/message/nmea/pashr.rb +21 -0
- data/gem/lib/nmea_plus/message/nmea/r00.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/rma.rb +30 -0
- data/gem/lib/nmea_plus/message/nmea/rmb.rb +30 -0
- data/gem/lib/nmea_plus/message/nmea/rmc.rb +33 -0
- data/gem/lib/nmea_plus/message/nmea/rot.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/rpm.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/rsa.rb +17 -0
- data/gem/lib/nmea_plus/message/nmea/rsd.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/rte.rb +16 -0
- data/gem/lib/nmea_plus/message/nmea/sfi.rb +18 -0
- data/gem/lib/nmea_plus/message/nmea/stn.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/tds.rb +11 -0
- data/gem/lib/nmea_plus/message/nmea/tfi.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/tpc.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/tpr.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/tpt.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/trf.rb +28 -0
- data/gem/lib/nmea_plus/message/nmea/ttm.rb +22 -0
- data/gem/lib/nmea_plus/message/nmea/vbw.rb +16 -0
- data/gem/lib/nmea_plus/message/nmea/vdr.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/vhw.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/vlw.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/vpw.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/vtg.rb +41 -0
- data/gem/lib/nmea_plus/message/nmea/vwr.rb +15 -0
- data/gem/lib/nmea_plus/message/nmea/wcv.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/wnc.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/wpl.rb +19 -0
- data/gem/lib/nmea_plus/message/nmea/xdr.rb +14 -0
- data/gem/lib/nmea_plus/message/nmea/xte.rb +16 -0
- data/gem/lib/nmea_plus/message/nmea/xtr.rb +12 -0
- data/gem/lib/nmea_plus/message/nmea/zda.rb +23 -0
- data/gem/lib/nmea_plus/message/nmea/zfo.rb +13 -0
- data/gem/lib/nmea_plus/message/nmea/ztg.rb +13 -0
- data/gem/lib/nmea_plus/message_factory.rb +68 -0
- data/gem/lib/nmea_plus/nmea_message_factory.rb +110 -0
- data/gem/lib/nmea_plus/version.rb +3 -0
- metadata +285 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb6a15ea1c4b8d040614ae117703221df082cf53
|
4
|
+
data.tar.gz: 3d738a04a524c69e02c371befc3d1edd9f913a96
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f026b1f759c56c97952ea97b3b7a6190c0930e4cb02486a715cf704eefe5fa8c4f012c19b6b8778dd32be709be95cc671c23f267096635ddcd55d3f2e1d1074c
|
7
|
+
data.tar.gz: 9002792450538ed8f492db5f54707f1631bc44f27acede7a44868b82900755177da03974922565249ebde61b175fd56023aa5cf23878d0141b606dbad891ceb8
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'nmea_plus/version'
|
2
|
+
|
3
|
+
require 'nmea_plus/generated_parser/parser'
|
4
|
+
require 'nmea_plus/generated_parser/tokenizer'
|
5
|
+
|
6
|
+
module NMEAPlus
|
7
|
+
class SourceDecoder
|
8
|
+
attr_accessor :throw_on_parse_fail
|
9
|
+
attr_accessor :throw_on_unrecognized_type # typically for development
|
10
|
+
|
11
|
+
def initialize(line_reader)
|
12
|
+
unless line_reader.respond_to? :each_line
|
13
|
+
fail ArgumentError, "line_reader must inherit from type IO (or implement each_line)"
|
14
|
+
end
|
15
|
+
@throw_on_parse_fail = false
|
16
|
+
@source = line_reader
|
17
|
+
@decoder = NMEAPlus::Decoder.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# return each parsed message
|
21
|
+
def each_message
|
22
|
+
@source.each_line do |line|
|
23
|
+
if @throw_on_parse_fail
|
24
|
+
yield @decoder.parse(line)
|
25
|
+
else
|
26
|
+
begin
|
27
|
+
y = @decoder.parse(line)
|
28
|
+
yield y
|
29
|
+
rescue
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# return messages grouped into multipart chains as required
|
36
|
+
def each_complete_message
|
37
|
+
partials = {}
|
38
|
+
each_message do |msg|
|
39
|
+
slot = msg.data_type
|
40
|
+
|
41
|
+
if partials[slot].nil?
|
42
|
+
partials[slot] = msg
|
43
|
+
else
|
44
|
+
# the message was already in there
|
45
|
+
if 1 != (msg.message_number - partials[slot].message_number)
|
46
|
+
# error! just overwrite what was there
|
47
|
+
partials[slot] = msg
|
48
|
+
else
|
49
|
+
partials[slot].add_message_part(msg)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# take action if we've completed the chain
|
54
|
+
maybe_full = partials[slot]
|
55
|
+
if maybe_full.all_messages_received?
|
56
|
+
partials[slot] = nil
|
57
|
+
yield maybe_full
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
require_relative 'message_factory'
|
3
|
+
|
4
|
+
require_relative 'message/ais/vdm'
|
5
|
+
|
6
|
+
module NMEAPlus
|
7
|
+
class AISMessageFactory < MessageFactory
|
8
|
+
def self.parent_module
|
9
|
+
"AIS"
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.alternate_data_type(data_type)
|
13
|
+
# match last 3 digits (get rid of talker)
|
14
|
+
data_type[2..4]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
#
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by Racc 1.4.13
|
4
|
+
# from Racc grammer file "".
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'racc/parser.rb'
|
8
|
+
|
9
|
+
|
10
|
+
require_relative "../nmea_message_factory"
|
11
|
+
require_relative "../ais_message_factory"
|
12
|
+
|
13
|
+
module NMEAPlus
|
14
|
+
class Parser < Racc::Parser
|
15
|
+
|
16
|
+
module_eval(<<'...end parser.y/module_eval...', 'parser.y', 19)
|
17
|
+
|
18
|
+
# override racc's on_error so we can have context in our error messages
|
19
|
+
def on_error(t, val, vstack)
|
20
|
+
errcontext = (@ss.pre_match[-10..-1] || @ss.pre_match) +
|
21
|
+
@ss.matched + @ss.post_match[0..9]
|
22
|
+
line_number = @ss.pre_match.lines.count
|
23
|
+
raise ParseError, sprintf("parse error on value %s (%s) " +
|
24
|
+
"on line %s around \"%s\"",
|
25
|
+
val.inspect, token_to_str(t) || '?',
|
26
|
+
line_number, errcontext)
|
27
|
+
end
|
28
|
+
...end parser.y/module_eval...
|
29
|
+
##### State transition tables begin ###
|
30
|
+
|
31
|
+
racc_action_table = [
|
32
|
+
2, 3, 6, 5, 4, 7, 8, 9 ]
|
33
|
+
|
34
|
+
racc_action_check = [
|
35
|
+
0, 0, 3, 2, 1, 4, 5, 6 ]
|
36
|
+
|
37
|
+
racc_action_pointer = [
|
38
|
+
-3, 4, -2, -3, 5, 4, 5, nil, nil, nil ]
|
39
|
+
|
40
|
+
racc_action_default = [
|
41
|
+
-3, -3, -3, -3, -3, -3, -3, 10, -1, -2 ]
|
42
|
+
|
43
|
+
racc_goto_table = [
|
44
|
+
1 ]
|
45
|
+
|
46
|
+
racc_goto_check = [
|
47
|
+
1 ]
|
48
|
+
|
49
|
+
racc_goto_pointer = [
|
50
|
+
nil, 0 ]
|
51
|
+
|
52
|
+
racc_goto_default = [
|
53
|
+
nil, nil ]
|
54
|
+
|
55
|
+
racc_reduce_table = [
|
56
|
+
0, 0, :racc_error,
|
57
|
+
3, 7, :_reduce_1,
|
58
|
+
3, 7, :_reduce_2 ]
|
59
|
+
|
60
|
+
racc_reduce_n = 3
|
61
|
+
|
62
|
+
racc_shift_n = 10
|
63
|
+
|
64
|
+
racc_token_table = {
|
65
|
+
false => 0,
|
66
|
+
:error => 1,
|
67
|
+
:CSUM => 2,
|
68
|
+
:CASH => 3,
|
69
|
+
:BANG => 4,
|
70
|
+
:DATA => 5 }
|
71
|
+
|
72
|
+
racc_nt_base = 6
|
73
|
+
|
74
|
+
racc_use_result_var = true
|
75
|
+
|
76
|
+
Racc_arg = [
|
77
|
+
racc_action_table,
|
78
|
+
racc_action_check,
|
79
|
+
racc_action_default,
|
80
|
+
racc_action_pointer,
|
81
|
+
racc_goto_table,
|
82
|
+
racc_goto_check,
|
83
|
+
racc_goto_default,
|
84
|
+
racc_goto_pointer,
|
85
|
+
racc_nt_base,
|
86
|
+
racc_reduce_table,
|
87
|
+
racc_token_table,
|
88
|
+
racc_shift_n,
|
89
|
+
racc_reduce_n,
|
90
|
+
racc_use_result_var ]
|
91
|
+
|
92
|
+
Racc_token_to_s_table = [
|
93
|
+
"$end",
|
94
|
+
"error",
|
95
|
+
"CSUM",
|
96
|
+
"CASH",
|
97
|
+
"BANG",
|
98
|
+
"DATA",
|
99
|
+
"$start",
|
100
|
+
"start" ]
|
101
|
+
|
102
|
+
Racc_debug_parser = false
|
103
|
+
|
104
|
+
##### State transition tables end #####
|
105
|
+
|
106
|
+
# reduce 0 omitted
|
107
|
+
|
108
|
+
module_eval(<<'.,.,', 'parser.y', 8)
|
109
|
+
def _reduce_1(val, _values, result)
|
110
|
+
result = NMEAPlus::NMEAMessageFactory.create(val[0], val[1], val[2])
|
111
|
+
result
|
112
|
+
end
|
113
|
+
.,.,
|
114
|
+
|
115
|
+
module_eval(<<'.,.,', 'parser.y', 9)
|
116
|
+
def _reduce_2(val, _values, result)
|
117
|
+
result = NMEAPlus::AISMessageFactory.create(val[0], val[1], val[2])
|
118
|
+
result
|
119
|
+
end
|
120
|
+
.,.,
|
121
|
+
|
122
|
+
def _reduce_none(val, _values, result)
|
123
|
+
val[0]
|
124
|
+
end
|
125
|
+
|
126
|
+
end # class Parser
|
127
|
+
end # module NMEAPlus
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#--
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by rex 1.0.5
|
4
|
+
# from lexical definition file "/Users/iakatz/Code Base/nmea_plus/parser/tokenizer.rex".
|
5
|
+
#++
|
6
|
+
|
7
|
+
|
8
|
+
module NMEAPlus
|
9
|
+
class Decoder < Parser # not indented due to constraints of .rex format
|
10
|
+
require 'strscan'
|
11
|
+
|
12
|
+
class ScanError < StandardError ; end
|
13
|
+
|
14
|
+
attr_reader :lineno
|
15
|
+
attr_reader :filename
|
16
|
+
attr_accessor :state
|
17
|
+
|
18
|
+
def scan_setup(str)
|
19
|
+
@ss = StringScanner.new(str)
|
20
|
+
@lineno = 1
|
21
|
+
@state = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def action
|
25
|
+
yield
|
26
|
+
end
|
27
|
+
|
28
|
+
def scan_str(str)
|
29
|
+
scan_setup(str)
|
30
|
+
do_parse
|
31
|
+
end
|
32
|
+
alias :scan :scan_str
|
33
|
+
|
34
|
+
def load_file( filename )
|
35
|
+
@filename = filename
|
36
|
+
open(filename, "r") do |f|
|
37
|
+
scan_setup(f.read)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def scan_file( filename )
|
42
|
+
load_file(filename)
|
43
|
+
do_parse
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def next_token
|
48
|
+
return if @ss.eos?
|
49
|
+
|
50
|
+
# skips empty actions
|
51
|
+
until token = _next_token or @ss.eos?; end
|
52
|
+
token
|
53
|
+
end
|
54
|
+
|
55
|
+
def _next_token
|
56
|
+
text = @ss.peek(1)
|
57
|
+
@lineno += 1 if text == "\n"
|
58
|
+
token = case @state
|
59
|
+
when nil
|
60
|
+
case
|
61
|
+
when (text = @ss.scan(/\*[0-9A-F]{2}[\w\n\r]*/i))
|
62
|
+
action { [:CSUM, text[1..2]] }
|
63
|
+
|
64
|
+
when (text = @ss.scan(/\$/i))
|
65
|
+
action { [:CASH, text] }
|
66
|
+
|
67
|
+
when (text = @ss.scan(/!/i))
|
68
|
+
action { [:BANG, text] }
|
69
|
+
|
70
|
+
when (text = @ss.scan(/[^\*]+/i))
|
71
|
+
action { [:DATA, text] }
|
72
|
+
|
73
|
+
else
|
74
|
+
text = @ss.string[@ss.pos .. -1]
|
75
|
+
raise ScanError, "can not match: '" + text + "'"
|
76
|
+
end # if
|
77
|
+
|
78
|
+
else
|
79
|
+
raise ScanError, "undefined state: '" + state.to_s + "'"
|
80
|
+
end # case state
|
81
|
+
token
|
82
|
+
end # def _next_token
|
83
|
+
|
84
|
+
def parse(input)
|
85
|
+
@yydebug = true if ENV['DEBUG_RACC']
|
86
|
+
scan_str(input)
|
87
|
+
end
|
88
|
+
def tokenize(input)
|
89
|
+
scan_setup(input)
|
90
|
+
ret = []
|
91
|
+
last_token = nil
|
92
|
+
loop do
|
93
|
+
last_token = next_token
|
94
|
+
break if last_token.nil?
|
95
|
+
ret << last_token
|
96
|
+
end
|
97
|
+
ret
|
98
|
+
end
|
99
|
+
end # class
|
100
|
+
end # class
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require_relative "../base"
|
3
|
+
|
4
|
+
module NMEAPlus
|
5
|
+
module Message
|
6
|
+
module AIS
|
7
|
+
|
8
|
+
class AISMessage < NMEAPlus::Message::Base
|
9
|
+
def talker
|
10
|
+
data_type[0..1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def message_type
|
14
|
+
data_type[2..-1]
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require_relative "base_ais"
|
2
|
+
|
3
|
+
require_relative "vdm_payload/vdm_msg1"
|
4
|
+
require_relative "vdm_payload/vdm_msg5"
|
5
|
+
require_relative "vdm_payload/vdm_msg8"
|
6
|
+
|
7
|
+
=begin boilerplate for vdm payload objects
|
8
|
+
require_relative 'vdm_msg'
|
9
|
+
|
10
|
+
module NMEAPlus
|
11
|
+
module Message
|
12
|
+
module AIS
|
13
|
+
module VDMPayload
|
14
|
+
class VDMMsg5 < NMEAPlus::Message::AIS::VDMPayload::VDMMsg
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
=end
|
23
|
+
|
24
|
+
module NMEAPlus
|
25
|
+
module Message
|
26
|
+
module AIS
|
27
|
+
class VDM < NMEAPlus::Message::AIS::AISMessage
|
28
|
+
field_reader :total_messages, 1, :_integer
|
29
|
+
field_reader :message_number, 2, :_integer
|
30
|
+
field_reader :message_id, 3, :_string
|
31
|
+
field_reader :channel_code, 4, :_string
|
32
|
+
field_reader :raw_ais_payload, 5, :_string
|
33
|
+
field_reader :ais_payload_fill_bits, 6, :_integer
|
34
|
+
|
35
|
+
def ais
|
36
|
+
# factory method: find the appropriate message type class and instantiate it
|
37
|
+
p = full_dearmored_ais_payload
|
38
|
+
ret = _payload_container(p[0, 6].to_i(2))
|
39
|
+
ret.payload_bitstring = p
|
40
|
+
ret.fill_bits = last_ais_fill_bits
|
41
|
+
ret
|
42
|
+
end
|
43
|
+
|
44
|
+
# the full encoded payload as it was received
|
45
|
+
def full_armored_ais_payload
|
46
|
+
# get the full message and fill bits for the last one
|
47
|
+
ptr = self
|
48
|
+
ret = ""
|
49
|
+
loop do
|
50
|
+
ret << ptr.raw_ais_payload
|
51
|
+
break if ptr.next_part.nil?
|
52
|
+
ptr = ptr.next_part
|
53
|
+
end
|
54
|
+
ret
|
55
|
+
end
|
56
|
+
|
57
|
+
# a binary string ("0010101110110") representing the dearmored payload
|
58
|
+
def full_dearmored_ais_payload
|
59
|
+
data = full_armored_ais_payload
|
60
|
+
out = ""
|
61
|
+
# dearmor all but the last byte, then apply the fill bits to the last byte
|
62
|
+
data[0..-2].each_char { |c| out << _dearmor6b(c) }
|
63
|
+
out << _dearmor6b(data[-1], 6 - last_ais_fill_bits)
|
64
|
+
out
|
65
|
+
end
|
66
|
+
|
67
|
+
def last_ais_fill_bits
|
68
|
+
# get the fill bits for the last message in the sequence
|
69
|
+
ptr = self
|
70
|
+
fill_bits = nil
|
71
|
+
loop do
|
72
|
+
fill_bits = ptr.ais_payload_fill_bits
|
73
|
+
break if ptr.next_part.nil?
|
74
|
+
ptr = ptr.next_part
|
75
|
+
end
|
76
|
+
fill_bits.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
# perform the 6-bit to 8-bit conversion defined in the spec
|
80
|
+
def _dearmor6b(c, len = 6)
|
81
|
+
val = c.ord
|
82
|
+
if val >= 96
|
83
|
+
ret = val - 56
|
84
|
+
else
|
85
|
+
ret = val - 48
|
86
|
+
end
|
87
|
+
ret.to_s(2).rjust(6, "0")[0..(len - 1)]
|
88
|
+
end
|
89
|
+
|
90
|
+
def _payload_container(message_type_id)
|
91
|
+
class_identifier = "NMEAPlus::Message::AIS::VDMPayload::VDMMsg#{message_type_id}"
|
92
|
+
Object::const_get(class_identifier).new
|
93
|
+
rescue ::NameError
|
94
|
+
class_identifier = "NMEAPlus::Message::AIS::VDMPayload::VDMMsgUndefined" # generic
|
95
|
+
Object::const_get(class_identifier).new
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class Class
|
2
|
+
# make our own shortcut syntax for payload attributes
|
3
|
+
def payload_reader(name, start_bit, length, formatter, formatter_arg = nil)
|
4
|
+
if formatter_arg.nil?
|
5
|
+
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length});end")
|
6
|
+
else
|
7
|
+
self.class_eval("def #{name};#{formatter}(#{start_bit}, #{length}, #{formatter_arg});end")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module NMEAPlus
|
13
|
+
module Message
|
14
|
+
module AIS
|
15
|
+
module VDMPayload
|
16
|
+
class VDMMsg
|
17
|
+
attr_accessor :payload_bitstring
|
18
|
+
attr_accessor :fill_bits
|
19
|
+
|
20
|
+
payload_reader :message_type, 0, 6, :_u
|
21
|
+
payload_reader :repeat_indicator, 6, 2, :_u
|
22
|
+
payload_reader :source_mmsi, 8, 30, :_u
|
23
|
+
|
24
|
+
# lookup table for 6-bit ascii
|
25
|
+
def _6b_ascii(ord)
|
26
|
+
'@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
|
27
|
+
end
|
28
|
+
|
29
|
+
# convert an entire string from the payload
|
30
|
+
def _6b_string(start, length)
|
31
|
+
# pull out 6b chunks from the string, use their value as a lookup into the ascii array
|
32
|
+
_bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
|
33
|
+
end
|
34
|
+
|
35
|
+
def _8b_data_string(start, length)
|
36
|
+
_bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
|
37
|
+
end
|
38
|
+
|
39
|
+
def _bit_slices(start, length, chunk_size)
|
40
|
+
@payload_bitstring[start, length].chars.each_slice(chunk_size)
|
41
|
+
end
|
42
|
+
|
43
|
+
# convert a string but trim off the 0s ('@')
|
44
|
+
def _6b_string_nullterminated(start, length)
|
45
|
+
_6b_string(start, length).split("@", 2)[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
# directly convert a string to a binary number as you'd read it
|
49
|
+
def _6b_unsigned_integer(start, length)
|
50
|
+
@payload_bitstring[start, length].to_i(2)
|
51
|
+
end
|
52
|
+
|
53
|
+
# perform a twos complement operation on part of the payload
|
54
|
+
def _6b_twoscomplement(start, length)
|
55
|
+
# two's complement: flip bits, then add 1
|
56
|
+
@payload_bitstring[start, length].tr("01", "10").to_i(2) + 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def _6b_integer(start, length)
|
60
|
+
# MSB is 1 for negative
|
61
|
+
_6b_twoscomplement(start, length) * (@payload_bitstring[start] == 0 ? 1 : -1)
|
62
|
+
end
|
63
|
+
|
64
|
+
# scale an integer by dividing it by 10^decimal_places
|
65
|
+
def _6b_integer_scaled(start, length, decimal_places)
|
66
|
+
_6b_integer(start, length).to_f / (10 ** decimal_places)
|
67
|
+
end
|
68
|
+
|
69
|
+
# scale an unsigned integer by dividing it by 10^decimal_places
|
70
|
+
def _6b_unsigned_integer_scaled(start, length, decimal_places)
|
71
|
+
_6b_unsigned_integer(start, length).to_f / (10.0 ** decimal_places)
|
72
|
+
end
|
73
|
+
|
74
|
+
def _6b_boolean(start, _)
|
75
|
+
@payload_bitstring[start].to_i == 1
|
76
|
+
end
|
77
|
+
|
78
|
+
def _2b_data_string(start, length)
|
79
|
+
@payload_bitstring[start, length]
|
80
|
+
end
|
81
|
+
|
82
|
+
# use shorthand for data types as defined in http://catb.org/gpsd/AIVDM.html
|
83
|
+
alias_method :_u, :_6b_unsigned_integer
|
84
|
+
alias_method :_U, :_6b_unsigned_integer_scaled
|
85
|
+
alias_method :_i, :_6b_integer
|
86
|
+
alias_method :_I, :_6b_integer_scaled
|
87
|
+
alias_method :_b, :_6b_boolean
|
88
|
+
alias_method :_e, :_6b_unsigned_integer
|
89
|
+
alias_method :_t, :_6b_string_nullterminated
|
90
|
+
alias_method :_d, :_2b_data_string
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
class VDMMsgUndefined < VDMMsg; end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|