Class: OpenC3::CrcProtocol

Inherits:
Protocol show all
Defined in:
lib/openc3/interfaces/protocols/crc_protocol.rb

Overview

Creates a CRC on write and verifies a CRC on read

Constant Summary collapse

ERROR =

on CRC mismatch

"ERROR"
DISCONNECT =

on CRC mismatch

"DISCONNECT"

Instance Attribute Summary

Attributes inherited from Protocol

#allow_empty_data, #extra, #interface

Instance Method Summary collapse

Methods inherited from Protocol

#connect_reset, #disconnect_reset, #post_write_interface, #protocol_cmd, #read_packet, #reset

Constructor Details

#initialize(write_item_name = nil, strip_crc = false, bad_strategy = "ERROR", bit_offset = -32,, bit_size = 32, endianness = 'BIG_ENDIAN', poly = nil, seed = nil, xor = nil, reflect = nil, allow_empty_data = nil) ⇒ CrcProtocol

Returns a new instance of CrcProtocol.

Parameters:

  • write_item_name (String/nil) (defaults to: nil)

    Item to fill with calculated CRC value for outgoing packets (nil = don't fill)

  • strip_crc (Boolean) (defaults to: false)

    Whether or not to remove the CRC from incoming packets

  • bad_strategy (ERROR/DISCONNECT) (defaults to: "ERROR")

    How to handle CRC errors on incoming packets. ERROR = Just log the error, DISCONNECT = Disconnect interface

  • bit_offset (Integer) (defaults to: -32,)

    Bit offset of the CRC in the data. Can be negative to indicate distance from end of packet

  • bit_size (Integer) (defaults to: 32)

    Bit size of the CRC - Must be 16, 32, or 64

  • endianness (BIG_ENDIAN/LITTLE_ENDIAN) (defaults to: 'BIG_ENDIAN')

    Endianness of the CRC

  • poly (Integer) (defaults to: nil)

    Polynomial to use when calculating the CRC

  • seed (Integer) (defaults to: nil)

    Seed value to start the calculation

  • xor (Boolean) (defaults to: nil)

    Whether to XOR the CRC result with 0xFFFF

  • reflect (Boolean) (defaults to: nil)

    Whether to bit reverse each byte of data before calculating the CRC

  • allow_empty_data (true/false/nil) (defaults to: nil)

    See Protocol#initialize



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/openc3/interfaces/protocols/crc_protocol.rb', line 45

def initialize(
  write_item_name = nil,
  strip_crc = false,
  bad_strategy = "ERROR",
  bit_offset = -32,
  bit_size = 32,
  endianness = 'BIG_ENDIAN',
  poly = nil,
  seed = nil,
  xor = nil,
  reflect = nil,
  allow_empty_data = nil
)
  super(allow_empty_data)
  @write_item_name = ConfigParser.handle_nil(write_item_name)
  @strip_crc = ConfigParser.handle_true_false(strip_crc)
  raise "Invalid strip CRC of '#{strip_crc}'. Must be TRUE or FALSE." unless !!@strip_crc == @strip_crc

  case bad_strategy
  when ERROR, DISCONNECT
    @bad_strategy = bad_strategy
  else
    raise "Invalid bad CRC strategy of #{bad_strategy}. Must be ERROR or DISCONNECT."
  end

  case endianness.to_s.upcase
  when 'BIG_ENDIAN'
    @endianness = :BIG_ENDIAN # Convert to symbol for use in BinaryAccessor.write
  when 'LITTLE_ENDIAN'
    @endianness = :LITTLE_ENDIAN # Convert to symbol for use in BinaryAccessor.write
  else
    raise "Invalid endianness '#{endianness}'. Must be BIG_ENDIAN or LITTLE_ENDIAN."
  end

  begin
    @bit_offset = Integer(bit_offset)
  rescue
    raise "Invalid bit offset of #{bit_offset}. Must be a number."
  end
  raise "Invalid bit offset of #{bit_offset}. Must be divisible by 8." if @bit_offset % 8 != 0

  poly = ConfigParser.handle_nil(poly)
  begin
    poly = Integer(poly) if poly
  rescue
    raise "Invalid polynomial of #{poly}. Must be a number."
  end

  seed = ConfigParser.handle_nil(seed)
  begin
    seed = Integer(seed) if seed
  rescue
    raise "Invalid seed of #{seed}. Must be a number."
  end

  xor = ConfigParser.handle_true_false_nil(xor)
  raise "Invalid XOR value of '#{xor}'. Must be TRUE or FALSE." if xor && !!xor != xor

  reflect = ConfigParser.handle_true_false_nil(reflect) if reflect
  raise "Invalid reflect value of '#{reflect}'. Must be TRUE or FALSE." if reflect && !!reflect != reflect

  # Built the CRC arguments array. All subsequent arguments are dependent
  # on the previous ones so we build it up incrementally.
  args = []
  if poly
    args << poly
    if seed
      args << seed
      unless xor.nil? # Can't check raw variable because it could be false
        args << xor
        unless reflect.nil? # Can't check raw variable because it could be false
          args << reflect
        end
      end
    end
  end

  @bit_size = bit_size.to_i
  case @bit_size
  when 16
    @pack = (@endianness == :BIG_ENDIAN) ? 'n' : 'v'
    if args.empty?
      @crc = Crc16.new
    else
      @crc = Crc16.new(*args)
    end
  when 32
    @pack = (@endianness == :BIG_ENDIAN) ? 'N' : 'V'
    if args.empty?
      @crc = Crc32.new
    else
      @crc = Crc32.new(*args)
    end
  when 64
    @pack = (@endianness == :BIG_ENDIAN) ? 'N' : 'V'
    if args.empty?
      @crc = Crc64.new
    else
      @crc = Crc64.new(*args)
    end
  else
    raise "Invalid bit size of #{bit_size}. Must be 16, 32, or 64."
  end
end

Instance Method Details

#read_data(data, extra = nil) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/openc3/interfaces/protocols/crc_protocol.rb', line 150

def read_data(data, extra = nil)
  return super(data, extra) if data.length <= 0

  crc = BinaryAccessor.read(@bit_offset, @bit_size, :UINT, data, @endianness)
  calculated_crc = @crc.calc(data[0...(@bit_offset / 8)])
  if calculated_crc != crc
    Logger.error "#{@interface ? @interface.name : ""}: Invalid CRC detected! Calculated 0x#{calculated_crc.to_s(16).upcase} vs found 0x#{crc.to_s(16).upcase}."
    if @bad_strategy == DISCONNECT
      return :DISCONNECT
    end
  end
  if @strip_crc
    new_data = data.dup
    new_data = new_data[0...(@bit_offset / 8)]
    end_range = (@bit_offset + @bit_size) / 8
    new_data << data[end_range..-1] if end_range != 0
    return new_data, extra
  end
  return data, extra
end

#write_data(data, extra = nil) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/openc3/interfaces/protocols/crc_protocol.rb', line 180

def write_data(data, extra = nil)
  unless @write_item_name
    if @bit_size == 64
      crc = @crc.calc(data)
      data << ("\x00" * 8)
      BinaryAccessor.write((crc >> 32), -64, 32, :UINT, data, @endianness, :ERROR)
      BinaryAccessor.write((crc & 0xFFFFFFFF), -32, 32, :UINT, data, @endianness, :ERROR)
    else
      crc = @crc.calc(data)
      data << ("\x00" * (@bit_size / 8))
      BinaryAccessor.write(crc, -@bit_size, @bit_size, :UINT, data, @endianness, :ERROR)
    end
  end
  return data, extra
end

#write_packet(packet) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/openc3/interfaces/protocols/crc_protocol.rb', line 171

def write_packet(packet)
  if @write_item_name
    end_range = packet.get_item(@write_item_name).bit_offset / 8
    crc = @crc.calc(packet.buffer(false)[0...end_range])
    packet.write(@write_item_name, crc)
  end
  packet
end