rtlsdr 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RTLSDR
4
+ # FFI bindings for FFTW3 (Fastest Fourier Transform in the West)
5
+ #
6
+ # This module provides low-level FFI bindings to the FFTW3 library for
7
+ # performing fast Fourier transforms. FFTW3 must be installed on the system.
8
+ #
9
+ # @note This is an internal module. Use RTLSDR::DSP.fft and related methods instead.
10
+ #
11
+ # @example System installation
12
+ # # macOS: brew install fftw
13
+ # # Ubuntu/Debian: apt-get install libfftw3-dev
14
+ # # Fedora: dnf install fftw-devel
15
+ module FFTW
16
+ extend ::FFI::Library
17
+
18
+ # Try to load FFTW3 library with common paths
19
+ LIBRARY_NAMES = %w[
20
+ fftw3
21
+ libfftw3.so.3
22
+ libfftw3.dylib
23
+ libfftw3-3.dll
24
+ ].freeze
25
+
26
+ begin
27
+ ffi_lib LIBRARY_NAMES
28
+ @available = true
29
+ rescue LoadError => e
30
+ @available = false
31
+ @load_error = e.message
32
+ end
33
+
34
+ class << self
35
+ # Check if FFTW3 library is available
36
+ #
37
+ # @return [Boolean] true if FFTW3 is loaded and available
38
+ def available?
39
+ @available
40
+ end
41
+
42
+ # Get the error message if FFTW3 failed to load
43
+ #
44
+ # @return [String, nil] Error message or nil if loaded successfully
45
+ attr_reader :load_error
46
+ end
47
+
48
+ # FFTW planning flags
49
+ FFTW_MEASURE = 0
50
+ FFTW_DESTROY_INPUT = 1
51
+ FFTW_UNALIGNED = 2
52
+ FFTW_CONSERVE_MEMORY = 4
53
+ FFTW_EXHAUSTIVE = 8
54
+ FFTW_PRESERVE_INPUT = 16
55
+ FFTW_PATIENT = 32
56
+ FFTW_ESTIMATE = 64
57
+ FFTW_WISDOM_ONLY = 2_097_152
58
+
59
+ # FFT direction
60
+ FFTW_FORWARD = -1
61
+ FFTW_BACKWARD = 1
62
+
63
+ if available?
64
+ # Memory allocation
65
+ attach_function :fftw_malloc, [:size_t], :pointer
66
+ attach_function :fftw_free, [:pointer], :void
67
+
68
+ # Plan creation and execution
69
+ attach_function :fftw_plan_dft_1d, %i[int pointer pointer int uint], :pointer
70
+ attach_function :fftw_execute, [:pointer], :void
71
+ attach_function :fftw_destroy_plan, [:pointer], :void
72
+
73
+ # Wisdom (plan caching)
74
+ attach_function :fftw_export_wisdom_to_string, [], :pointer
75
+ attach_function :fftw_import_wisdom_from_string, [:string], :int
76
+ attach_function :fftw_forget_wisdom, [], :void
77
+ end
78
+
79
+ # Size of a complex number in FFTW (two doubles)
80
+ COMPLEX_SIZE = 16
81
+
82
+ # Perform forward FFT on complex samples
83
+ #
84
+ # @param [Array<Complex>] samples Input complex samples
85
+ # @return [Array<Complex>] FFT result (complex frequency bins)
86
+ # @raise [RuntimeError] if FFTW3 is not available
87
+ def self.forward(samples)
88
+ raise "FFTW3 library not available: #{load_error}" unless available?
89
+
90
+ n = samples.length
91
+ return [] if n.zero?
92
+
93
+ # Allocate input and output arrays
94
+ input = fftw_malloc(n * COMPLEX_SIZE)
95
+ output = fftw_malloc(n * COMPLEX_SIZE)
96
+
97
+ begin
98
+ # Copy samples to input array (interleaved real/imag doubles)
99
+ samples.each_with_index do |sample, i|
100
+ input.put_float64(i * COMPLEX_SIZE, sample.real)
101
+ input.put_float64((i * COMPLEX_SIZE) + 8, sample.imag)
102
+ end
103
+
104
+ # Create and execute plan
105
+ plan = fftw_plan_dft_1d(n, input, output, FFTW_FORWARD, FFTW_ESTIMATE)
106
+ raise "Failed to create FFTW plan" if plan.null?
107
+
108
+ begin
109
+ fftw_execute(plan)
110
+
111
+ # Read output into Ruby Complex array
112
+ result = Array.new(n) do |i|
113
+ real = output.get_float64(i * COMPLEX_SIZE)
114
+ imag = output.get_float64((i * COMPLEX_SIZE) + 8)
115
+ Complex(real, imag)
116
+ end
117
+
118
+ result
119
+ ensure
120
+ fftw_destroy_plan(plan)
121
+ end
122
+ ensure
123
+ fftw_free(input)
124
+ fftw_free(output)
125
+ end
126
+ end
127
+
128
+ # Perform inverse FFT on complex spectrum
129
+ #
130
+ # @param [Array<Complex>] spectrum Input complex spectrum
131
+ # @return [Array<Complex>] IFFT result (time domain samples)
132
+ # @raise [RuntimeError] if FFTW3 is not available
133
+ def self.backward(spectrum)
134
+ raise "FFTW3 library not available: #{load_error}" unless available?
135
+
136
+ n = spectrum.length
137
+ return [] if n.zero?
138
+
139
+ # Allocate input and output arrays
140
+ input = fftw_malloc(n * COMPLEX_SIZE)
141
+ output = fftw_malloc(n * COMPLEX_SIZE)
142
+
143
+ begin
144
+ # Copy spectrum to input array
145
+ spectrum.each_with_index do |sample, i|
146
+ input.put_float64(i * COMPLEX_SIZE, sample.real)
147
+ input.put_float64((i * COMPLEX_SIZE) + 8, sample.imag)
148
+ end
149
+
150
+ # Create and execute plan
151
+ plan = fftw_plan_dft_1d(n, input, output, FFTW_BACKWARD, FFTW_ESTIMATE)
152
+ raise "Failed to create FFTW plan" if plan.null?
153
+
154
+ begin
155
+ fftw_execute(plan)
156
+
157
+ # Read output and normalize (FFTW doesn't normalize IFFT)
158
+ result = Array.new(n) do |i|
159
+ real = output.get_float64(i * COMPLEX_SIZE) / n
160
+ imag = output.get_float64((i * COMPLEX_SIZE) + 8) / n
161
+ Complex(real, imag)
162
+ end
163
+
164
+ result
165
+ ensure
166
+ fftw_destroy_plan(plan)
167
+ end
168
+ ensure
169
+ fftw_free(input)
170
+ fftw_free(output)
171
+ end
172
+ end
173
+ end
174
+ end
@@ -12,5 +12,5 @@ module RTLSDR
12
12
  #
