Module: RTLSDR::FFTW

Extended by:
FFI::Library
Defined in:
lib/rtlsdr/fftw.rb

Overview

Note:

This is an internal module. Use RTLSDR::DSP.fft and related methods instead.

FFI bindings for FFTW3 (Fastest Fourier Transform in the West)

This module provides low-level FFI bindings to the FFTW3 library for performing fast Fourier transforms. FFTW3 must be installed on the system.

Examples:

System installation

# macOS: brew install fftw
# Ubuntu/Debian: apt-get install libfftw3-dev
# Fedora: dnf install fftw-devel

FFTW Planning Flags collapse

FFTW_MEASURE =
0
FFTW_DESTROY_INPUT =
1
FFTW_UNALIGNED =
2
FFTW_CONSERVE_MEMORY =
4
FFTW_EXHAUSTIVE =
8
FFTW_PRESERVE_INPUT =
16
FFTW_PATIENT =
32
FFTW_ESTIMATE =
64
FFTW_WISDOM_ONLY =
2_097_152

FFT Direction Constants collapse

FFTW_FORWARD =
-1
FFTW_BACKWARD =
1
COMPLEX_SIZE =

Size of a complex number in FFTW (two doubles)

16

Constant Summary collapse

LIBRARY_NAMES =

Try to load FFTW3 library with common paths

%w[
  fftw3
  libfftw3.so.3
  libfftw3.dylib
  libfftw3-3.dll
].freeze

Class Attribute Summary collapse

FFT Direction Constants collapse

Class Method Summary collapse

Class Attribute Details

.load_errorString? (readonly)

Get the error message if FFTW3 failed to load



45
46
47
# File 'lib/rtlsdr/fftw.rb', line 45

def load_error
  @load_error
end

Class Method Details

.available?Boolean

Check if FFTW3 library is available



38
39
40
# File 'lib/rtlsdr/fftw.rb', line 38

def available?
  @available
end

.backward(spectrum) ⇒ Array<Complex>

Perform inverse FFT on complex spectrum

Raises:

  • (RuntimeError)

    if FFTW3 is not available



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rtlsdr/fftw.rb', line 161

def self.backward(spectrum)
  raise "FFTW3 library not available: #{load_error}" unless available?

  n = spectrum.length
  return [] if n.zero?

  # Allocate input and output arrays
  input = fftw_malloc(n * COMPLEX_SIZE)
  output = fftw_malloc(n * COMPLEX_SIZE)

  begin
    # Copy spectrum to input array
    spectrum.each_with_index do |sample, i|
      input.put_float64(i * COMPLEX_SIZE, sample.real)
      input.put_float64((i * COMPLEX_SIZE) + 8, sample.imag)
    end

    # Create and execute plan
    plan = fftw_plan_dft_1d(n, input, output, FFTW_BACKWARD, FFTW_ESTIMATE)
    raise "Failed to create FFTW plan" if plan.null?

    begin
      fftw_execute(plan)

      # Read output and normalize (FFTW doesn't normalize IFFT)
      result = Array.new(n) do |i|
        real = output.get_float64(i * COMPLEX_SIZE) / n
        imag = output.get_float64((i * COMPLEX_SIZE) + 8) / n
        Complex(real, imag)
      end

      result
    ensure
      fftw_destroy_plan(plan)
    end
  ensure
    fftw_free(input)
    fftw_free(output)
  end
end

.forward(samples) ⇒ Array<Complex>

Perform forward FFT on complex samples

Raises:

  • (RuntimeError)

    if FFTW3 is not available



115
116
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
145
146
147
148
149
150
151
152
153
154
# File 'lib/rtlsdr/fftw.rb', line 115

def self.forward(samples)
  raise "FFTW3 library not available: #{load_error}" unless available?

  n = samples.length
  return [] if n.zero?

  # Allocate input and output arrays
  input = fftw_malloc(n * COMPLEX_SIZE)
  output = fftw_malloc(n * COMPLEX_SIZE)

  begin
    # Copy samples to input array (interleaved real/imag doubles)
    samples.each_with_index do |sample, i|
      input.put_float64(i * COMPLEX_SIZE, sample.real)
      input.put_float64((i * COMPLEX_SIZE) + 8, sample.imag)
    end

    # Create and execute plan
    plan = fftw_plan_dft_1d(n, input, output, FFTW_FORWARD, FFTW_ESTIMATE)
    raise "Failed to create FFTW plan" if plan.null?

    begin
      fftw_execute(plan)

      # Read output into Ruby Complex array
      result = Array.new(n) do |i|
        real = output.get_float64(i * COMPLEX_SIZE)
        imag = output.get_float64((i * COMPLEX_SIZE) + 8)
        Complex(real, imag)
      end

      result
    ensure
      fftw_destroy_plan(plan)
    end
  ensure
    fftw_free(input)
    fftw_free(output)
  end
end