nmea_plus 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +116 -0
- data/{gem/lib → lib}/nmea_plus.rb +19 -3
- data/lib/nmea_plus/ais_message_factory.rb +23 -0
- data/{gem/lib → lib}/nmea_plus/generated_parser/parser.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/generated_parser/tokenizer.rb +1 -1
- data/{gem/lib → lib}/nmea_plus/message/ais/base_ais.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/ais/vdm.rb +5 -4
- data/{gem/lib → lib}/nmea_plus/message/ais/vdm_payload/vdm_msg.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/ais/vdm_payload/vdm_msg1.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/ais/vdm_payload/vdm_msg5.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/ais/vdm_payload/vdm_msg8.rb +0 -0
- data/lib/nmea_plus/message/base.rb +235 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/aam.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/alm.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/apa.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/apb.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/base_nmea.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/bod.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/bwc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/bwr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/bww.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dbk.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dbs.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dbt.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dcn.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dpt.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/dtm.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/fsi.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gbs.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gga.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/glc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gll.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gns.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/grs.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gsa.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gst.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gsv.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gtd.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/gxa.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/hdg.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/hdm.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/hdt.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/hfb.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/hsc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/its.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/lcd.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/msk.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/mss.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/mtw.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/mwv.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/oln.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/osd.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/pashr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/r00.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rma.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rmb.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rmc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rot.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rpm.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rsa.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rsd.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/rte.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/sfi.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/stn.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/tds.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/tfi.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/tpc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/tpr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/tpt.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/trf.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/ttm.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vbw.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vdr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vhw.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vlw.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vpw.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vtg.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/vwr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/wcv.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/wnc.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/wpl.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/xdr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/xte.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/xtr.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/zda.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/zfo.rb +0 -0
- data/{gem/lib → lib}/nmea_plus/message/nmea/ztg.rb +0 -0
- data/lib/nmea_plus/message_factory.rb +92 -0
- data/{gem/lib → lib}/nmea_plus/nmea_message_factory.rb +5 -0
- data/{gem/lib → lib}/nmea_plus/version.rb +1 -1
- metadata +117 -96
- data/gem/lib/nmea_plus/ais_message_factory.rb +0 -17
- data/gem/lib/nmea_plus/message/base.rb +0 -172
- data/gem/lib/nmea_plus/message_factory.rb +0 -68
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,172 +0,0 @@
|
|
1
|
-
|
2
|
-
class Class
|
3
|
-
# make our own shortcut syntax for message attributes
|
4
|
-
def field_reader(name, field_num, formatter = nil)
|
5
|
-
if formatter.nil?
|
6
|
-
self.class_eval("def #{name};@fields[#{field_num}];end")
|
7
|
-
else
|
8
|
-
self.class_eval("def #{name};#{formatter}(@fields[#{field_num}]);end")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
module NMEAPlus
|
14
|
-
module Message
|
15
|
-
class Base
|
16
|
-
attr_accessor :prefix
|
17
|
-
attr_reader :payload
|
18
|
-
attr_reader :fields
|
19
|
-
attr_accessor :checksum
|
20
|
-
attr_accessor :interpreted_data_type
|
21
|
-
attr_accessor :next_part
|
22
|
-
|
23
|
-
field_reader :data_type, 0, nil
|
24
|
-
|
25
|
-
def original
|
26
|
-
"#{prefix}#{payload}*#{checksum}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def payload=(val)
|
30
|
-
@payload = val
|
31
|
-
@fields = val.split(',', -1)
|
32
|
-
end
|
33
|
-
|
34
|
-
def checksum_ok?
|
35
|
-
calculated_checksum.upcase == checksum.upcase
|
36
|
-
end
|
37
|
-
|
38
|
-
def all_checksums_ok?
|
39
|
-
return false unless checksum_ok?
|
40
|
-
return true if @next_part.nil?
|
41
|
-
@next_part.all_checksums_ok?
|
42
|
-
end
|
43
|
-
|
44
|
-
def calculated_checksum
|
45
|
-
"%02x" % @payload.each_byte.map(&:ord).reduce(:^)
|
46
|
-
end
|
47
|
-
|
48
|
-
# many messages override these fields
|
49
|
-
def total_messages
|
50
|
-
1
|
51
|
-
end
|
52
|
-
|
53
|
-
# sequence number
|
54
|
-
def message_number
|
55
|
-
1
|
56
|
-
end
|
57
|
-
|
58
|
-
# create a linked list (O(n) implementation; message parts assumed to be < 10) of message parts
|
59
|
-
def add_message_part(msg)
|
60
|
-
if @next_part.nil?
|
61
|
-
@next_part = msg
|
62
|
-
else
|
63
|
-
@next_part.add_message_part(msg)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def all_messages_received?
|
68
|
-
total_messages == highest_contiguous_index
|
69
|
-
end
|
70
|
-
|
71
|
-
def highest_contiguous_index
|
72
|
-
_highest_contiguous_index(0)
|
73
|
-
end
|
74
|
-
|
75
|
-
def _highest_contiguous_index(last_index)
|
76
|
-
mn = message_number # just in case this is expensive to compute
|
77
|
-
return last_index if mn - last_index != 1 # indicating a skip or restart
|
78
|
-
return mn if @next_part.nil? # indicating we're the last message
|
79
|
-
@next_part._highest_contiguous_index(mn) # recurse down
|
80
|
-
end
|
81
|
-
|
82
|
-
######################### Conversion functions
|
83
|
-
|
84
|
-
# convert DDMM.MMM to single decimal value.
|
85
|
-
# sign_letter can be N,S,E,W
|
86
|
-
def _degrees_minutes_to_decimal(dm_string, sign_letter = "")
|
87
|
-
return nil if dm_string.nil? || dm_string.empty?
|
88
|
-
r = /(\d+)(\d{2}\.\d+)/ # (some number of digits) (2 digits for minutes).(decimal minutes)
|
89
|
-
m = r.match(dm_string)
|
90
|
-
raw = m.values_at(1)[0].to_f + (m.values_at(2)[0].to_f / 60)
|
91
|
-
_nsew_signed_float(raw, sign_letter)
|
92
|
-
end
|
93
|
-
|
94
|
-
# Use cardinal directions to assign positive or negative to mixed_val
|
95
|
-
# mixed_val can be string or float
|
96
|
-
# Of possible directions NSEW (sign_letter) treat N/E as + and S/W as -
|
97
|
-
def _nsew_signed_float(mixed_val, sign_letter = "")
|
98
|
-
value = mixed_val.to_f
|
99
|
-
value *= -1 if !sign_letter.empty? && "SW".include?(sign_letter.upcase)
|
100
|
-
value
|
101
|
-
end
|
102
|
-
|
103
|
-
# integer or nil
|
104
|
-
def _integer(field)
|
105
|
-
return nil if field.nil? || field.empty?
|
106
|
-
field.to_i
|
107
|
-
end
|
108
|
-
|
109
|
-
# float or nil
|
110
|
-
def _float(field)
|
111
|
-
return nil if field.nil? || field.empty?
|
112
|
-
field.to_f
|
113
|
-
end
|
114
|
-
|
115
|
-
# string or nil
|
116
|
-
def _string(field)
|
117
|
-
return nil if field.nil? || field.empty?
|
118
|
-
field
|
119
|
-
end
|
120
|
-
|
121
|
-
# hex to int or nil
|
122
|
-
def _hex_to_integer(field)
|
123
|
-
return nil if field.nil? || field.empty?
|
124
|
-
field.hex
|
125
|
-
end
|
126
|
-
|
127
|
-
# utc time or nil (HHMMSS or HHMMSS.SS)
|
128
|
-
def _utctime_hms(field)
|
129
|
-
return nil if field.nil? || field.empty?
|
130
|
-
re_format = /(\d{2})(\d{2})(\d{2}(\.\d+)?)/
|
131
|
-
now = Time.now
|
132
|
-
begin
|
133
|
-
hms = re_format.match(field)
|
134
|
-
Time.new(now.year, now.month, now.day, hms[1].to_i, hms[2].to_i, hms[3].to_f)
|
135
|
-
rescue
|
136
|
-
nil
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# time interval or nil (HHMMSS or HHMMSS.SS)
|
141
|
-
def _interval_hms(field)
|
142
|
-
return nil if field.nil? || field.empty?
|
143
|
-
re_format = /(\d{2})(\d{2})(\d{2}(\.\d+)?)/
|
144
|
-
begin
|
145
|
-
hms = re_format.match(field)
|
146
|
-
Time.new(0, 0, 0, hms[1].to_i, hms[2].to_i, hms[3].to_f)
|
147
|
-
rescue
|
148
|
-
nil
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def _utc_date_time(d_field, t_field)
|
153
|
-
return nil if t_field.nil? || t_field.empty?
|
154
|
-
return nil if d_field.nil? || d_field.empty?
|
155
|
-
|
156
|
-
# get formats and time
|
157
|
-
time_format = /(\d{2})(\d{2})(\d{2}(\.\d+)?)/
|
158
|
-
date_format = /(\d{2})(\d{2})(\d{2})/
|
159
|
-
|
160
|
-
# crunch numbers
|
161
|
-
begin
|
162
|
-
dmy = date_format.match(d_field)
|
163
|
-
hms = time_format.match(t_field)
|
164
|
-
Time.new(2000 + dmy[3].to_i, dmy[2].to_i, dmy[1].to_i, hms[1].to_i, hms[2].to_i, hms[3].to_f)
|
165
|
-
rescue
|
166
|
-
nil
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
|
2
|
-
module NMEAPlus
|
3
|
-
|
4
|
-
# meant to be extended. Creates message classes with a common prefix and variable suffix
|
5
|
-
class MessageFactory
|
6
|
-
|
7
|
-
# factory class must be extended
|
8
|
-
def self.parent_module
|
9
|
-
"FIXME_parent_module"
|
10
|
-
end
|
11
|
-
|
12
|
-
# a way to override the data_type (e.g. __AAM) with GPAAM to get a match
|
13
|
-
# whatever factory extends this class should override this method
|
14
|
-
def self.alternate_data_type(data_type)
|
15
|
-
data_type # in basic implementation, there is no alternative.
|
16
|
-
end
|
17
|
-
|
18
|
-
# check whether a given object exists. this will work for all consts but shhhhhhhhh
|
19
|
-
def self.message_class_exists?(class_identifier)
|
20
|
-
Object::const_get(class_identifier)
|
21
|
-
return true
|
22
|
-
rescue ::NameError
|
23
|
-
return false
|
24
|
-
end
|
25
|
-
|
26
|
-
# shortcut for the full name to a message class
|
27
|
-
def self.message_class_name(data_type)
|
28
|
-
"NMEAPlus::Message::#{self.parent_module}::#{data_type}"
|
29
|
-
end
|
30
|
-
|
31
|
-
# use the actual type if we have it, else try an alternate (and let it fail there)
|
32
|
-
def self.best_match_for_data_type(data_type)
|
33
|
-
return data_type if self.message_class_exists?(self.message_class_name(data_type))
|
34
|
-
self.alternate_data_type(data_type)
|
35
|
-
end
|
36
|
-
|
37
|
-
# get a message class through reflection
|
38
|
-
def self.dynamically_get_message_object(class_identifier)
|
39
|
-
Object::const_get(class_identifier).new
|
40
|
-
rescue ::NameError => e
|
41
|
-
raise ::NameError, "Couldn't instantiate a #{class_identifier} object: #{e}"
|
42
|
-
end
|
43
|
-
|
44
|
-
# Choose what class to create, and create it based on the first of the (unsplitted) fields
|
45
|
-
#
|
46
|
-
def self.create(message_prefix, fields, checksum)
|
47
|
-
# get the data type and adjust it if necessary (e.g. support for NMEA standard sentences like __AAM)
|
48
|
-
data_type = fields.split(',', 2)[0].upcase # assumed to be 'GPGGA', etc
|
49
|
-
interpreted_type = self.best_match_for_data_type(data_type)
|
50
|
-
class_name = self.message_class_name(interpreted_type)
|
51
|
-
|
52
|
-
# create message and make sure it's the right type
|
53
|
-
message = self.dynamically_get_message_object(class_name)
|
54
|
-
unless message.is_a? NMEAPlus::Message::Base
|
55
|
-
fail ArgumentError, "Undefined message type #{data_type} (classname #{class_name})"
|
56
|
-
end
|
57
|
-
|
58
|
-
# assign its data and return it
|
59
|
-
message.checksum = checksum
|
60
|
-
message.payload = fields
|
61
|
-
message.prefix = message_prefix
|
62
|
-
message.interpreted_data_type = interpreted_type
|
63
|
-
message
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|