Module: PWN::SDR::Decoder::LTE

Defined in:
lib/pwn/sdr/decoder/lte.rb

Overview

LTE (E-UTRA) true-air PSS/SSS cell search.

I/Q resampled to 1.92 Msps (128-FFT grid), time-domain PSS correlation against Zadoff-Chu roots 25,29,34 → N_ID_2 ∈ 0,1,2

  • half-frame timing + coarse CFO. SSS m-sequence pair 5 symbols earlier → N_ID_1 ∈ 0..167 → PCI = 3·N_ID_1 + N_ID_2. All FFTs via PWN::FFI::FFTW; falls back to naive DFT for small N.

Defined Under Namespace

Classes: DemodIQ

Constant Summary collapse

FS_BASE =
1_920_000
NFFT =
128
CP_NORM =

samples @ 1.92 Msps for symbols 1..6 (10 for symbol 0)

9
PSS_ROOTS =
{ 0 => 25, 1 => 29, 2 => 34 }.freeze

Class Method Summary collapse

Class Method Details

.authorsObject

Author(s)

0day Inc. support@0dayinc.com



234
235
236
# File 'lib/pwn/sdr/decoder/lte.rb', line 234

public_class_method def self.authors
  "AUTHOR(S):\n  0day Inc. <support@0dayinc.com>\n"
end

.cseq(opts = {}) ⇒ Object

Scrambling sequence c0 (x^5+x^3+1) tied to N_ID_2, ±1.



187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/pwn/sdr/decoder/lte.rb', line 187

public_class_method def self.cseq(opts = {})
  @cseq_base ||= begin
    reg = [0, 0, 0, 0, 1]
    Array.new(31) do
      o = reg[0]
      fb = reg[0] ^ reg[2]
      reg = reg[1..] + [fb]
      1 - (2 * o)
    end
  end
  sh = opts[:nid2].to_i
  Array.new(31) { |n| @cseq_base[(n + sh) % 31] }
end

.decode(opts = {}) ⇒ Object

Supported Method Parameters

PWN::SDR::Decoder::LTE.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq' )



206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/pwn/sdr/decoder/lte.rb', line 206

public_class_method def self.decode(opts = {})
  freq_obj = opts[:freq_obj]
  rate = (opts[:sample_rate] || freq_obj[:iq_rate] || 1_920_000).to_i
  PWN::SDR::Decoder::Base.run_iq(
    freq_obj: freq_obj,
    protocol: 'LTE',
    sample_rate: rate,
    source: opts[:source],
    file: opts[:file],
    demod: DemodIQ.new(rate: rate),
    note: 'OFDMA — I/Q→1.92 Msps→PSS ZC-correlate (FFTW)→N_ID_2+CFO→SSS m-seq→PCI.',
    describe: proc { |_b| { modulation: 'OFDMA', subcarrier_khz: 15 } }
  )
end

.helpObject



238
239
240
241
242
243
244
245
246
247
248
# File 'lib/pwn/sdr/decoder/lte.rb', line 238

public_class_method def self.help
  puts "USAGE (true-air I/Q via PWN::FFI + detector fallback):
    #{self}.decode(
      freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq',
      source:   'optional - :auto|:rtlsdr|:adalm_pluto|:file',
      file:     'optional - .cu8/.cs16 capture (≥1.92 Msps)'
    )

    #{self}.authors
  "
end

.mseq(opts = {}) ⇒ Object

Length-31 m-sequence x^5+x^2+1, cyclic-shifted by shift, as ±1.



172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/pwn/sdr/decoder/lte.rb', line 172

public_class_method def self.mseq(opts = {})
  @mseq_base ||= begin
    reg = [0, 0, 0, 0, 1]
    Array.new(31) do
      o = reg[0]
      fb = reg[0] ^ reg[3]
      reg = reg[1..] + [fb]
      1 - (2 * o)
    end
  end
  sh = opts[:shift].to_i
  Array.new(31) { |n| @mseq_base[(n + sh) % 31] }
end

.parse_line(opts = {}) ⇒ Object



221
222
223
224
225
226
227
228
229
230
# File 'lib/pwn/sdr/decoder/lte.rb', line 221

public_class_method def self.parse_line(opts = {})
  line = opts[:line].to_s
  out  = { protocol: 'LTE' }
  out[:earfcn] = ::Regexp.last_match(1) if line =~ /EARFCN[:= ]+(\d+)/i
  out[:pci]    = ::Regexp.last_match(1) if line =~ /(?:PCI|N_id_cell|Id)[:= ]+(\d{1,3})/i
  out[:prb]    = (::Regexp.last_match(1) || ::Regexp.last_match(2)) if line =~ /(?:PRB[:= ]+(\d+)|(\d+)\s*PRB)/i
  out[:rsrp]   = ::Regexp.last_match(1) if line =~ /(-?\d+(?:\.\d+)?)\s*dBm/
  out[:summary] = "LTE PCI=#{out[:pci]} EARFCN=#{out[:earfcn]} PRB=#{out[:prb]} RSRP=#{out[:rsrp]}dBm"
  out.compact
end

.sss_indices(opts = {}) ⇒ Object

SSS helper: (m0, m1) pair for a given N_ID_1 per TS 36.211 §6.11.2.



161
162
163
164
165
166
167
168
169
# File 'lib/pwn/sdr/decoder/lte.rb', line 161

public_class_method def self.sss_indices(opts = {})
  nid1 = opts[:nid1].to_i
  qp = (nid1 / 30)
  q  = ((nid1 + (qp * (qp + 1) / 2)) / 30)
  mp = nid1 + (q * (q + 1) / 2)
  m0 = mp % 31
  m1 = (m0 + (mp / 31) + 1) % 31
  [m0, m1]
end