digiproc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +21 -0
- data/README.md +78 -0
- data/Rakefile +37 -0
- data/TODO.md +50 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/environment.rb +118 -0
- data/console_tests.rb +44 -0
- data/digiproc.gemspec +49 -0
- data/examples/analog_signals/analog_to_digital.rb +31 -0
- data/examples/analog_signals/companded-signals.png +0 -0
- data/examples/analog_signals/companding.rb +68 -0
- data/examples/analog_signals/fft-plot.png +0 -0
- data/examples/analog_signals/plot_Digiproc::FFT.png +0 -0
- data/examples/analog_signals/plot_Dsp::FFT.png +0 -0
- data/examples/analog_signals/quantization-outputs.png +0 -0
- data/examples/analog_signals/quantize_compand.rb +69 -0
- data/examples/binomial_distribution/bit_error.rb +14 -0
- data/examples/binomial_distribution/dice.rb +35 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/coded_time_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/frequency_signal,_ts_=_2_s.png +0 -0
- data/examples/digital_signals/modulate_square_pulses.rb +9 -0
- data/examples/digital_signals/modulated_sq._pulses.png +0 -0
- data/examples/digital_signals/modulated_sq._pulses_alt.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_1_s.png +0 -0
- data/examples/digital_signals/power_spectral_density,__ts_=_2_s.png +0 -0
- data/examples/digital_signals/square_signals.rb +90 -0
- data/examples/digital_signals/time_signal,_ts_=_1_s.png +0 -0
- data/examples/digital_signals/time_signal,_ts_=_2_s.png +0 -0
- data/examples/encoding/gray_code.rb +22 -0
- data/examples/encoding/psk.rb +91 -0
- data/examples/encoding/system_2_phase.png +0 -0
- data/examples/encoding/system_2_xmit_signal.png +0 -0
- data/examples/encoding/system_3_phase.png +0 -0
- data/examples/encoding/system_3_xmit_signal.png +0 -0
- data/examples/encoding/system_4_xmit_signal.png +0 -0
- data/examples/encoding/xor-dpsk-phase-signal-(sys1).png +0 -0
- data/examples/encoding/xor-dpsk-xmit-signal-(sys-1).png +0 -0
- data/examples/factories/Quickplot Graph.png +0 -0
- data/examples/factories/bandpass.rb +6 -0
- data/examples/fft/plot_Dsp::FFT.png +0 -0
- data/examples/fft/recieved_data_(time_domain).png +0 -0
- data/examples/fft/simple_fft_example.rb +47 -0
- data/examples/fft/unprocessed_fft.png +0 -0
- data/examples/filters/bandpass_filter.png +0 -0
- data/examples/filters/filter_a_signal.rb +38 -0
- data/examples/filters/white_noise_db_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_mag_out_of_bp_filter.png +0 -0
- data/examples/filters/white_noise_spectra.png +0 -0
- data/examples/functions/compute_probability.rb +29 -0
- data/examples/functions/gram_schmidt.rb +10 -0
- data/examples/functions/minimize_energy.rb +29 -0
- data/examples/functions/orthoganalize.rb +18 -0
- data/examples/functions/simple_functions.rb +81 -0
- data/examples/linear_algebra/diverging_sys.rb +13 -0
- data/examples/linear_algebra/iterative_sys_of_eqns_methods.rb +27 -0
- data/examples/modulation_schemes/dpsk_2.png +0 -0
- data/examples/modulation_schemes/dpsk_256.png +0 -0
- data/examples/modulation_schemes/dpsk_freq_domain.rb +119 -0
- data/examples/modulation_schemes/psk.rb +36 -0
- data/examples/modulation_schemes/psk_2.png +0 -0
- data/examples/modulation_schemes/psk_256.png +0 -0
- data/examples/modulation_schemes/psksystem_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/psksystem_3_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_1_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_2_xmit_signal.png +0 -0
- data/examples/modulation_schemes/system_3_xmit_signal.png +0 -0
- data/examples/quickplot/PlottableClass_plot.png +0 -0
- data/examples/quickplot/decorators.rb +13 -0
- data/examples/quickplot/direct_gruff.png +0 -0
- data/examples/quickplot/plot_PlottableClass.png +0 -0
- data/examples/quickplot/quickplot_vs_others.rb +85 -0
- data/examples/quickplot/random_data_quickplot,_dark.png +0 -0
- data/examples/quickplot/random_data_quickplot.png +0 -0
- data/examples/realized_gaussian/norm_dist_plot.png +0 -0
- data/examples/realized_gaussian/norm_dist_spectrum.png +0 -0
- data/examples/realized_gaussian/realized_gaussian_example.rb +23 -0
- data/lib/concerns/convolvable.rb +144 -0
- data/lib/concerns/data_properties.rb +223 -0
- data/lib/concerns/fourier_transformable.rb +178 -0
- data/lib/concerns/initializable.rb +43 -0
- data/lib/concerns/multipliable.rb +22 -0
- data/lib/concerns/os.rb +36 -0
- data/lib/concerns/plottable.rb +248 -0
- data/lib/concerns/requires_data.rb +8 -0
- data/lib/digiproc/version.rb +8 -0
- data/lib/digiproc.rb +2 -0
- data/lib/extensions/array_extension.rb +23 -0
- data/lib/extensions/core_extensions.rb +117 -0
- data/lib/factories/factories.rb +3 -0
- data/lib/factories/filter_factory.rb +83 -0
- data/lib/factories/window_factory.rb +22 -0
- data/lib/fft.rb +255 -0
- data/lib/filters/bandpass_filter.rb +43 -0
- data/lib/filters/bandstop_filter.rb +44 -0
- data/lib/filters/digital_filter.rb +59 -0
- data/lib/filters/highpass_filter.rb +27 -0
- data/lib/filters/lowpass_filter.rb +27 -0
- data/lib/functions.rb +221 -0
- data/lib/probability/binomial_distribution.rb +84 -0
- data/lib/probability/bit_generator.rb +94 -0
- data/lib/probability/gaussian_distribution.rb +29 -0
- data/lib/probability/probability.rb +234 -0
- data/lib/probability/theoretical_gaussian_distribution.rb +59 -0
- data/lib/quick_plot.rb +96 -0
- data/lib/rbplot.rb +219 -0
- data/lib/signals/analog_signal.rb +143 -0
- data/lib/signals/digital_signal.rb +181 -0
- data/lib/strategies/code/differential_encoding_strategy.rb +69 -0
- data/lib/strategies/code/gray_code.rb +75 -0
- data/lib/strategies/code/xor_differential_encoding_strategy.rb +100 -0
- data/lib/strategies/code/xor_differential_encoding_zero_angle_strategy.rb +103 -0
- data/lib/strategies/companding/custom_companding_strategy.rb +29 -0
- data/lib/strategies/convolution/bf_conv.rb +57 -0
- data/lib/strategies/fft/brute_force_dft_strategy.rb +31 -0
- data/lib/strategies/fft/inverse_fft_conjugate_strategy.rb +44 -0
- data/lib/strategies/fft/radix2_strategy.rb +84 -0
- data/lib/strategies/gaussian/gaussian_generator.rb +49 -0
- data/lib/strategies/linear_algebra/gauss_seidel_strategy.rb +90 -0
- data/lib/strategies/linear_algebra/jacobi_strategy.rb +81 -0
- data/lib/strategies/linear_algebra/sor2_strategy.rb +98 -0
- data/lib/strategies/linear_algebra/sor_strategy.rb +108 -0
- data/lib/strategies/modulation/phase_shift_keying_strategy.rb +96 -0
- data/lib/strategies/orthogonalize/gram_schmidt.rb +50 -0
- data/lib/strategies/strategies.rb +3 -0
- data/lib/strategies/window/blackman_window.rb +32 -0
- data/lib/strategies/window/hamming_window.rb +31 -0
- data/lib/strategies/window/hanning_window.rb +31 -0
- data/lib/strategies/window/kaiser_window.rb +27 -0
- data/lib/strategies/window/rectangular_window.rb +22 -0
- data/lib/strategies/window/window.rb +42 -0
- data/lib/systems/custom_system.rb +13 -0
- data/lib/systems/hilbert_transform.rb +6 -0
- data/lib/systems/matched_filter.rb +21 -0
- data/lib/systems/raised_cosine_filter.rb +11 -0
- data/lib/systems/system.rb +19 -0
- data/lib/systems/systems.rb +3 -0
- data/playground.rb +323 -0
- data/plots/_coded_frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/_coded_frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/coded_freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/coded_power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_1_s.png +0 -0
- data/plots/coded_time_signal,_ts_=_2_s.png +0 -0
- data/plots/dpsk_2.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_1_s.png +0 -0
- data/plots/freq_sig_from_eqn,_ts_=_2_s.png +0 -0
- data/plots/frequency_signal,_ts_=_1_s.png +0 -0
- data/plots/frequency_signal,_ts_=_2_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_1_s.png +0 -0
- data/plots/power_spectral_density,__ts_=_2_s.png +0 -0
- data/plots/psk_2.png +0 -0
- data/plots/time_signal,_ts_=_1_s.png +0 -0
- data/plots/time_signal,_ts_=_2_s.png +0 -0
- data/test-title-dark.png +0 -0
- data/test-title.png +0 -0
- 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
|
data/lib/digiproc.rb
ADDED
@@ -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,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
|