Class: PWN::SDR::Decoder::Bluetooth::SymbolStream

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

Overview

Continuous Ruby FM/NRZ fallback. Unlike a one-shot liquid gmskdem call, filter history and symbol phase survive transport chunks. Also used for DECT and ZigBee's existing FM/NRZ recovery path.

Instance Method Summary collapse

Constructor Details

#initialize(rate:, baud:) ⇒ SymbolStream

Returns a new instance of SymbolStream.

Raises:

  • (ArgumentError)


33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 33

def initialize(rate:, baud:)
  @spb = rate.to_f / baud
  raise ArgumentError, 'sample rate must be at least twice the baud' if @spb < 2

  @phase = @spb / 2
  @previous_iq = nil
  @previous_input = 0.0
  @dc = 0.0
  @previous = 0.0
  @window = Array.new([(@spb / 4).round, 1].max, 0.0)
  @slot = 0
  @sum = 0.0
end

Instance Method Details

#feed(samples) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 61

def feed(samples)
  bits = []
  samples.each do |x|
    @dc = x - @previous_input + (0.995 * @dc)
    @previous_input = x
    @sum += @dc - @window[@slot]
    @window[@slot] = @dc
    @slot = (@slot + 1) % @window.length
    v = @sum / @window.length
    @phase = @spb / 2 if (@previous.negative? && v >= 0) || (@previous.positive? && v.negative?)
    @phase -= 1
    if @phase <= 0
      bits << (v.negative? ? 0 : 1)
      @phase += @spb
    end
    @previous = v
  end
  bits
end

#feed_iq(samples) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 47

def feed_iq(samples)
  audio = []
  samples.each_slice(2) do |re, im|
    next unless im

    if @previous_iq
      pr, pi = @previous_iq
      audio << Math.atan2((im * pr) - (re * pi), (re * pr) + (im * pi))
    end
    @previous_iq = [re, im]
  end
  feed(audio)
end