13
13
  # @return [String] Current gem version
14
14
  # @since 0.1.0
15
- VERSION = "0.1.12"
15
+ VERSION = "0.2.0"
16
16
  end
data/lib/rtlsdr.rb CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  require_relative "rtlsdr/version"
4
4
  require_relative "rtlsdr/ffi"
5
+ require_relative "rtlsdr/fftw"
5
6
  require_relative "rtlsdr/device"
6
7
  require_relative "rtlsdr/errors"
7
8
  require_relative "rtlsdr/dsp"
9
+ require_relative "rtlsdr/dsp/filter"
10
+ require_relative "rtlsdr/demod"
8
11
  require_relative "rtlsdr/scanner"
9
12
 
10
13
  # Ruby bindings for RTL-SDR (Software Defined Radio) devices
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtlsdr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - joshfng
@@ -49,10 +49,13 @@ files:
49
49
  - examples/spectrum_analyzer.rb
50
50
  - exe/rtlsdr
51
51
  - lib/rtlsdr.rb
52
+ - lib/rtlsdr/demod.rb
52
53
  - lib/rtlsdr/device.rb
53
54
  - lib/rtlsdr/dsp.rb
55
+ - lib/rtlsdr/dsp/filter.rb
54
56
  - lib/rtlsdr/errors.rb
55
57
  - lib/rtlsdr/ffi.rb
58
+ - lib/rtlsdr/fftw.rb
56
59
  - lib/rtlsdr/scanner.rb
57
60
  - lib/rtlsdr/version.rb
58
61
  homepage: https://github.com/joshfng/rtlsdr
@@ -84,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
87
  - !ruby/object:Gem::Version
85
88
  version: '0'
86
89
  requirements: []
87
- rubygems_version: 3.7.1
90
+ rubygems_version: 3.6.9
88
91
  specification_version: 4
89
92
  summary: Ruby bindings for librtlsdr
90
93
  test_files: []