digiproc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,248 @@
1
+ require 'gruff'
2
+
3
+ ##
4
+ # Defines plotting helpers using the `gruff` library.
5
+ # See `examples/quickplot/quickplot_vs_others.rb for good examples of this module and Quickplot (which extends Digiproc::Plottable::InstanceMethods)
6
+
7
+ module Digiproc::Plottable
8
+
9
+ ##
10
+ # Defines custom plot styles to be used
11
+ module Styles
12
+ MIDNIGHT = {
13
+ :colors => [
14
+ '#00dae5', #teal
15
+ '#FF1A1A', # red
16
+ '#3FFC07', # green
17
+ '#FDD84E', # yellow
18
+ '#6886B4', # blue
19
+ '#8A6EAF', # purple
20
+ '#EFAA43', # orange
21
+ 'white'
22
+ ],
23
+ :marker_color => '#7e7c7f',
24
+ :font_color => '#cccccc',
25
+ :background_colors => %w(black #2a2a2a)
26
+ }
27
+
28
+ SUBMARINE = {
29
+ :colors => [
30
+ '#FF1A1A', # red
31
+ '#3FFC07', # green
32
+ '#FDD84E', # yellow
33
+ '#6886B4', # blue
34
+ '#8A6EAF', # purple
35
+ '#EFAA43', # orange
36
+ 'white'
37
+ ],
38
+ :marker_color => '#3d007a',
39
+ :font_color => '#cccccc',
40
+ :background_colors => %w(black #2a2a2a)
41
+ }
42
+
43
+ BLUESCALE = {
44
+ :colors => [
45
+ '#0b0b70', #
46
+ '#383838', #
47
+ '#686868', #
48
+ '#989898', #
49
+ '#c8c8c8', #
50
+ '#e8e8e8', #
51
+ ],
52
+ :marker_color => '#aea9a9', # Grey
53
+ :font_color => 'black',
54
+ :background_colors => 'white'
55
+ }
56
+ end
57
+
58
+ ##
59
+ # Contains generic plotting helpers which can be extended to make more specific plotting helpers, or can be used
60
+ # in another class to extend their functionality. Digiproc::Plottable does extend self::ClassMethods, so they can be used
61
+ # as standalone plotting functions using Digiproc::Plottable.iplot() { |g| ... } or Digiproc::Plottable.qplot(...)
62
+
63
+ module ClassMethods
64
+
65
+
66
+ # TODO: Below method will be plotted inside a block instead
67
+ # of passing params to the method. Also, the logic below the
68
+ # yield will fix the problem of x float values not being able to
69
+ # have corresponding labels. Lastly, it will return an instance
70
+ # of the plot without writing it, so it can be decorated with
71
+ # vertical lines, or any other future decorator.
72
+
73
+ ##
74
+ # Will yield g and allow the caller to define the plot as they wish. It does very little beforehand to setup the plot
75
+
76
+ def iplot(xsteps: 4)
77
+ g = Gruff::Line.new('1000x1000')
78
+ g.theme = Digiproc::Plottable::Styles::BLUESCALE
79
+ g.line_width = 2
80
+ g.dot_radius = 0.1
81
+ yield g
82
+ #must insert data :name, data_arr
83
+ #or dataxy :name, x_arr, y_arr
84
+
85
+ # Go through each dataset,
86
+ #If there are x values, get max and min
87
+ #If there are not, get the length of the data
88
+ #If labels are not inserted by user, do:
89
+ # get range of largest dataset
90
+ # label at 0, 0.25*len, 0.5*len, 0.75*len, len
91
+ return g
92
+ end
93
+
94
+
95
+
96
+
97
+ ##
98
+ # Used by Digiproc::QuickPlot.
99
+ ## qplot(x: Array[Numeric], y: Array[Numeric], data: Array[Numeric], data_name: String, xyname: String, filename: String, path: String, xsteps: Integer, label_map: ->(Float) returns Float) #=> returns a plot at the entered directory or './plots' by default (ensure directory exists)
100
+ # x and y OR data must exist to make a plot.
101
+ # `label_map` is used to map the index of the data (or the x value at that point if using xy) to an appropriate label. For example if the x values are between 1 and 10 but data.length is 10000, your label_map could be:
102
+ ## label_map = ->(index_val){ return index_val / 1000.0 }
103
+ # The frequency of labels will be determined by `xsteps`
104
+ def qplot(x: nil ,y: nil , data: nil, data_name: "data", xyname: "data",filename: "#{self}_plot", path: "./plots/", xsteps: 4, label_map: nil)
105
+ raise ArgumentError.new("Either x and y or data must exist") if data.nil? and (x.nil? or y.nil?)
106
+ data = data
107
+ raise TypeError.new("Data must be an array, not a #{data.class}") if data and not data.is_a? Array
108
+ raise TypeError.new("X and Y must be arrays, not #{x.class}, #{y.class}") if (!!x and !!y) and not (x.is_a?(Array) and y.is_a?(Array))
109
+ raise ArgumentError.new("X and Y must be the same size") if (!!x and !!y) and (x.length != y.length)
110
+ g = Gruff::Line.new('1000x1000')
111
+ g.theme = Styles::BLUESCALE
112
+ g.line_width = 2.5
113
+ g.dot_radius = 0.1
114
+ # g.minimum_x_value = 0
115
+ g.data data_name, data if !!data
116
+ g.dataxy xyname, x, y if !!x and !!y
117
+ xmax = !!data ? data.length : x.max
118
+ xmin = !!data ? 0 : x.min
119
+ increment_label = (xmax - xmin) / xsteps.to_f
120
+ datalength = !!data ? data.length : x.length
121
+ increment_datapoints = (datalength.to_f / xsteps).round
122
+ labels = {}
123
+ for i in 0..xsteps do
124
+ datapoint_location = i * increment_datapoints
125
+ datapoint_location -= 1 if datapoint_location > (datalength - 1)
126
+ label_val = label_map.nil? ? (i * increment_label).round(2) : label_map.call((i * increment_label)).round(2)
127
+ labels[datapoint_location] = label_val
128
+ end
129
+ g.labels = labels
130
+ g.show_vertical_markers = false
131
+ yield g
132
+ g.write(path + filename + '.png')
133
+ end
134
+
135
+
136
+ #TODO: Figure out a way to display a vertical line using a decorator.
137
+ #The class below displays a line from the upper left to lower right hand corner of the plot
138
+
139
+ # class VerticalLine
140
+
141
+ # attr_reader :plot, :x_value, :label, :color
142
+
143
+ # def initialize(plot, x_value, label = "vline", color = "white")
144
+ # @plot, @x_value, @label, @color = plot, x_value, label, color
145
+ # end
146
+
147
+ # def write(location)
148
+ # add_vertical_line
149
+ # puts plot.instance_variable_get(:@data).to_s
150
+ # plot.write(location)
151
+ # end
152
+
153
+ # private
154
+
155
+ # def add_vertical_line
156
+ # data = plot.instance_variable_get(:@data)
157
+ # data_cpy = data.dup
158
+ # data_num = data.length
159
+ # max_y_val = data.map{ |dataset| get_max_y(dataset) }.max
160
+ # min_y_val = data.map{ |dataset| get_min_y(dataset) }.min
161
+ # plot.dataxy label, [x_value, x_value+1], [max_y_val, min_y_val]
162
+ # plot.colors[data_num] = color
163
+ # end
164
+
165
+ # def get_max_y(arr)
166
+ # arr[1].max
167
+ # end
168
+
169
+ # def get_min_y(arr)
170
+ # arr[1].min
171
+ # end
172
+
173
+ # def has_x_values?(data)
174
+ # data.length == 4
175
+ # end
176
+ # end
177
+
178
+
179
+ end
180
+
181
+ extend self::ClassMethods
182
+
183
+ ##
184
+ # Can be included in classes in which you may want a specific plot for a method output (ie the Discrete Fourier Transform magnitude plot)
185
+ module InstanceMethods
186
+
187
+ ##
188
+ ## plot(method: Symbol, filename: String [default = "plot_#{self.class}"], path: String [default = "./"], xmax: Integer [default = 1], xmin: Integer [default = 0], xsteps: Integer [default = 4])
189
+ # Can be used to plot the output of a specific method. By specifying the name of the method when you call plot, if the output of that method is a Numeric Array, then a plot will be made.
190
+ # An example can be seen in Digiproc::FFT #plot_db
191
+ def plot(method:, filename: "plot_#{self.class}", path: "./", xmax: 1, xmin: 0, xsteps: 4)
192
+ data = self.send(method)
193
+ raise TypeError.new('Data must be an array, not a #{data.class}') if not data.is_a? Array
194
+ g = Gruff::Line.new('1000x1000')
195
+ g.theme = Digiproc::Plottable::Styles::MIDNIGHT
196
+ g.line_width = 1
197
+ g.dot_radius = 1
198
+ g.minimum_x_value = 0
199
+ g.data method, data
200
+ increment_label = (xmax - xmin) / xsteps.to_f
201
+ increment_datapoints = (data.length.to_f / xsteps).round
202
+ labels = {}
203
+ for i in 0..xsteps do
204
+ datapoint_location = i * increment_datapoints
205
+ datapoint_location -= 1 if datapoint_location > (data.length - 1)
206
+ labels[datapoint_location] = (i * increment_label).round(2)
207
+ end
208
+ g.labels = labels
209
+ g.show_vertical_markers = true
210
+ yield g
211
+ g.write(path + filename + '.png')
212
+ end
213
+
214
+ ##
215
+ # Same as qplot in Digiproc::Plottable::InstanceMethods
216
+
217
+ def qplot(x: nil ,y: nil , data: nil, data_name: "data", xyname: "data",filename: "#{self}_plot", path: "./plots/", xsteps: 4, label_map: nil)
218
+ raise ArgumentError.new("Either x and y or data must exist") if data.nil? and (x.nil? or y.nil?)
219
+ data = data
220
+ raise TypeError.new("Data must be an array, not a #{data.class}") if data and not data.is_a? Array
221
+ raise TypeError.new("X and Y must be arrays, not #{x.class}, #{y.class}") if (!!x and !!y) and not (x.is_a?(Array) and y.is_a?(Array))
222
+ raise ArgumentError.new("X and Y must be the same size") if (!!x and !!y) and (x.length != y.length)
223
+ g = Gruff::Line.new('1000x1000')
224
+ g.theme = Styles::BLUESCALE
225
+ g.line_width = 2.5
226
+ g.dot_radius = 0.1
227
+ # g.minimum_x_value = 0
228
+ g.data data_name, data if !!data
229
+ g.dataxy xyname, x, y if !!x and !!y
230
+ xmax = !!data ? data.length : x.max
231
+ xmin = !!data ? 0 : x.min
232
+ increment_label = (xmax - xmin) / xsteps.to_f
233
+ datalength = !!data ? data.length : x.length
234
+ increment_datapoints = (datalength.to_f / xsteps).round
235
+ labels = {}
236
+ for i in 0..xsteps do
237
+ datapoint_location = i * increment_datapoints
238
+ datapoint_location -= 1 if datapoint_location > (datalength - 1)
239
+ label_val = label_map.nil? ? (i * increment_label).round(2) : label_map.call((i * increment_label)).round(2)
240
+ labels[datapoint_location] = label_val
241
+ end
242
+ g.labels = labels
243
+ g.show_vertical_markers = false
244
+ yield g
245
+ g.write(path + filename + '.png')
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,8 @@
1
+ ##
2
+ # Many modules in this gem require that the class which includes them has a property called `data`. This module can be used by modules which require the use
3
+ # of the `data` property to ensure that they are being used correctly. If `data` is not a property, a `TypeError` will be thrown upon inclusion into the class.
4
+ module Digiproc::RequiresData
5
+ def self.included(base)
6
+ raise TypeError.new("To implement #{self}, #{base} must have an instance varaible @data, and getters/setters") if not base.method_defined?(:data)
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Digiproc
3
+ VERSION = "0.1.0"
4
+ end
5
+
6
+
7
+
8
+
data/lib/digiproc.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative "./digiproc/version"
2
+ require_relative "../config/environment"
@@ -0,0 +1,23 @@
1
+ class Array
2
+
3
+ # def *(arr)
4
+ # raise ArgumentError.new("Array sizes must be equal") if self.size != arr.size
5
+ # output = []
6
+ # self.each_with_index do |o,i|
7
+ # output << o * arr[i]
8
+ # end
9
+ # output
10
+ # end
11
+
12
+
13
+ # def plus(arr)
14
+ # raise ArgumentError.new("sizes must be equal") if self.size != arr.size
15
+ # output = []
16
+ # self.each_with_index do |o,i|
17
+ # output << o + arr[i]
18
+ # end
19
+ # output
20
+ # end
21
+
22
+
23
+ end
@@ -0,0 +1,117 @@
1
+ ##
2
+ # Add extensions to certain Ruby Classes for easy use of the library without using too many custom classes
3
+ module Digiproc::CoreExtensions
4
+ module ArrayExtension
5
+ module DotProduct
6
+ ##
7
+ # Take the dot product of self another Array (allow complex numbers). They must be the same size. Returns a scalar
8
+ ## myArray.dot(anotherArray) # => Float
9
+ def dot(arr)
10
+ raise ArgumentError.new("Array sizes must be equal") if self.size != arr.size
11
+ output = []
12
+ self.each_with_index do |o,i|
13
+ output << o * arr[i].conjugate
14
+ end
15
+ output.sum
16
+ end
17
+ end
18
+ module Sum
19
+ ##
20
+ # Add two arrays element by element. They must be the same size
21
+ ## myArray.plus(anotherArr) # => Array (same size as the input)
22
+ def plus(arr)
23
+ raise ArgumentError.new("sizes must be equal") if self.size != arr.size
24
+ output = []
25
+ self.each_with_index do |o,i|
26
+ output << o + arr[i]
27
+ end
28
+ output
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Multiply two arrays element by element. They must be the same size
34
+ ## myArray.times(anotherArr) => Array (same size as the input)
35
+ module Multiply
36
+ def times(arr)
37
+ raise ArgumentError.new("Array sizes must be equal") if self.size != arr.size
38
+ output = []
39
+ self.each_with_index do |o,i|
40
+ output << o * arr[i]
41
+ end
42
+ output
43
+ end
44
+ end
45
+ end
46
+
47
+ # Extend functionaly of Ruby's Math module
48
+ module MathExtension
49
+ ##
50
+ # Add methods which are useful when using decible values
51
+ module Decible
52
+ ##
53
+ ## db(numeric_input [Numeric]) # => returns 20 * Math.log(numeric_input, 10)
54
+ def db(value)
55
+ 20 * Math.log(value, 10)
56
+ end
57
+
58
+ ##
59
+ ## db_power(numeric_input [Numeric]) # => returns 10 * Math.log(numeric_input, 10)
60
+ def db_power(value)
61
+ 10 * Math.log(value, 10)
62
+ end
63
+
64
+ ##
65
+ # input a decible, recieve a magnitude
66
+ def mag_from_db(decible)
67
+ 10 ** (decible / 20.0)
68
+ end
69
+
70
+ ##
71
+ # Input a decible, recieve a magnitude (power)
72
+ def power_from_db(decible)
73
+ 10 ** (decible / 10.0)
74
+ end
75
+ end
76
+ end
77
+
78
+ module VectorExtension
79
+ ##
80
+ # Extend functionality to Vector
81
+ module Projection
82
+ module ClassMethods
83
+ ##
84
+ # .projcect(vector1, vector2) returns a projection of vector 1 onto vector 2
85
+ def project(vec1, vec2)
86
+ vec1 = vec1.is_a?(Vector)? vec1 : Vector.elements(vec1)
87
+ vec2 = vec2.is_a?(Vector) ? vec2 : Vector.elements(vec2)
88
+ vec2.project_onto vec1
89
+ end
90
+ end
91
+ module InstanceMethods
92
+ ##
93
+ # .project_onto(vector) projects self onto the input vector
94
+ def project_onto(vec)
95
+ raise ArgumentError.new("Argument must be a Vector") if not vec.is_a? Vector
96
+ (self.dot(vec) / (vec.r ** 2)) * vec
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ module FloatExtension
104
+
105
+ module OddPatch
106
+
107
+ def odd?
108
+ true
109
+ end
110
+
111
+ end
112
+
113
+ end
114
+
115
+
116
+
117
+ end
@@ -0,0 +1,3 @@
1
+ module Digiproc::Factories
2
+
3
+ end
@@ -0,0 +1,83 @@
1
+ ##
2
+ # Factory for different filters.
3
+ # Filters are created using the Windowing method
4
+ # The window for the filter outputted will depend on the required stopband attenuation
5
+
6
+
7
+ class Digiproc::Factories::FilterFactory
8
+
9
+ ##
10
+ # == Give requirements of the filter:
11
+ # type:: [String] Accepts: 'highpass', 'lowpass', 'bandpass', 'bandstop'
12
+ # wc:: [Float] cutoff frequency in radians
13
+ # wo:: [Float] center frequency in radians
14
+ # bw:: [Float] bandwidth in radians
15
+ # transition_width:: [Float] in NORMALIZED FREQUENCY, ie 0 to 1 scale, where 1 = Sampling Frequency (should be changed to rad for consistency)
16
+ # stopband_attenuation:: [Numeric] level of stopband in decibles
17
+ #
18
+ # == Also Note:
19
+ # Digiproc::LowpassFilter:: requires wc, not wo or bw
20
+ # Digiproc::HighpassFilter:: requires wc, not wo or bw
21
+ # Digiproc::BandpassFilter:: requires wo and bw, not wc
22
+ # Digiproc::BandstopFilter:: requires wo and bw, not wc
23
+ # NOTE: This factory makes all sizes odd in ensure all types of filters will work (ie an even number of values will not allow a highpass filter, an anti-symmetric odd will not allow a lowpass, etc.)
24
+ # Available windows: Digiproc::RectangularWindow, Digiproc::HanningWindow, Digiproc::HammingWindow, Digiproc::BlackmanWindow
25
+ def self.filter_for(type: , wc: nil, wo: nil, bw: nil, transition_width: nil, stopband_attenuation: )
26
+ window = nil
27
+ size = 0
28
+ if stopband_attenuation < 21
29
+ window = Digiproc::RectangularWindow
30
+ size = make_odd(0.9 / transition_width)
31
+ elsif stopband_attenuation < 40
32
+ window = Digiproc::HanningWindow
33
+ size = make_odd(3.1 / transition_width)
34
+ elsif stopband_attenuation < 50
35
+ window = Digiproc::HammingWindow
36
+ size = make_odd(3.3 / transition_width)
37
+ else
38
+ window = Digiproc::BlackmanWindow
39
+ size = make_odd(5.5 / transition_width)
40
+ end
41
+
42
+ case type.to_s.downcase
43
+ when 'lowpass'
44
+ return Filters::lowpass.new(wc: wc, size: size, window: window)
45
+ when 'highpass'
46
+ return Filters::highpass.new(wc: wc, size: size, window: window)
47
+ when 'bandpass'
48
+ return Filters::bandpass.new(wo: wo, bw: bw, size: size, window: window)
49
+ when 'bandstop'
50
+ return Filters::bandstop.new(wo: wo, bw: bw, size: size, window: window)
51
+ else
52
+ raise ArgumentError.new('Filter types include: lowpass, highpass, bandpass, bandstop')
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Input Numeric, output closest round integer >= to input
58
+ def self.make_odd(num)
59
+ n = num.round
60
+ n += 1 if n.even?
61
+ n
62
+ end
63
+
64
+ # An unnecessary inner class that Digiproc::Factories::FilterFactory uses. Only a wrapper to Digiproc::XXXXXFilter
65
+ class Filters
66
+ def self.lowpass
67
+ Digiproc::LowpassFilter
68
+ end
69
+
70
+ def self.highpass
71
+ Digiproc::HighpassFilter
72
+ end
73
+
74
+ def self.bandpass
75
+ Digiproc::BandpassFilter
76
+ end
77
+
78
+ def self.bandstop
79
+ Digiproc::BandstopFilter
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,22 @@
1
+ ##
2
+ # Factory class for Windows. Can output Digiproc::HanningWindow, Digiproc::HammingWindow, and Digiproc::BlackmanWindow
3
+
4
+ class Digiproc::Factories::WindowFactory
5
+
6
+ ##
7
+ # Decision made based off ofstopband_attenuation
8
+ # @example
9
+ # Digiproc::Factories::WindowFactory.window_for(normalized_transition_width: 0.05, stopband_attenuation: 60) # => outputs Digiproc::BlackmanWindow instance
10
+ def self.window_for(normalized_transition_width: , stopband_attenuation: )
11
+
12
+ if stopband_attenuation < 40
13
+ return Digiproc::HanningWindow.new(norm_trans_freq: normalized_transition_width)
14
+ elsif stopband_attenuation < 50
15
+ return Digiproc::HammingWindow.new(norm_trans_freq: normalized_transition_width)
16
+ else
17
+ return Digiproc::BlackmanWindow.new(norm_trans_freq: normalized_transition_width)
18
+ end
19
+ end
20
+
21
+
22
+ end