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 =

Returns Measure execution time to find optimal plan.

Returns:

  • (Integer)

    Measure execution time to find optimal plan

0
FFTW_DESTROY_INPUT =

Returns Allow input array to be destroyed during planning.

Returns:

  • (Integer)

    Allow input array to be destroyed during planning

1
FFTW_UNALIGNED =

Returns Don't assume arrays are aligned in memory.

Returns:

  • (Integer)

    Don't assume arrays are aligned in memory

2
FFTW_CONSERVE_MEMORY =

Returns Minimize memory usage at cost of speed.

Returns:

  • (Integer)

    Minimize memory usage at cost of speed

4
FFTW_EXHAUSTIVE =

Returns Try all possible algorithms (very slow planning).

Returns:

  • (Integer)

    Try all possible algorithms (very slow planning)

8
FFTW_PRESERVE_INPUT =

Returns Preserve input array contents during transform.

Returns:

  • (Integer)

    Preserve input array contents during transform

16
FFTW_PATIENT =

Returns Like MEASURE but try harder (slower planning).

Returns:

  • (Integer)

    Like MEASURE but try harder (slower planning)

32
FFTW_ESTIMATE =

Returns Use quick heuristic to pick plan (fast planning, default).

Returns:

  • (Integer)

    Use quick heuristic to pick plan (fast planning, default)

64
FFTW_WISDOM_ONLY =

Returns Only use wisdom (cached plans), fail if none available.

Returns:

  • (Integer)

    Only use wisdom (cached plans), fail if none available

2_097_152

FFT Direction Constants collapse

FFTW_FORWARD =

Returns Forward FFT direction (time to frequency domain).

Returns:

  • (Integer)

    Forward FFT direction (time to frequency domain)

-1
FFTW_BACKWARD =

Returns Backward/Inverse FFT direction (frequency to time domain).

Returns:

  • (Integer)

    Backward/Inverse FFT direction (frequency to time domain)

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

Returns:

  • (String, nil)

    Error message or nil if loaded successfully



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

Returns:

  • (Boolean)

    true if FFTW3 is loaded and 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

Parameters:

  • spectrum (Array<Complex>)

    Input complex spectrum

Returns:

  • (Array<Complex>)

    IFFT result (time domain samples)

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

Parameters:

  • samples (Array<Complex>)

    Input complex samples

Returns:

  • (Array<Complex>)

    FFT result (complex frequency bins)

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