Module: Meshtastic::Forwarder
- Defined in:
- lib/meshtastic/forwarder.rb
Overview
libcotshrink protobuf decoding. Detail is a schema-level hash, not CoT XML.
Defined Under Namespace
Classes: UnsupportedFormat
Constant Summary collapse
- MAX_BYTES =
1_048_576- SOURCES =
%w[DTED0 DTED1 DTED2 DTED3 LIDAR PFI USER ??? GPS SRTM1 COT PRI CALC ESTIMATED RTK DGPS GPS_PPS].freeze
- EXTENSIONS =
[ [:how, 3, %w[h-e h-g-i-g-o m-g]], [:geopointsrc, 6, SOURCES], [:altsrc, 6, SOURCES], [:role, 5, ['Team Member', 'Team Lead', 'HQ', 'Sniper', 'Medic', 'Forward Observer', 'RTO', 'K9']], [:battery, 8, nil], [:readiness, 2, :boolean], [:labels_on, 2, :boolean], [:height_unit, 4, nil], [:ce_human_input, 2, :boolean], [:tog, 2, :boolean], [:route_planning_method, 2, %w[Infil Exfil]], [:route_method, 4, %w[Driving Walking Flying Swimming Watercraft]], [:route_type, 2, ['On Foot', 'Vehicle']], [:route_route_type, 2, %w[Primary Secondary]], [:route_order, 2, ['Ascending Check Points', 'Descending Check Points']], [:route_stroke, 8, nil] ].freeze
Class Method Summary collapse
- .authors ⇒ Object
- .decode(opts = {}) ⇒ Object
-
.decode_chunks(opts = {}) ⇒ Object
Stateless reassembly: callers group only chunks known to belong together.
-
.decode_packet(opts = {}) ⇒ Object
Packet payload includes the upstream (index << 4 | count) byte.
- .help ⇒ Object
Class Method Details
.authors ⇒ Object
72 73 74 |
# File 'lib/meshtastic/forwarder.rb', line 72 public_class_method def self. '0day Inc.; libcotshrink schema and format: paulmandal (MIT)' end |
.decode(opts = {}) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/meshtastic/forwarder.rb', line 27 public_class_method def self.decode(opts = {}) bytes = opts[:payload].to_s.b limit = byte_limit(opts) raise ArgumentError, 'Forwarder input exceeds byte limit' if bytes.bytesize > limit compression = bytes.start_with?("\x1f\x8b".b) ? :gzip : :none bytes = gunzip(payload: bytes, max_bytes: limit) if compression == :gzip raise UnsupportedFormat, 'libcotshrink EXI requires a schema-less EXI grammar decoder; not implemented in Ruby' if bytes.start_with?('$EXI') || (bytes.getbyte(0) && (bytes.getbyte(0) & 0xC0) == 0x80) = ForwarderProtobuf::CotEvent.decode(bytes) extensions = unpack_extensions(value: .customBytesExt) event = unpack_event(message: , start_of_year: opts[:start_of_year]) event[:how] = extensions[:how] { format: :libcotshrink_protobuf, compression: compression, protobuf: .to_h, event: event, extensions: extensions } rescue Google::Protobuf::ParseError => e raise ArgumentError, "invalid libcotshrink protobuf: #{e.}" end |
.decode_chunks(opts = {}) ⇒ Object
Stateless reassembly: callers group only chunks known to belong together.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/meshtastic/forwarder.rb', line 57 public_class_method def self.decode_chunks(opts = {}) chunks = opts[:chunks] raise ArgumentError, 'Forwarder requires one to fifteen chunks' unless chunks.is_a?(Array) && (1..15).cover?(chunks.length) fragments = chunks.map { |payload| parse_fragment(payload: payload) } count = fragments.first[:count] raise ArgumentError, 'inconsistent Forwarder chunk counts' unless fragments.all? { |fragment| fragment[:count] == count } raise ArgumentError, 'duplicate Forwarder chunk index' unless fragments.map { |fragment| fragment[:index] }.uniq.length == fragments.length raise ArgumentError, 'missing Forwarder chunks' unless fragments.length == count raise ArgumentError, 'Forwarder reassembly exceeds byte limit' if fragments.sum { |fragment| fragment[:body].bytesize } > byte_limit(opts) bytes = fragments.sort_by { |fragment| fragment[:index] }.map { |fragment| fragment[:body] }.join.b decode_body(opts.merge(payload: bytes)) end |
.decode_packet(opts = {}) ⇒ Object
Packet payload includes the upstream (index << 4 | count) byte.
49 50 51 52 53 54 |
# File 'lib/meshtastic/forwarder.rb', line 49 public_class_method def self.decode_packet(opts = {}) fragment = parse_fragment(opts.merge({})) return fragment unless fragment[:count] == 1 decode_body(opts.merge(payload: fragment[:body])) end |
.help ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/meshtastic/forwarder.rb', line 76 public_class_method def self.help puts "USAGE: # Decode reassembled libcotshrink event bytes. #{self}.decode( payload: 'required - reassembled protobuf or GZIP bytes without chunk header', max_bytes: 'optional - input and decompressed byte ceiling up to 1048576', start_of_year: 'optional - explicit Time epoch matching sender year and timezone; otherwise return offsets' ) # Decode one header-bearing port 257 packet. #{self}.decode_packet( payload: 'required - one complete Meshtastic Forwarder port payload', max_bytes: 'optional - maximum input and decompressed bytes', start_of_year: 'optional - explicit Time epoch for sender timestamps' ) # Reassemble an explicitly grouped complete message. #{self}.decode_chunks( chunks: 'required - array of header-bearing packets from one message and sender', max_bytes: 'optional - maximum reassembled and decompressed bytes', start_of_year: 'optional - explicit Time epoch for sender timestamps' ) # List decoder authors and upstream attribution. #{self}.authors " end |