digiproc 0.1.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.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +48 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +78 -0
  10. data/Rakefile +37 -0
  11. data/TODO.md +50 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/config/environment.rb +118 -0
  15. data/console_tests.rb +44 -0
  16. data/digiproc.gemspec +49 -0
  17. data/examples/analog_signals/analog_to_digital.rb +31 -0
  18. data/examples/analog_signals/companded-signals.png +0 -0
  19. data/examples/analog_signals/companding.rb +68 -0
  20. data/examples/analog_signals/fft-plot.png +0 -0
  21. data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
  22. data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
  23. data/examples/analog_signals/quantization-outputs.png +0 -0
  24. data/examples/analog_signals/quantize_compand.rb +69 -0
  25. data/examples/binomial_distribution/bit_error.rb +14 -0
  26. data/examples/binomial_distribution/dice.rb +35 -0
  27. data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  28. data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  29. data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  30. data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  31. data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
  32. data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
  33. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  34. data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  35. data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
  36. data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
  37. data/examples/digital_signals/modulate_square_pulses.rb +9 -0
  38. data/examples/digital_signals/modulated_sq._pulses.png +0 -0
  39. data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
  40. data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
  41. data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
  42. data/examples/digital_signals/square_signals.rb +90 -0
  43. data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
  44. data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
  45. data/examples/encoding/gray_code.rb +22 -0
  46. data/examples/encoding/psk.rb +91 -0
  47. data/examples/encoding/system_2_phase.png +0 -0
  48. data/examples/encoding/system_2_xmit_signal.png +0 -0
  49. data/examples/encoding/system_3_phase.png +0 -0
  50. data/examples/encoding/system_3_xmit_signal.png +0 -0
  51. data/examples/encoding/system_4_xmit_signal.png +0 -0
  52. data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
  53. data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
  54. data/examples/factories/Quickplot Graph.png +0 -0
  55. data/examples/factories/bandpass.rb +6 -0
  56. data/examples/fft/plot_Dsp::FFT.png +0 -0
  57. data/examples/fft/recieved_data_(time_domain).png +0 -0
  58. data/examples/fft/simple_fft_example.rb +47 -0
  59. data/examples/fft/unprocessed_fft.png +0 -0
  60. data/examples/filters/bandpass_filter.png +0 -0
  61. data/examples/filters/filter_a_signal.rb +38 -0
  62. data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
  63. data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
  64. data/examples/filters/white_noise_spectra.png +0 -0
  65. data/examples/functions/compute_probability.rb +29 -0
  66. data/examples/functions/gram_schmidt.rb +10 -0
  67. data/examples/functions/minimize_energy.rb +29 -0
  68. data/examples/functions/orthoganalize.rb +18 -0
  69. data/examples/functions/simple_functions.rb +81 -0
  70. data/examples/linear_algebra/diverging_sys.rb +13 -0
  71. data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
  72. data/examples/modulation_schemes/dpsk_2.png +0 -0
  73. data/examples/modulation_schemes/dpsk_256.png +0 -0
  74. data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
  75. data/examples/modulation_schemes/psk.rb +36 -0
  76. data/examples/modulation_schemes/psk_2.png +0 -0
  77. data/examples/modulation_schemes/psk_256.png +0 -0
  78. data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
  79. data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
  80. data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
  81. data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
  82. data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
  83. data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
  84. data/examples/quickplot/PlottableClass_plot.png +0 -0
  85. data/examples/quickplot/decorators.rb +13 -0
  86. data/examples/quickplot/direct_gruff.png +0 -0
  87. data/examples/quickplot/plot_PlottableClass.png +0 -0
  88. data/examples/quickplot/quickplot_vs_others.rb +85 -0
  89. data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
  90. data/examples/quickplot/random_data_quickplot.png +0 -0
  91. data/examples/realized_gaussian/norm_dist_plot.png +0 -0
  92. data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
  93. data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
  94. data/lib/concerns/convolvable.rb +144 -0
  95. data/lib/concerns/data_properties.rb +223 -0
  96. data/lib/concerns/fourier_transformable.rb +178 -0
  97. data/lib/concerns/initializable.rb +43 -0
  98. data/lib/concerns/multipliable.rb +22 -0
  99. data/lib/concerns/os.rb +36 -0
  100. data/lib/concerns/plottable.rb +248 -0
  101. data/lib/concerns/requires_data.rb +8 -0
  102. data/lib/digiproc/version.rb +8 -0
  103. data/lib/digiproc.rb +2 -0
  104. data/lib/extensions/array_extension.rb +23 -0
  105. data/lib/extensions/core_extensions.rb +117 -0
  106. data/lib/factories/factories.rb +3 -0
  107. data/lib/factories/filter_factory.rb +83 -0
  108. data/lib/factories/window_factory.rb +22 -0
  109. data/lib/fft.rb +255 -0
  110. data/lib/filters/bandpass_filter.rb +43 -0
  111. data/lib/filters/bandstop_filter.rb +44 -0
  112. data/lib/filters/digital_filter.rb +59 -0
  113. data/lib/filters/highpass_filter.rb +27 -0
  114. data/lib/filters/lowpass_filter.rb +27 -0
  115. data/lib/functions.rb +221 -0
  116. data/lib/probability/binomial_distribution.rb +84 -0
  117. data/lib/probability/bit_generator.rb +94 -0
  118. data/lib/probability/gaussian_distribution.rb +29 -0
  119. data/lib/probability/probability.rb +234 -0
  120. data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
  121. data/lib/quick_plot.rb +96 -0
  122. data/lib/rbplot.rb +219 -0
  123. data/lib/signals/analog_signal.rb +143 -0
  124. data/lib/signals/digital_signal.rb +181 -0
  125. data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
  126. data/lib/strategies/code/gray_code.rb +75 -0
  127. data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
  128. data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
  129. data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
  130. data/lib/strategies/convolution/bf_conv.rb +57 -0
  131. data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
  132. data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
  133. data/lib/strategies/fft/radix2_strategy.rb +84 -0
  134. data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
  135. data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
  136. data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
  137. data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
  138. data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
  139. data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
  140. data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
  141. data/lib/strategies/strategies.rb +3 -0
  142. data/lib/strategies/window/blackman_window.rb +32 -0
  143. data/lib/strategies/window/hamming_window.rb +31 -0
  144. data/lib/strategies/window/hanning_window.rb +31 -0
  145. data/lib/strategies/window/kaiser_window.rb +27 -0
  146. data/lib/strategies/window/rectangular_window.rb +22 -0
  147. data/lib/strategies/window/window.rb +42 -0
  148. data/lib/systems/custom_system.rb +13 -0
  149. data/lib/systems/hilbert_transform.rb +6 -0
  150. data/lib/systems/matched_filter.rb +21 -0
  151. data/lib/systems/raised_cosine_filter.rb +11 -0
  152. data/lib/systems/system.rb +19 -0
  153. data/lib/systems/systems.rb +3 -0
  154. data/playground.rb +323 -0
  155. data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
  156. data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
  157. data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  158. data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  159. data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
  160. data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
  161. data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
  162. data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
  163. data/plots/dpsk_2.png +0 -0
  164. data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
  165. data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
  166. data/plots/frequency_signal,_ts_=_1_s.png +0 -0
  167. data/plots/frequency_signal,_ts_=_2_s.png +0 -0
  168. data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
  169. data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
  170. data/plots/psk_2.png +0 -0
  171. data/plots/time_signal,_ts_=_1_s.png +0 -0
  172. data/plots/time_signal,_ts_=_2_s.png +0 -0
  173. data/test-title-dark.png +0 -0
  174. data/test-title.png +0 -0
  175. metadata +322 -0
