Class: PWN::SDR::Decoder::ADSB::DemodIQ

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

Overview

Streaming I/Q demod for Base.run_iq.

Instance Method Summary collapse

Constructor Details

#initialize(rate: 2_000_000, reference: nil) ⇒ DemodIQ

Returns a new instance of DemodIQ.

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
# File 'lib/pwn/sdr/decoder/adsb.rb', line 29

def initialize(rate: 2_000_000, reference: nil)
  raise ArgumentError, 'Mode S requires sample rate 2_000_000' unless (rate.to_f - 2_000_000).zero?

  @reference = reference
  @rate = rate.to_f
  @spb  = @rate / 1_000_000.0 # samples per µs (expect ~2)
  @mag  = []
  @seen = {}
  @cpr = {}
  @sample_offset = 0
end

Instance Method Details

#feed_iq(samples, rate: nil, &emit) ⇒ Object

Raises:

  • (ArgumentError)


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/pwn/sdr/decoder/adsb.rb', line 41

def feed_iq(samples, rate: nil, &emit)
  @rate = rate.to_f if rate
  @spb  = @rate / 1_000_000.0
  raise ArgumentError, 'Mode S requires sample rate 2_000_000' unless @rate == 2_000_000

  samples = [@pending_i] + samples if @pending_i
  @pending_i = samples.length.odd? ? samples.last : nil
  samples = samples[0...-1] if @pending_i
  m2 = PWN::SDR::Decoder::DSP.mag_sq(iq: samples)
  @mag.concat(m2)
  # Scan the entire buffer FIRST so frames that land earlier in a
  # multi-ms chunk are not discarded by the ring-buffer clamp below.
  # Only AFTER scan() has consumed what it can do we cap residual
  # history (frame + preamble headroom ≈ 0.5 ms, keep 50 ms).
  scan(&emit)
  max = (@rate * 0.05).to_i
  return unless @mag.length > max

  @sample_offset += @mag.length - max
  @mag.shift(@mag.length - max)
end