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.
- 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
|