@@ -0,0 +1,223 @@
1
+ ##
2
+ # Module which can perform basic operations on 1D data arrays
3
+ # This module extends itself so all methods can be called via:
4
+ ## Digiproc::DataProperties.method_i_want_to_call
5
+
6
+ module Digiproc::DataProperties
7
+
8
+ extend self
9
+ ##
10
+ ## all_maxima(data [Array]) => returns Array(OpenStruct<#index, #value>)
11
+ # returns all maximum in an array of `OpenStruct`s with #index and #value, ie:
12
+ ## all_maxima([1,2,3,4,5,4,3,2,6,7,8,7,6,5]) # [#<OpenStruct index=4, value=5>, #<OpenStruct index=10, value=8>]
13
+ def all_maxima(data)
14
+ raise ArgumentError.new("Data must be an array") if not data.is_a? Array
15
+ slope = Slope::Positive
16
+ max = []
17
+ data.each_with_index do |n, i|
18
+ old_slope = slope
19
+ if i <= data.length - 2
20
+ new_slope = find_slope(data[i], data[i+1])
21
+ slope = new_slope.is?(:zero) ? old_slope : new_slope
22
+ max << OpenStruct.new(index: i, value: n) if old_slope.is? :positive and slope.is? :negative
23
+ else
24
+ max << OpenStruct.new(index: i, value: n) if slope.is? :positive
25
+ end
26
+ end
27
+ max
28
+ end
29
+
30
+ ##
31
+ # maxima(data [Array], num = 1 [Integer]) returns `num` number of largest maxima from the data array returned in #all_maxima
32
+ ## arr = [1,2,3,4,5,4,3,2,6,7,8,7,6,5]
33
+ ## Digiproc::DataProperties.maxima(arr, 1) # => [#<OpenStruct index=10, value=8>]
34
+ def maxima(data, num = 1)
35
+ all_maxima(data).sort{ |a, b| b.value <=> a.value }.take num
36
+ end
37
+
38
+ ##
39
+ # local_maxima(data [Array], num=1 [Integer]) => returns Array(OpenStruct(#index, #value))
40
+ # calculates all maxima and orders them by how much proportionally they
41
+ # are to any directly adjacent maxima. It then takes `num` answer and returns an array of `OpenStruct`s with #index and #value
42
+ # This is particularly useful to use when looking for local maxima in a FFT dB or magnitude plot.
43
+ ## arr = [50,45,40,30,35,30,29,28,29,20,15,10,19,9,8,7,9,8,7,6,5,9,6,5,4,9,6,3,2,7,5,4,3,2,1,9,1,2,3,4]
44
+ ## Digiproc::DataProperties.local_maxima(arr, 3) #=> [#<OpenStruct index=35, value=9>, #<OpenStruct index=0, value=50>, #<OpenStruct index=12, value=19>]
45
+ def local_maxima(data, num=1)
46
+ all_maxima = all_maxima(data)
47
+ all_maxima.sort do |a, b|
48
+ a_upper = all_maxima.find{ |maxima| maxima.index > a.index }
49
+ a_lower = all_maxima.reverse.find{ |maxima| maxima.index < a.index }
50
+ a_adjacent = 0
51
+ if a_upper && a_lower
52
+ a_adjacent = ((a.value.to_f / a_upper.value) + (a.value.to_f / a_lower.value)) / 2.0
53
+ else
54
+ a_adjacent = !!a_upper ? (a.value.to_f / a_upper.value) : (a.value.to_f / a_lower.value)
55
+ end
56
+ b_upper = all_maxima.find{ |maxima| maxima.index > b.index }
57
+ b_lower = all_maxima.reverse.find{ |maxima| maxima.index < b.index }
58
+ b_adjacent = 0
59
+ if b_upper && b_lower
60
+ b_adjacent = ((b.value.to_f / b_upper.value) + (b.value.to_f / b_lower.value)) / 2.0
61
+ else
62
+ b_adjacent = !!b_upper ? (b.value.to_f / b_upper.value) : (b.value.to_f / b_lower.value)
63
+ end
64
+ b_adjacent <=> a_adjacent
65
+ end
66
+ .take(num)
67
+ end
68
+
69
+ ##
70
+ # slope(a: Numeric, b: Numeric, range=1: Numeric ) => float
71
+ # returns the slope of these two y values, given a change in x values by `range` which defualts to 1. Returns a float
72
+ ## Digiproc::DataProperties.slope(0.5, 1.2, 0.5) #=> 1.4
73
+ def slope(a,b, range=1)
74
+ return (b - a) / range.to_f
75
+ end
76
+
77
+ ##
78
+ # find_slope(a: Int or Double, b: Int or Double) returns Slope::Positive, Slope::Negative, or Slope::Zero
79
+ # Note: Slope::Positive.is? :positive returns true, Slope::Negative.is? :negative returns true, etc.
80
+ def find_slope(a,b)
81
+ slope = nil
82
+ if b - a > 0
83
+ slope = Slope::Positive
84
+ elsif b - a < 0
85
+ slope = Slope::Negative
86
+ else
87
+ slope = Slope::Zero
88
+ end
89
+ slope
90
+ end
91
+
92
+ ##
93
+ ## Inner slope class made for pleasant syntax in the maxima methods
94
+ class Slope
95
+
96
+ ##
97
+ # returns inner class Negative
98
+ def self.Negative
99
+ Negative
100
+ end
101
+
102
+ ##
103
+ # returns inner class Positive
104
+ def self.Positive
105
+ Positive
106
+ end
107
+
108
+ ##
109
+ # returns inner class Zero
110
+ def self.Zero
111
+ Zero
112
+ end
113
+
114
+ ##
115
+ ## Used in the Slope class to indicate a negative slope.
116
+ # Made for easy syntax purposes
117
+ class Negative
118
+
119
+ ##
120
+ # Used for comparison in ==
121
+ # returns :negative
122
+ def self.type
123
+ :negative
124
+ end
125
+
126
+ ##
127
+ # Can test equality in multiple ways:
128
+ ## Digiproc::DataProperties::Slope::Negative == OpenStruct.new(type: :negative) # true
129
+ ## Digiproc::DataProperties::Slope::Negative == :negative # true
130
+ ## Digiproc::DataProperties::Slope::Negative == "negative" # true
131
+ def self.==(val)
132
+ if val.respond_to? :type
133
+ return true if val.type == :negative
134
+ end
135
+ return true if val == :negative
136
+ if val.respond_to? :downcase
137
+ return true if val.downcase == "negative"
138
+ end
139
+ false
140
+ end
141
+
142
+ ##
143
+ # Alias to ==
144
+ # Can test equality in multiple ways:
145
+ ## Digiproc::DataProperties::Slope::Negative.is? OpenStruct.new(type: :negative) # true
146
+ ## Digiproc::DataProperties::Slope::Negative.is? :negative # true
147
+ ## Digiproc::DataProperties::Slope::Negative.is? "negative" # true, not case sensitive
148
+ def self.is?(val)
149
+ self.==(val)
150
+ end
151
+ end
152
+
153
+ ##
154
+ ## Used in the Slope class to indicate a positive slope.
155
+ # Made for easy syntax purposes
156
+ class Positive
157
+ def self.type
158
+ :positive
159
+ end
160
+
161
+ ##
162
+ # Can test equality in multiple ways:
163
+ ## Digiproc::DataProperties::Slope::Positive == OpenStruct.new(type: :positive) # true
164
+ ## Digiproc::DataProperties::Slope::Positive == :positive # true
165
+ ## Digiproc::DataProperties::Slope::Negative == "positive" # true, not case-sensitive
166
+ def self.==(val)
167
+ if val.respond_to? :type
168
+ return true if val.type == :positive
169
+ end
170
+ return true if val == :positive
171
+ if val.respond_to? :downcase
172
+ return true if val.downcase == "positive"
173
+ end
174
+ false
175
+ end
176
+
177
+ ##
178
+ # Alias to ==
179
+ # Can test equality in multiple ways:
180
+ ## Digiproc::DataProperties::Slope::Piositive.is? OpenStruct.new(type: :negative) # true
181
+ ## Digiproc::DataProperties::Slope::Positive.is? :positive # true
182
+ ## Digiproc::DataProperties::Slope::Positive.is? "positive" # true, not case-sensitive
183
+ def self.is?(val)
184
+ self.==(val)
185
+ end
186
+ end
187
+
188
+ ##
189
+ ## Used in the Slope class to indicate a zero slope.
190
+ # Made for easy syntax purposes
191
+ class Zero
192
+ def self.type
193
+ :zero
194
+ end
195
+
196
+ ##
197
+ # Can test equality in multiple ways:
198
+ ## Digiproc::DataProperties::Slope::Zero == OpenStruct.new(type: :zero) # true
199
+ ## Digiproc::DataProperties::Slope::Zero == :zero # true
200
+ ## Digiproc::DataProperties::Slope::Zero == "zero" # true, not case sensitive
201
+ def self.==(val)
202
+ if val.respond_to? :type
203
+ return true if val.type == :zero
204
+ end
205
+ return true if val == :zero
206
+ if val.respond_to? :downcase
207
+ return true if val.downcase == "zero"
208
+ end
209
+ false
210
+ end
211
+
212
+ ##
213
+ # Alias to ==
214
+ # Can test equality in multiple ways:
215
+ ## Digiproc::DataProperties::Slope::Zero.is? OpenStruct.new(type: :zero) # true
216
+ ## Digiproc::DataProperties::Slope::Zero.is? :zero # true
217
+ ## Digiproc::DataProperties::Slope::Zero.is? "zero" # true, not case sensitive
218
+ def self.is?(val)
219
+ self.==(val)
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,178 @@
1
+ ##
2
+ # Module for Classes which have a property `data` which we can take the Discrete Fourier Transform of.
3
+ # A class which wants to use methods outside of GenricMethods will also include Digiproc::Initializable, and you have to use the appropriate methods in your class constructor if you want the Digiproc::FFT class to be set up automatically
4
+ # You could manually set it up if your class sets up its own @fft property which is an instance of Digiproc::FFT with the class' `data` passed in.
5
+ # See an example of this in action in Digiproc::DigitalSignal initializer method.
6
+ module Digiproc::FourierTransformable
7
+
8
+ ##
9
+ # Inner module for places where standalone functions are needed, not associated with a class which contains `data`. See Digiproc::Functions for use
10
+ module GenericMethods
11
+
12
+ ##
13
+ # Return a Fast Fourier Transform (FFT) strategy to be used for GenericMethods. Set to Digiproc::Strategies::Radix2Strategy
14
+ # It is important to note that the Radix2Strategy increases the size of the FFT return to the closest power of 2.
15
+ def fft_strategy
16
+ Digiproc::Strategies::Radix2Strategy
17
+ end
18
+
19
+ # Return an Inverse Fast Fourier Transform (IFFT) strategy to be used for GenericMethods. Set to Digiproc::Strategies::IFFTConjugateStrategy
20
+ def ifft_strategy
21
+ Digiproc::Strategies::IFFTConjugateStrategy
22
+ end
23
+
24
+ ##
25
+ ## fft(data [Array of complex Numerics]) => returns Array of data corresponding to the FFT
26
+ # Note that for the Radix2Strategy, the only time the return size will equal the input size is if the input size is a power of 2.
27
+ # Otherwise the return will be increased to the closest power of 2.
28
+ ## Digiproc::Functions.fft([1,2,3,4,5,6,7,8]) # => [
29
+ ## # 36,
30
+ ## # (-4.0+9.65685424949238i),
31
+ ## # (-4.000000000000001+4.0i),
32
+ ## # (-4.000000000000002+1.6568542494923797i),
33
+ ## # -4,
34
+ ## # (-3.9999999999999996-1.6568542494923797i),
35
+ ## # (-3.999999999999999-4.0i),
36
+ ## # (-3.999999999999998-9.65685424949238i)]
37
+
38
+ def fft(data)
39
+ fft_strategy.new(data.dup).calculate
40
+ end
41
+
42
+ ##
43
+ ## ifft(data [Array of complex Numerics])
44
+ # Due to using the Radix2Strategy, the ifft will return the exact input if the input is a power of 2.
45
+ # Otherwise, there will be trailing 0s. ie:
46
+ ## ft = Digiproc::Functions.fft([1,2,3,4,5,6,7,8])
47
+ ## Digiproc::Functions.ifft(ft) # => [(1.0-0.0i),
48
+ ## # (2.0000000000000004-2.718345674301793e-16i),
49
+ ## # (3.0+4.440892098500626e-16i),
50
+ ## # (4.0+3.8285686989269494e-16i),
51
+ ## # (5.0-0.0i),
52
+ ## # (6.0-4.978996250514798e-17i),
53
+ ## # (7.0-4.440892098500626e-16i),
54
+ ## # (8.0-6.123233995736767e-17i)]
55
+ ## ft = Digiproc::Functions.fft([1,2,3,4,5])
56
+ ## Digiproc::Functions.ifft(ft) # => [(1.0-0.0i),
57
+ ## # (2.0+3.0616169978683836e-17i),
58
+ ## # (3.0-3.3306690738754696e-16i),
59
+ ## # (4.0+8.040613248383182e-17i),
60
+ ## # (5.0-0.0i),
61
+ ## # (0.0-1.9142843494634747e-16i),
62
+ ## # (0.0+3.3306690738754696e-16i),
63
+ ## # (0.0+8.040613248383182e-17i)]
64
+ def ifft(data)
65
+ ifft_strategy.new(data.dup).calculate
66
+ end
67
+
68
+ end
69
+
70
+ ##
71
+ # Include Digiproc::RequiresData when an instance is instantiated that includes Digiproc::FourierTransformable because
72
+ # methods require `data` (an array of Fourier Transformable data) to exist in the class
73
+ def self.included(base)
74
+ base.class_eval do
75
+ include Digiproc::RequiresData, Digiproc::Initializable
76
+ end
77
+ end
78
+
79
+ attr_writer :fft
80
+ attr_reader :fft_strategy
81
+
82
+ ##
83
+ # Initialize with (time_data: [Array(Numeric)], fft_strategy: [optional, defaults to Digiproc::Strategies::Radix2Strategy])
84
+ # Upon instantiation, a new Digiproc::FFT class is made with the data passed in as time_data.
85
+ # NOTE this does not happen automatically upon instantiation of the class which includes this module.
86
+ # The class including this module will also include Digiproc::Initializable, so you can initialize this module in the class' #initialize method as follows:
87
+ ## class TestClass
88
+ ## include Digiproc::FourierTransformable
89
+ ##
90
+ ## attr_accessor :data
91
+ ##
92
+ ## def initialize(data: )
93
+ ## @data = data
94
+ ## initialize_modules(Digiproc::FourierTransformable => {time_data: data})
95
+ ## end
96
+ ## end
97
+ ##
98
+ # Note that the calculation of the FFT itself is lazy and will not be calculated unless there is an
99
+ # attempt to access it or #calculate is called on @fft
100
+ def initialize(time_data: , fft_strategy: Digiproc::Strategies::Radix2Strategy)
101
+ @fft_strategy = fft_strategy
102
+ @fft = Digiproc::FFT.new(time_data: time_data.dup, strategy: fft_strategy)
103
+ end
104
+
105
+ ##
106
+ ## fft_db #=> Array of decible values of the magnitude of the FFT (Float, not complex)
107
+ # Ensures the fft is calculated, and then returns the dB vals
108
+ def fft_db
109
+ setup_fft
110
+ @fft.fft.db
111
+ end
112
+
113
+ ## fft_db #=> Array of values of the magnitude of the FFT (numeric, not complex)
114
+ # Ensures the fft is calculated, and then returns the magnitude
115
+ def fft_magnitude
116
+ setup_fft
117
+ @fft.magnitude
118
+ end
119
+
120
+ ##
121
+ ## fft(size [optional Integer]) #=> returns Digiproc::FFT instance
122
+ # Will calculate the FFT if it has not yet been calculated
123
+ # size is an optional parameter which allows you to delegate the size of the FFT to be calculated..
124
+ # This is useful if you want a particular resolution of the frequency domain, or if you are trying to match
125
+ # FFT sizes for calculation purposes.
126
+ def fft(size = @fft.data.size)
127
+ if @fft.data.size != size
128
+ @fft.calculate_at_size(size)
129
+ end
130
+ @fft
131
+ end
132
+
133
+ ##
134
+ # setter for the FFT strategy
135
+ def fft_strategy=(strategy)
136
+ @fft.strategy = strategy
137
+ end
138
+
139
+ ##
140
+ ## fft_data # => Array of FFT vals (complex Numeric)
141
+ # Calculates the fft if not yet calculated, and returns the calculation
142
+ def fft_data
143
+ setup_fft
144
+ @fft.fft
145
+ end
146
+
147
+ ##
148
+ ## fft_data # => Array of angle vals (radians)
149
+ # Calculate the fft if not yet calculated, and returns Arra of the angle data in radians
150
+ def fft_angle
151
+ setup_fft
152
+ @fft.angle
153
+ end
154
+
155
+ ##
156
+ ## fft_real # => Array of real vals
157
+ # Calculate the fft if not yet calculated, and retun an Array of the real values
158
+ def fft_real
159
+ setup_fft
160
+ @fft.real
161
+ end
162
+
163
+ ##
164
+ ## fft_imaginary # => Array of imaginary vals
165
+ # Calculate the fft if not yet calculated, and retun an Array of the imaginary values
166
+ def fft_imaginary
167
+ setup_fft
168
+ @fft.fft.imaginary
169
+ end
170
+
171
+ private
172
+ ##
173
+ # Private method which calculates the fft
174
+ def setup_fft
175
+ self.fft.calculate if self.fft.fft.nil?
176
+ end
177
+
178
+ end
@@ -0,0 +1,43 @@
1
+
2
+ ##
3
+ # A module for classes which have other modules which can accept parameters from the class.
4
+ # For example when included in a class, `Digiproc::FourierTransformable` automatically also includes
5
+ # 'Digiproc::Initializable' because `Digiproc::FourierTransformable` needs time data (and an optional Strategy) to
6
+ # generate the FFT for the data in the class. While another appropriate pattern could be to make the `Digiproc::FFT` generation
7
+ # lazy, and just pull the `time_data` from the class' `data` property when any fft property is first queried, this pattern
8
+ # allows more flexibility by allowing more customizable setup during instantiation
9
+ module Digiproc::Initializable
10
+
11
+ ##
12
+ # Adds a `initialize_modules` method to the including class which allows you to call `initialize_modules(*modules)`
13
+ # where *modules can be Modules or a hash of Module => {params: params_values} for module initialization.
14
+ # Modules which support initialization have an `initialize` method that can be called in this way
15
+ # See `Digiproc::DigitalSignal` for an example
16
+ ## initialize_modules(Module => { module_initializer_accepts_key: value, another_key: value2})
17
+ def self.included(base)
18
+ base.class_eval do
19
+ def initialize_modules(*modules)
20
+ modules.each do |m|
21
+ if verify_params(m) == :valid_hash
22
+ m.keys.first.instance_method(:initialize).bind(self).call(m.values.first)
23
+ elsif verify_params(m) == :valid_module
24
+ m.instance_method(:initialize).bind(self).call
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def verify_params(item)
32
+ return :valid_module if item.is_a? Module
33
+ if item.is_a? Hash
34
+ return :valid_hash if item.keys.length == 1 and item.keys.first.is_a? Module
35
+ end
36
+ raise ArgumentError.new("Each argument must either be a module or a hash of type Module => args")
37
+ end
38
+
39
+ end
40
+ end
41
+
42
+
43
+ end
@@ -0,0 +1,22 @@
1
+ ##
2
+ # Including Class instance must have a data property which is an array. Allows you to say
3
+ # classInstance1 * classInstance2 and the two data vectors will be multiplied on an element-by-element basis
4
+ # Note: the data vectors must be the same length
5
+
6
+ module Digiproc::Multipliable
7
+
8
+ def self.included(base)
9
+ base.class_eval do
10
+ include RequiresData
11
+ end
12
+ end
13
+
14
+
15
+ ##
16
+ # Multiplies the instance's `data` property on an element-by-element basis with the other instance's data property
17
+ def * (obj)
18
+ raise ArgumentError.new("Object must have #data reader") if not obj.respond_to?(:data)
19
+ raise ArgumentError.new("Object data must respond to #times, #{obj.data.class} does not") if not obj.data.respond_to?(:times)
20
+ self.data.times obj.data
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ ##
2
+ # Deternine the OS for classes which need to perform system commands
3
+ module Digiproc::OS
4
+
5
+
6
+ ##
7
+ # return true if in a windows env
8
+ def windows?
9
+ (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
10
+ end
11
+
12
+ ##
13
+ # return true if in a mac env
14
+ def mac?
15
+ (/darwin/ =~ RUBY_PLATFORM) != nil
16
+ end
17
+
18
+ ##
19
+ # return true if in a unix env
20
+ def unix?
21
+ !windows?
22
+ end
23
+
24
+ ##
25
+ # return true if in a linux env
26
+ def linux?
27
+ unix? and not mac?
28
+ end
29
+
30
+ ##
31
+ # return true if using jruby
32
+ def ruby?
33
+ RUBY_ENGINE == 'jruby'
34
+ end
35
+
36
+ end