Class: PWN::SDR::Decoder::Flex::Demod

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

Overview

Streaming FLEX demodulator fed by Base.run_native / Base.run_iq. Per-sample PLL symbol clock, 4-level slicer, full state machine. rubocop:disable Metrics/ClassLength

Instance Method Summary collapse

Constructor Details

#initialize(rate: 48_000) ⇒ Demod

Returns a new instance of Demod.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/pwn/sdr/decoder/flex.rb', line 46

def initialize(rate: 48_000)
  @rate     = rate.to_f
  @zero     = 0.0
  @env      = 0.0
  @env_sum  = 0.0
  @env_n    = 0
  @phase    = 0.0
  @baud     = 1600
  @last     = 0.0
  @locked   = false
  @symcnt   = [0, 0, 0, 0]
  @state    = :sync1
  @syncbuf  = 0
  @polarity = 0
  @mode     = [1600, 2]
  @fiwcnt   = 0
  @fiw      = 0
  @s2cnt    = 0
  @dcnt     = 0
  @cycle    = 0
  @frame    = 0
  @sync_cw  = 0
  reset_phases
end

Instance Method Details

#feed(samples) ⇒ Object Also known as: feed_audio



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
# File 'lib/pwn/sdr/decoder/flex.rb', line 71

def feed(samples, &)
  phase_max = 100.0 * @rate
  samples.each do |x|
    # DC-offset IIR (10 ms) — only track during Sync-1 hunt
    @zero = ((@zero * (@rate * 0.010)) + x) / ((@rate * 0.010) + 1) if @state == :sync1
    s = x - @zero
    phase_rate = phase_max * @baud / @rate
    ppc = 100.0 * @phase / phase_max
    if @locked
      if @state == :sync1
        @env_sum += s.abs
        @env_n  += 1
        @env     = @env_sum / @env_n
      end
    else
      @env = @env_sum = 0.0
      @env_n = 0
      @baud  = 1600
    end
    # 4-level majority vote over mid-80 % of the symbol period
    if ppc > 10 && ppc < 90
      th = @env * SLICE_TH
      if s.positive?
        s > th ? @symcnt[3] += 1 : @symcnt[2] += 1
      else
        s < -th ? @symcnt[0] += 1 : @symcnt[1] += 1
      end
    end
    # Zero-crossing PLL
    if (@last.negative? && s >= 0) || (@last >= 0 && s.negative?)
      perr = ppc < 50 ? @phase : @phase - phase_max
      @phase -= perr * (@locked ? LOCKED_RATE : UNLOCK_RATE)
      @locked = true
    end
    @last   = s
    @phase += phase_rate
    next unless @phase >= phase_max

    @phase -= phase_max
    sym = @symcnt.index(@symcnt.max)
    @symcnt = [0, 0, 0, 0]
    on_symbol(sym, &)
  end
end