Class: NMEAPlus::Message::AIS::VDMPayload::Payload

Inherits:
Object
  • Object
show all
Defined in:
lib/nmea_plus/message/ais/vdm_payload/payload.rb

Overview

Basic tools for interpreting the armored (binary) payload encoding of AIS. This class provides convenience functions for accessing the fields as the appropriate data type, as well as logic for AIS bit-level formats

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePayload

Returns a new instance of Payload.



13
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 13

def initialize; end

Instance Attribute Details

#fill_bitsInteger

Returns The number of padding characters required to bring the payload to a 6 bit boundary.

Returns:

  • (Integer)

    The number of padding characters required to bring the payload to a 6 bit boundary



19
20
21
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 19

def fill_bits
  @fill_bits
end

#payload_bitstringString

Returns The raw “armored payload” in the original message.

Returns:

  • (String)

    The raw “armored payload” in the original message



16
17
18
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 16

def payload_bitstring
  @payload_bitstring
end

Class Method Details

.payload_reader(name, start_bit, length, formatter, fmt_arg = nil, fmt_arg2 = nil, fmt_arg3 = nil) ⇒ void

This method returns an undefined value.

Enable a shortcut syntax for AIS payload attributes, in the style of ‘attr_accessor` metaprogramming. This is used to create a named field pointing to a specific bit range in the payload, applying a specific formatting function with up to 3 arguments as necessary

Parameters:

  • name (String)

    What the accessor will be called

  • start_bit (Integer)

    The index of first bit of this field in the payload

  • length (Integer)

    The number of bits in this field

  • formatter (Symbol)

    The symbol for the formatting function to apply to the field (optional)

  • fmt_arg (defaults to: nil)

    Any argument necessary for the formatting function

  • fmt_arg2 (defaults to: nil)

    Any other argument necessary for the formatting function

  • fmt_arg3 (defaults to: nil)

    Any other argument necessary for the formatting function



35
36
37
38
39
40
41
42
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 35

def self.payload_reader(name, start_bit, length, formatter, fmt_arg = nil, fmt_arg2 = nil, fmt_arg3 = nil)
  args = [start_bit, length]
  args << fmt_arg unless fmt_arg.nil?
  args << fmt_arg2 unless fmt_arg2.nil?
  args << fmt_arg3 unless fmt_arg3.nil?

  define_method(name) { self.send(formatter, *args) }
end

Instance Method Details

#_2b_data_string(start, length) ⇒ String Also known as: _d

Return a string representing binary digits. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Returns:

  • (String)

    e.g. “0101010101011000”



223
224
225
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 223

def _2b_data_string(start, length)
  _access(start, length)
end

#_6b_ascii(ord) ⇒ String

Convert 6-bit ascii to a character, according to catb.org/gpsd/AIVDM.html#_ais_payload_data_types

Parameters:

  • ord (Integer)

    The 6-bit ascii code

Returns:

  • (String)

    the character for that code



56
57
58
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 56

def _6b_ascii(ord)
  '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !"#$%&\'()*+,-./0123456789:;<=>?'[ord]
end

#_6b_boolean(start, _) ⇒ bool Also known as: _b

Get the value of a bit in the payload. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • _ (Integer)

    Doesn’t matter. Here so signatures match; we hard-code 1 because it’s 1 bit.

Returns:

  • (bool)


205
206
207
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 205

def _6b_boolean(start, _)
  _access(start, 1) { |bits| bits.to_i == 1 }
end

#_6b_integer(start, length, equiv_nil = nil) ⇒ Integer Also known as: _i

perform a twos complement operation on part of the payload. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an integer value



128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 128

def _6b_integer(start, length, equiv_nil = nil)
  case @payload_bitstring[start]
  when "0"
    _6b_unsigned_integer(start, length, equiv_nil)
  when "1"
    # MSB is 1 for negative
    # two's complement: flip bits, then add 1
    ret = _access(start, length) { |bits| (bits.tr("01", "10").to_i(2) + 1) * -1 }
    return nil if ret == equiv_nil

    ret
  end
end

#_6b_integer_scaled(start, length, denominator, equiv_nil = nil) ⇒ Integer Also known as: _I

scale an integer by dividing it by a denominator. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • denominator (Integer)

    The divisor to use in scaling down the result

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an integer value



149
150
151
152
153
154
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 149

def _6b_integer_scaled(start, length, denominator, equiv_nil = nil)
  ret = _6b_integer(start, length, equiv_nil)
  return nil if ret.nil?

  ret.to_f / denominator
end

#_6b_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil) ⇒ Integer Also known as: _II

scale an integer by dividing it by a denominator. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • denominator (Integer)

    The divisor to use in scaling down the result

  • shift (Float)

    the amount to shift (up) the result by

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an integer value



178
179
180
181
182
183
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 178

def _6b_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil)
  ret = _6b_integer_scaled(start, length, denominator, equiv_nil)
  return nil if ret.nil?

  ret + shift
end

#_6b_negated_boolean(start, _) ⇒ bool Also known as: _nb

Get the flipped value of a bit in the payload. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • _ (Integer)

    Doesn’t matter. Here so signatures match; we hard-code 1 because it’s 1 bit.

Returns:

  • (bool)


214
215
216
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 214

def _6b_negated_boolean(start, _)
  !_6b_boolean(start, 1)
end

#_6b_string(start, length) ⇒ String Also known as: _tt

pull out 6b chunks from the payload, then convert those to their more familar characters This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Returns:

  • (String)


78
79
80
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 78

def _6b_string(start, length)
  _bit_slices(start, length, 6).to_a.map(&:join).map { |x| _6b_ascii(x.to_i(2)) }.join
end

#_6b_string_nullterminated(start, length) ⇒ String Also known as: _t

convert a 6b string but trim off the 0s (‘@’). This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Returns:

  • (String)


105
106
107
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 105

def _6b_string_nullterminated(start, length)
  _6b_string(start, length).split("@", 2)[0]
end

#_6b_unsigned_integer(start, length, equiv_nil = nil) ⇒ Integer Also known as: _u, _e

directly convert a string to a binary number as you’d read it. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an unsigned integer value



115
116
117
118
119
120
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 115

def _6b_unsigned_integer(start, length, equiv_nil = nil)
  ret = _access(start, length) { |bits| bits.to_i(2) }
  return nil if ret == equiv_nil

  ret
end

#_6b_unsigned_integer_scaled(start, length, denominator, equiv_nil = nil) ⇒ Integer Also known as: _U

scale an unsigned integer by dividing it by a denominator. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • denominator (Integer)

    The divisor to use in scaling down the result

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an integer value



163
164
165
166
167
168
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 163

def _6b_unsigned_integer_scaled(start, length, denominator, equiv_nil = nil)
  ret = _6b_unsigned_integer(start, length, equiv_nil)
  return nil if ret.nil?

  ret.to_f / denominator
end

#_6b_unsigned_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil) ⇒ Integer Also known as: _UU

scale an unsigned integer by dividing it by a denominator. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

  • denominator (Integer)

    The divisor to use in scaling down the result

  • shift (Float)

    the amount to shift (up) the result by

  • equiv_nil (Integer) (defaults to: nil)

    If applicable, the value for this field that would indicate nil

Returns:

  • (Integer)

    an integer value



193
194
195
196
197
198
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 193

def _6b_unsigned_integer_scaled_shifted(start, length, denominator, shift, equiv_nil = nil)
  ret = _6b_unsigned_integer_scaled(start, length, denominator, equiv_nil)
  return nil if ret.nil?

  ret + shift
end

#_8b_data_string(start, length) ⇒ String Also known as: _T

pull out 8b chunks from the payload, then convert those to their more familar characters. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Returns:

  • (String)


87
88
89
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 87

def _8b_data_string(start, length)
  _bit_slices(start, length, 8).to_a.map(&:join).map { |x| x.to_i(2).chr }.join
end

#_access(start, length) {|String| ... } ⇒ Object

Access part of the payload. If there aren’t bytes, there, return nil. Else, execute a block

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Yields:

  • (String)

    A binary coded string (“010010101” etc)

Returns:

  • Nil or whatever is yielded by the block



66
67
68
69
70
71
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 66

def _access(start, length)
  part = @payload_bitstring[start, length]
  return nil if part.nil? || part.empty?

  block_given? ? (yield part) : part
end

#_bit_slices(start, length, chunk_size) ⇒ Array<String>

Slice a part of the payload into binary chunks of a given size. This function is meant to be passed as a formatter to payload_reader.

Parameters:

  • start (Integer)

    The index of the first bit in the payload field

  • length (Integer)

    The number of bits in the payload field

Returns:

  • (Array<String>)

    Strings representing binary (“01010101” etc) for each slice



96
97
98
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 96

def _bit_slices(start, length, chunk_size)
  _access(start, length) { |bits| bits.chars.each_slice(chunk_size) }
end

#_get_date_mdhm(month, day, hour, minute) ⇒ Object

Get the date value of a month/day/hour/minute package This function is meant to be used for commonly-found date operations in which year, seconds, and timezone are not specified.

Parameters:

  • month (Integer)

    the month number, 1-indexed, optional

  • day (Integer)

    the day number, 1-indexed

  • hour (Integer)

    the hour number, 0-indexed

  • minute (Integer)

    the minute number, 0-indexed



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 249

def _get_date_mdhm(month, day, hour, minute)
  now = Time.now
  month = now.month if month.nil?

  return nil if month.zero?
  return nil if day.zero?
  return nil if hour == 24
  return nil if minute == 60

  # try to be smart about picking a year
  rollover = 0
  rollover = 1 if now.month > month
  rollover = -1 if now.month == 1 && month == 12
  Time.new(now.year + rollover, month, day, hour, minute, 0, "+00:00")
end

#_object_by_name(class_identifier) ⇒ Object

Return an object by its class name, or nil if it isn’t defined

Parameters:

  • class_identifier (String)

    The ruby class identifier

Returns:

  • (Object)

    An object of the given class



47
48
49
50
51
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 47

def _object_by_name(class_identifier)
  Object::const_get(class_identifier).new
rescue ::NameError
  nil
end