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.



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

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



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

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



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

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



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

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”



217
218
219
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 217

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



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

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)


199
200
201
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 199

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



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

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



147
148
149
150
151
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 147

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



174
175
176
177
178
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 174

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)


208
209
210
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 208

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
# 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



160
161
162
163
164
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 160

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



188
189
190
191
192
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 188

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



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

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



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/nmea_plus/message/ais/vdm_payload/payload.rb', line 243

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



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

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