Module: PWN::FFI::Liquid
- Extended by:
- Library
- Defined in:
- lib/pwn/ffi/liquid.rb
Overview
Thin liquid-dsp binding.
Provides the high-level DSP building blocks that PWN::SDR::Decoder::*
needs beyond pure-Ruby primitives: FM / frequency demodulation,
multi-stage arbitrary resampling, and Kaiser FIR filters. Opaque
liquid handles are held as FFI::Pointer and always freed in an
ensure — no compile step, no shells. If libliquid is absent
.available? is false and DSP falls back to pure Ruby.
Complex samples use the GCC float _Complex ABI: interleaved float (re, im) per sample, which is how liquid_float_complex is laid out under LIQUID_DEFINE_COMPLEX.
Class Attribute Summary collapse
-
.load_error ⇒ Object
readonly
Returns the value of attribute load_error.
Class Method Summary collapse
-
.authors ⇒ Object
- Author(s)
0day Inc.
-
.available? ⇒ Boolean
- Supported Method Parameters
PWN::FFI::Liquid.available?.
-
.dc_block(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.dc_block( samples: 'required - Array
', m: 'optional - prototype semi-length (default 7 → len 15)', as_db: 'optional - stop-band attenuation dB (default 60.0)' ).
-
.fir_kaiser(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.fir_kaiser( samples: 'required - Array
', length: 'optional - filter length (default 51, odd preferred)', fc: 'optional - normalised cutoff 0..0.5 (default 0.1)', as_db: 'optional - stop-band attenuation dB (default 60.0)', mu: 'optional - fractional sample offset (default 0.0)' ) Returns Array same length as input.
-
.freq_demod(opts = {}) ⇒ Object
- Supported Method Parameters
audio = PWN::FFI::Liquid.freq_demod( iq: 'required - interleaved I/Q Array
(even length)', kf: 'optional - modulation factor (default 0.5)' ) Returns Array of demodulated real samples (length = iq.length/2).
-
.help ⇒ Object
Display Usage for this Module.
-
.resample(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.resample( samples: 'required - Array
', rate: 'required - output/input ratio (>0)', as_db: 'optional - stop-band attenuation dB (default 60.0)' ) Returns Array of resampled samples.
Class Attribute Details
.load_error ⇒ Object (readonly)
Returns the value of attribute load_error.
32 33 34 |
# File 'lib/pwn/ffi/liquid.rb', line 32 def load_error @load_error end |
Class Method Details
.authors ⇒ Object
- Author(s)
0day Inc. support@0dayinc.com
215 216 217 |
# File 'lib/pwn/ffi/liquid.rb', line 215 public_class_method def self. "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n" end |
.available? ⇒ Boolean
- Supported Method Parameters
PWN::FFI::Liquid.available?
73 74 75 76 77 |
# File 'lib/pwn/ffi/liquid.rb', line 73 public_class_method def self.available? !@load_error && respond_to?(:freqdem_create, true) rescue StandardError false end |
.dc_block(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.dc_block(
samples: 'required - Array
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/pwn/ffi/liquid.rb', line 189 public_class_method def self.dc_block(opts = {}) raise 'ERROR: libliquid not available' unless available? samples = opts[:samples] m = (opts[:m] || 7).to_i as_db = (opts[:as_db] || 60.0).to_f return [] if samples.empty? q = firfilt_rrrf_create_dc_blocker(m, as_db) raise 'ERROR: firfilt_rrrf_create_dc_blocker failed' if q.null? begin n = samples.length in_ptr = float_ptr(samples) out_ptr = PubFFI::MemoryPointer.new(:float, n) rc = firfilt_rrrf_execute_block(q, in_ptr, n, out_ptr) raise "ERROR: firfilt_rrrf_execute_block rc=#{rc}" unless rc.zero? out_ptr.read_array_of_float(n) ensure firfilt_rrrf_destroy(q) end end |
.fir_kaiser(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.fir_kaiser(
samples: 'required - Array
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/pwn/ffi/liquid.rb', line 156 public_class_method def self.fir_kaiser(opts = {}) raise 'ERROR: libliquid not available' unless available? samples = opts[:samples] length = (opts[:length] || 51).to_i fc = (opts[:fc] || 0.1).to_f as_db = (opts[:as_db] || 60.0).to_f mu = (opts[:mu] || 0.0).to_f return [] if samples.empty? q = firfilt_rrrf_create_kaiser(length, fc, as_db, mu) raise 'ERROR: firfilt_rrrf_create_kaiser failed' if q.null? begin n = samples.length in_ptr = float_ptr(samples) out_ptr = PubFFI::MemoryPointer.new(:float, n) rc = firfilt_rrrf_execute_block(q, in_ptr, n, out_ptr) raise "ERROR: firfilt_rrrf_execute_block rc=#{rc}" unless rc.zero? out_ptr.read_array_of_float(n) ensure firfilt_rrrf_destroy(q) end end |
.freq_demod(opts = {}) ⇒ Object
- Supported Method Parameters
audio = PWN::FFI::Liquid.freq_demod(
iq: 'required - interleaved I/Q Array
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/pwn/ffi/liquid.rb', line 86 public_class_method def self.freq_demod(opts = {}) raise 'ERROR: libliquid not available' unless available? iq = opts[:iq] kf = (opts[:kf] || 0.5).to_f n = iq.length / 2 return [] if n.zero? fd = freqdem_create(kf) raise 'ERROR: freqdem_create failed' if fd.null? begin in_ptr = float_ptr(iq.first(n * 2)) out_ptr = PubFFI::MemoryPointer.new(:float, n) rc = freqdem_demodulate_block(fd, in_ptr, n, out_ptr) raise "ERROR: freqdem_demodulate_block rc=#{rc}" unless rc.zero? out_ptr.read_array_of_float(n) ensure freqdem_destroy(fd) end end |
.help ⇒ Object
Display Usage for this Module
221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/pwn/ffi/liquid.rb', line 221 public_class_method def self.help puts "USAGE: #{self}.available? # => true/false #{self}.freq_demod(iq:, kf: 0.5) # FM I/Q → audio #{self}.resample(samples:, rate:, as_db: 60.0) # arbitrary rate #{self}.fir_kaiser(samples:, length: 51, fc: 0.1, as_db: 60.0) #{self}.dc_block(samples:, m: 7, as_db: 60.0) #{self}.authors " end |
.resample(opts = {}) ⇒ Object
- Supported Method Parameters
out = PWN::FFI::Liquid.resample(
samples: 'required - Array
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 |
# File 'lib/pwn/ffi/liquid.rb', line 117 public_class_method def self.resample(opts = {}) raise 'ERROR: libliquid not available' unless available? samples = opts[:samples] rate = opts[:rate].to_f as_db = (opts[:as_db] || 60.0).to_f raise 'ERROR: rate must be > 0' unless rate.positive? return samples.dup if samples.empty? q = msresamp_rrrf_create(rate, as_db) raise 'ERROR: msresamp_rrrf_create failed' if q.null? begin nx = samples.length # allocate generously: ceil(1 + 2*r*nx) ny_max = [1, (1 + (2 * rate * nx)).ceil].max in_ptr = float_ptr(samples) out_ptr = PubFFI::MemoryPointer.new(:float, ny_max) ny_ptr = PubFFI::MemoryPointer.new(:uint, 1) rc = msresamp_rrrf_execute(q, in_ptr, nx, out_ptr, ny_ptr) raise "ERROR: msresamp_rrrf_execute rc=#{rc}" unless rc.zero? ny = ny_ptr.read_uint out_ptr.read_array_of_float(ny) ensure msresamp_rrrf_destroy(q) end end |