Class: PWN::SDR::Decoder::Iridium::RingAlert

Inherits:
Object
  • Object
show all
Defined in:
lib/pwn/sdr/decoder/iridium.rb

Overview

Deliberately scoped to the IRA family, not a generic RAW-bit decoder.

Constant Summary collapse

ACCESS =
'001100000011000011110011'
POLYNOMIAL =
1207
ERROR_SYNDROMES =

Extended BCH(32,21): minimum distance six, bounded radius two. Include the parity bit in the syndrome; never silently ignore it.

begin
  positions = (0...32).to_a
  masks = [0] + positions.map { |bit| 1 << bit } + positions.combination(2).map { |a, b| (1 << a) | (1 << b) }
  masks.to_h do |mask|
    value = mask >> 1
    value ^= POLYNOMIAL << (value.bit_length - POLYNOMIAL.bit_length) while value.bit_length >= POLYNOMIAL.bit_length
    [((value << 1) | (mask.to_s(2).count('1') & 1)), mask]
  end.freeze
end

Instance Method Summary collapse

Instance Method Details

#decode(bits) ⇒ Object

Raises:

  • (ArgumentError)


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
149
150
151
152
153
# File 'lib/pwn/sdr/decoder/iridium.rb', line 113

def decode(bits)
  raise ArgumentError, 'Expected binary IRA bits with downlink access word' unless bits.is_a?(String) && bits.match?(/\A[01]+\z/) && bits.start_with?(ACCESS)
  raise ArgumentError, 'Truncated IRA header' if bits.length < 120

  @corrected = 0
  header = deinterleave(bits[24, 96], 3).map { |word| check_word(word) }.join
  pages = []
  offset = 120
  ended = false
  13.times do
    raise ArgumentError, 'Truncated IRA paging section' if bits.length < offset + 64

    page = deinterleave(bits[offset, 64], 2).map { |word| check_word(word) }.join
    offset += 64
    if page == '1' * 42
      ended = true
      break
    end
    raise ArgumentError, 'Invalid IRA paging reserved bits' unless page[32, 2] == '00' && page[39, 3] == '000'

    pages << { tmsi: page[0, 32].to_i(2), msc_id: page[34, 5].to_i(2) }
    break if pages.length == 12
  end
  raise ArgumentError, 'Missing IRA paging terminator' unless ended || pages.length == 12

  x, y, z = [13, 25, 37].map do |start|
    value = header[start, 12].to_i(2)
    value >= 2048 ? value - 4096 : value
  end
  {
    protocol: 'IRIDIUM', frame_type: 'IRA', decoded: true,
    integrity: 'BCH(31,21)+parity', corrected_bits: @corrected,
    sat: header[0, 7].to_i(2), beam: header[7, 6].to_i(2),
    xyz: [x, y, z], latitude: Math.atan2(z, Math.sqrt((x * x) + (y * y))) * 180 / Math::PI,
    longitude: Math.atan2(y, x) * 180 / Math::PI,
    radius_km: Math.sqrt((x * x) + (y * y) + (z * z)) * 4,
    interval: header[49, 7].to_i(2), broadcast_slot: header[56] == '0' ? 1 : 4,
    eip: header[57].to_i, downlink_subband: header[58, 5].to_i(2), pages: pages,
    summary: "IRIDIUM IRA sat=#{header[0, 7].to_i(2)} beam=#{header[7, 6].to_i(2)}"
  }
end