spcore 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog.rdoc +14 -1
- data/README.rdoc +10 -2
- data/lib/spcore.rb +45 -14
- data/lib/spcore/{lib → core}/circular_buffer.rb +28 -1
- data/lib/spcore/core/constants.rb +7 -1
- data/lib/spcore/{lib → core}/delay_line.rb +15 -5
- data/lib/spcore/{lib → core}/envelope_detector.rb +13 -1
- data/lib/spcore/{lib → core}/oscillator.rb +33 -3
- data/lib/spcore/core/signal.rb +350 -0
- data/lib/spcore/filters/fir/dual_sinc_filter.rb +84 -0
- data/lib/spcore/filters/fir/fir.rb +87 -0
- data/lib/spcore/filters/fir/sinc_filter.rb +68 -0
- data/lib/spcore/{lib → filters/iir}/biquad_filter.rb +7 -0
- data/lib/spcore/{lib → filters/iir}/cookbook_allpass_filter.rb +2 -0
- data/lib/spcore/{lib → filters/iir}/cookbook_bandpass_filter.rb +2 -0
- data/lib/spcore/{lib → filters/iir}/cookbook_highpass_filter.rb +2 -0
- data/lib/spcore/{lib → filters/iir}/cookbook_lowpass_filter.rb +2 -0
- data/lib/spcore/{lib → filters/iir}/cookbook_notch_filter.rb +2 -0
- data/lib/spcore/interpolation/interpolation.rb +64 -0
- data/lib/spcore/resampling/discrete_resampling.rb +78 -0
- data/lib/spcore/resampling/hybrid_resampling.rb +30 -0
- data/lib/spcore/resampling/polynomial_resampling.rb +56 -0
- data/lib/spcore/transforms/dft.rb +47 -0
- data/lib/spcore/transforms/fft.rb +125 -0
- data/lib/spcore/{lib → util}/gain.rb +3 -0
- data/lib/spcore/{core → util}/limiters.rb +10 -1
- data/lib/spcore/util/plotter.rb +91 -0
- data/lib/spcore/{lib → util}/saturation.rb +2 -9
- data/lib/spcore/util/scale.rb +41 -0
- data/lib/spcore/util/signal_generator.rb +48 -0
- data/lib/spcore/version.rb +2 -1
- data/lib/spcore/windows/bartlett_hann_window.rb +15 -0
- data/lib/spcore/windows/bartlett_window.rb +13 -0
- data/lib/spcore/windows/blackman_harris_window.rb +14 -0
- data/lib/spcore/windows/blackman_nuttall_window.rb +14 -0
- data/lib/spcore/windows/blackman_window.rb +18 -0
- data/lib/spcore/windows/cosine_window.rb +13 -0
- data/lib/spcore/windows/flat_top_window.rb +27 -0
- data/lib/spcore/windows/gaussian_window.rb +15 -0
- data/lib/spcore/windows/hamming_window.rb +16 -0
- data/lib/spcore/windows/hann_window.rb +13 -0
- data/lib/spcore/windows/lanczos_window.rb +15 -0
- data/lib/spcore/windows/nuttall_window.rb +14 -0
- data/lib/spcore/windows/rectangular_window.rb +10 -0
- data/lib/spcore/windows/triangular_window.rb +14 -0
- data/spcore.gemspec +11 -3
- data/spec/{lib → core}/circular_buffer_spec.rb +0 -0
- data/spec/{lib → core}/delay_line_spec.rb +1 -1
- data/spec/{lib → core}/envelope_detector_spec.rb +3 -3
- data/spec/{lib → core}/oscillator_spec.rb +0 -0
- data/spec/filters/fir/dual_sinc_filter_spec.rb +64 -0
- data/spec/filters/fir/sinc_filter_spec.rb +57 -0
- data/spec/filters/iir/cookbook_filter_spec.rb +30 -0
- data/spec/interpolation/interpolation_spec.rb +49 -0
- data/spec/resampling/discrete_resampling_spec.rb +81 -0
- data/spec/resampling/hybrid_resampling_spec.rb +31 -0
- data/spec/resampling/polynomial_resampling_spec.rb +30 -0
- data/spec/transforms/dft_spec.rb +71 -0
- data/spec/transforms/fft_spec.rb +84 -0
- data/spec/{lib → util}/gain_spec.rb +2 -2
- data/spec/{core → util}/limiters_spec.rb +0 -0
- data/spec/{lib → util}/saturate_spec.rb +0 -0
- data/spec/util/signal_generator_spec.rb +54 -0
- data/spec/windows/window_spec.rb +33 -0
- metadata +90 -42
- data/lib/spcore/lib/interpolation.rb +0 -15
- data/spec/lib/cookbook_filter_spec.rb +0 -44
- data/spec/lib/interpolation_spec.rb +0 -21
@@ -0,0 +1,48 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Provides methods for generating a Signal that contains test waveforms or noise.
|
3
|
+
class SignalGenerator
|
4
|
+
include Hashmake::HashMakeable
|
5
|
+
|
6
|
+
# used to process hashed args in #initialize.
|
7
|
+
ARG_SPECS = [
|
8
|
+
Hashmake::ArgSpec.new(:key => :size, :reqd => true, :type => Fixnum, :validator => ->(a){ a > 0 }),
|
9
|
+
Hashmake::ArgSpec.new(:key => :sample_rate, :reqd => true, :type => Float, :validator => ->(a){ a > 0.0 })
|
10
|
+
]
|
11
|
+
|
12
|
+
attr_reader :sample_rate, :size
|
13
|
+
|
14
|
+
# A new instance of SignalGenerator.
|
15
|
+
# @param [Hash] args Required keys are :sample_rate and :size.
|
16
|
+
def initialize args
|
17
|
+
hash_make ARG_SPECS, args
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generate a Signal object with noise data.
|
21
|
+
def make_noise amplitude = 1.0
|
22
|
+
output = Array.new(@size)
|
23
|
+
output.each_index do |i|
|
24
|
+
output[i] = rand * amplitude
|
25
|
+
end
|
26
|
+
|
27
|
+
return Signal.new(:sample_rate => @sample_rate, :data => output)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generate a Signal object with waveform data at the given frequencies.
|
31
|
+
def make_signal freqs, extra_osc_args = {}
|
32
|
+
args = { :sample_rate => @sample_rate }.merge! extra_osc_args
|
33
|
+
oscs = []
|
34
|
+
freqs.each do |freq|
|
35
|
+
oscs.push Oscillator.new args.merge(:frequency => freq)
|
36
|
+
end
|
37
|
+
|
38
|
+
output = Array.new(size, 0.0)
|
39
|
+
size.times do |n|
|
40
|
+
oscs.each do |osc|
|
41
|
+
output[n] += osc.sample
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return Signal.new(:sample_rate => @sample_rate, :data => output)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/spcore/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Bartlett-Hann window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Bartlett.E2.80.93Hann_window.
|
4
|
+
class BartlettHannWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
a0, a1, a2 = 0.62, 0.48, 0.38
|
9
|
+
|
10
|
+
size.times do |n|
|
11
|
+
@data[n] = a0 - (a1 * ((n.to_f / (size - 1)) - 0.5).abs) - (a2 * Math::cos((TWO_PI * n)/(size - 1)))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a triangular window of a given size (number of samples).
|
3
|
+
# Endpoints are zero. Midpoint is one. There is a linear slope between endpoints and midpoint.
|
4
|
+
class BartlettWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
size.times do |n|
|
9
|
+
@data[n] = (2.0 / (size - 1)) * (((size - 1) / 2.0) - (n - ((size - 1) / 2.0)).abs)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Blackman-Harris window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Blackman.E2.80.93Harris_window.
|
4
|
+
class BlackmanHarrisWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
a0, a1, a2, a3 = 0.35875, 0.48829, 0.14128, 0.01168
|
9
|
+
size.times do |n|
|
10
|
+
@data[n] = a0 - a1 * Math::cos((TWO_PI * n)/(size - 1)) + a2 * Math::cos((FOUR_PI * n)/(size - 1)) - a3 * Math::cos((SIX_PI * n)/(size - 1))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Blackman-Nuttall window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Blackman.E2.80.93Nuttall_window.
|
4
|
+
class BlackmanNuttallWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
a0, a1, a2, a3 = 0.3635819, 0.4891775, 0.1365995, 0.0106411
|
9
|
+
size.times do |n|
|
10
|
+
@data[n] = a0 - a1 * Math::cos((TWO_PI * n)/(size - 1)) + a2 * Math::cos((FOUR_PI * n)/(size - 1)) - a3 * Math::cos((SIX_PI * n)/(size - 1))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Blackman window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Blackman_windows.
|
4
|
+
class BlackmanWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new size
|
8
|
+
alpha = 0.16
|
9
|
+
a0 = (1 - alpha) / 2.0
|
10
|
+
a1 = 0.5
|
11
|
+
a2 = alpha / 2.0
|
12
|
+
|
13
|
+
size.times do |n|
|
14
|
+
@data[n] = a0 - (a1 * Math::cos((TWO_PI * n)/(size - 1))) + (a2 * Math::cos((FOUR_PI * n)/(size - 1)))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Cosine window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Cosine_window.
|
4
|
+
class CosineWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new size
|
8
|
+
size.times do |n|
|
9
|
+
@data[n] = Math::sin((Math::PI * n)/(size - 1))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a flat top window of a given size (number of samples).
|
3
|
+
# A flat top window is a partially negative-valued window that has a flat top in
|
4
|
+
# the frequency domain. They are designed to have a broader bandwidth and so have
|
5
|
+
# a poorer frequency resolution, leading to low amplitude measurement error suitable
|
6
|
+
# for use in in spectrum analyzers for the measurement of amplitudes of sinusoidal
|
7
|
+
# frequency components
|
8
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Flat_top_window.
|
9
|
+
class FlatTopWindow
|
10
|
+
attr_reader :data
|
11
|
+
def initialize size
|
12
|
+
@data = Array.new(size)
|
13
|
+
a0, a1, a2, a3, a4 = 1.0, 1.93, 1.29, 0.388, 0.032
|
14
|
+
|
15
|
+
size.times do |n|
|
16
|
+
@data[n] = a0 - a1 * Math::cos((TWO_PI * n)/(size - 1)) + a2 * Math::cos((FOUR_PI * n)/(size - 1)) - a3 * Math::cos((SIX_PI * n)/(size - 1)) + a4 * Math::cos((EIGHT_PI * n)/(size - 1))
|
17
|
+
end
|
18
|
+
|
19
|
+
max = @data.max
|
20
|
+
|
21
|
+
# normalize to max of 1.0
|
22
|
+
@data.each_index do |i|
|
23
|
+
@data[i] /= max
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Gaussian window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Gaussian_windows.
|
4
|
+
class GaussianWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new size
|
8
|
+
sigma = 0.4 # must be <= 0.5
|
9
|
+
size.times do |n|
|
10
|
+
a = (n - (size - 1) / 2) / (sigma * (size - 1) / 2)
|
11
|
+
@data[n] = Math::exp(-0.5 * a**2)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Hamming window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Hamming_window.
|
4
|
+
class HammingWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new size
|
8
|
+
alpha = 0.54
|
9
|
+
beta = 1.0 - alpha
|
10
|
+
|
11
|
+
size.times do |n|
|
12
|
+
@data[n] = alpha - (beta * Math::cos((TWO_PI * n) / (size - 1)))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Hann window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Hann_.28Hanning.29_window.
|
4
|
+
class HannWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
size.times do |n|
|
9
|
+
@data[n] = 0.5 * (1 - Math::cos((TWO_PI * n)/(size - 1)))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Lanczos window of a given size (number of samples).
|
3
|
+
# The Lanczos window is used in Lanczos resampling.
|
4
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Lanczos_window.
|
5
|
+
class LanczosWindow
|
6
|
+
attr_reader :data
|
7
|
+
def initialize size
|
8
|
+
@data = Array.new size
|
9
|
+
sinc = lambda {|x| (Math::sin(Math::PI * x))/(Math::PI * x) }
|
10
|
+
size.times do |n|
|
11
|
+
@data[n] = sinc.call(((2.0*n)/(size-1)) - 1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a Nuttall window of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Nuttall_window.2C_continuous_first_derivative.
|
4
|
+
class NuttallWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size)
|
8
|
+
a0, a1, a2, a3 = 0.355768, 0.487396, 0.144232, 0.012604
|
9
|
+
size.times do |n|
|
10
|
+
@data[n] = a0 - a1 * Math::cos((TWO_PI * n)/(size - 1)) + a2 * Math::cos((FOUR_PI * n)/(size - 1)) - a3 * Math::cos((SIX_PI * n)/(size - 1))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a rectangular window (all ones) of a given size (number of samples).
|
3
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Rectangular_window.
|
4
|
+
class RectangularWindow
|
5
|
+
attr_reader :data
|
6
|
+
def initialize size
|
7
|
+
@data = Array.new(size, 1.0)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Produces a triangular window of a given size (number of samples).
|
3
|
+
# Endpoints are near zero. Midpoint is one. There is a linear slope between endpoints and midpoint.
|
4
|
+
# For more info, see https://en.wikipedia.org/wiki/Window_function#Triangular_window
|
5
|
+
class TriangularWindow
|
6
|
+
attr_reader :data
|
7
|
+
def initialize size
|
8
|
+
@data = Array.new(size)
|
9
|
+
size.times do |n|
|
10
|
+
@data[n] = (2.0 / (size + 1)) * (((size + 1) / 2.0) - (n - ((size - 1) / 2.0)).abs)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/spcore.gemspec
CHANGED
@@ -5,15 +5,23 @@ require File.expand_path('../lib/spcore/version', __FILE__)
|
|
5
5
|
Gem::Specification.new do |gem|
|
6
6
|
gem.name = "spcore"
|
7
7
|
gem.version = SPCore::VERSION
|
8
|
-
gem.summary = %q{
|
8
|
+
gem.summary = %q{A library of signal processing methods and classes.}
|
9
9
|
gem.description = <<DESCRIPTION
|
10
|
-
Contains core signal processing
|
10
|
+
Contains core signal processing methods and classes, including:
|
11
|
+
Resampling (discrete up, down and up/down, polynomial up, and hybrid up/down)
|
12
|
+
FFT transform (forward and inverse)
|
13
|
+
DFT transform (forward and inverse)
|
14
|
+
Windows (Blackman, Hamming, etc.)
|
15
|
+
Windowed sinc filter for lowpass and highpass.
|
16
|
+
Dual windowed sinc filter for bandpass and bandstop.
|
17
|
+
Interpolation (linear and polynomial)
|
18
|
+
Data plotting via gnuplot (must be installed to use).
|
11
19
|
Delay line
|
12
20
|
Biquad filters
|
13
21
|
Envelope detector
|
14
22
|
Conversion from dB-linear and linear-dB
|
15
|
-
Linear interpolation
|
16
23
|
Oscillator with selectable wave type (sine, square, triangle, sawtooth)
|
24
|
+
Signal abstraction class.
|
17
25
|
|
18
26
|
DESCRIPTION
|
19
27
|
gem.license = "MIT"
|
File without changes
|
@@ -3,8 +3,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe SPCore::EnvelopeDetector do
|
4
4
|
describe '#process_sample' do
|
5
5
|
it 'should produce an output that follows the amplitude of the input' do
|
6
|
-
sample_rate =
|
7
|
-
freqs = [20.0,
|
6
|
+
sample_rate = 400.0
|
7
|
+
freqs = [20.0, 60.0]
|
8
8
|
|
9
9
|
envelope_start = 1.0
|
10
10
|
envelope_end = 0.0
|
@@ -26,7 +26,7 @@ describe SPCore::EnvelopeDetector do
|
|
26
26
|
sample_count = (5.0 * sample_rate / freq).to_i
|
27
27
|
sample_count.times do |n|
|
28
28
|
percent = n.to_f / sample_count
|
29
|
-
amplitude = SPCore::Interpolation.linear
|
29
|
+
amplitude = SPCore::Interpolation.linear envelope_start, envelope_end, percent
|
30
30
|
osc.amplitude = amplitude
|
31
31
|
|
32
32
|
sample = osc.sample
|
File without changes
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
3
|
+
|
4
|
+
describe SPCore::SincFilter do
|
5
|
+
before :all do
|
6
|
+
@sample_rate = 4000.0
|
7
|
+
@orders = [62]
|
8
|
+
@left_cutoffs = Scale.exponential 300.0..1500.0, 3
|
9
|
+
@right_cutoffs = Scale.exponential 400.0..1600.0, 3
|
10
|
+
end
|
11
|
+
|
12
|
+
context '.bandpass' do
|
13
|
+
it 'should keep magnitude below -20 dB below left cutoff and above right cutoff, and close to 0 dB between' do
|
14
|
+
@orders.each do |order|
|
15
|
+
@left_cutoffs.each_index do |i|
|
16
|
+
left_cutoff = @left_cutoffs[i]
|
17
|
+
right_cutoff = @right_cutoffs[i]
|
18
|
+
|
19
|
+
if order % 2 == 1
|
20
|
+
order += 1
|
21
|
+
end
|
22
|
+
filter = DualSincFilter.new :order => order, :left_cutoff_freq => left_cutoff, :right_cutoff_freq => right_cutoff, :sample_rate => @sample_rate, :window_class => BlackmanWindow
|
23
|
+
#filter.bandpass_fir.plot_freq_response false
|
24
|
+
freq_response = filter.bandpass_fir.freq_response true
|
25
|
+
|
26
|
+
freq_response.each do |freq, magnitude|
|
27
|
+
if freq <= (0.8 * left_cutoff) || freq >= (1.2 * right_cutoff)
|
28
|
+
magnitude.should be < -20.0 # using dB
|
29
|
+
elsif freq.between?(1.2 * left_cutoff, 0.8 * right_cutoff)
|
30
|
+
magnitude.should be_within(1.0).of(0.0) # using dB
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context '.bandstop' do
|
39
|
+
it 'should keep magnitude close to 0 dB below left cutoff and above right cutoff, and below -20 dB between' do
|
40
|
+
@orders.each do |order|
|
41
|
+
@left_cutoffs.each_index do |i|
|
42
|
+
left_cutoff = @left_cutoffs[i]
|
43
|
+
right_cutoff = @right_cutoffs[i]
|
44
|
+
|
45
|
+
if order % 2 == 1
|
46
|
+
order += 1
|
47
|
+
end
|
48
|
+
filter = DualSincFilter.new :order => order, :left_cutoff_freq => left_cutoff, :right_cutoff_freq => right_cutoff, :sample_rate => @sample_rate, :window_class => BlackmanWindow
|
49
|
+
#filter.bandstop_fir.plot_freq_response false
|
50
|
+
freq_response = filter.bandstop_fir.freq_response true
|
51
|
+
|
52
|
+
freq_response.each do |freq, magnitude|
|
53
|
+
if freq <= (0.8 * left_cutoff) || freq >= (1.2 * right_cutoff)
|
54
|
+
magnitude.should be_within(1.0).of(0.0) # using dB
|
55
|
+
elsif freq.between?(1.2 * left_cutoff, 0.8 * right_cutoff)
|
56
|
+
magnitude.should be < -20.0 # using dB
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
3
|
+
|
4
|
+
describe SPCore::SincFilter do
|
5
|
+
before :all do
|
6
|
+
@sample_rate = 4000.0
|
7
|
+
@orders = [62]
|
8
|
+
@cutoffs = Scale.exponential 400.0..1600.0, 3
|
9
|
+
end
|
10
|
+
|
11
|
+
context '.highpass' do
|
12
|
+
it 'should keep magnitude below-20 dB below cutoff and close to 0 dB above cutoff' do
|
13
|
+
@orders.each do |order|
|
14
|
+
@cutoffs.each do |cutoff|
|
15
|
+
if order % 2 == 1
|
16
|
+
order += 1
|
17
|
+
end
|
18
|
+
filter = SincFilter.new :order => order, :cutoff_freq => cutoff, :sample_rate => @sample_rate, :window_class => BlackmanWindow
|
19
|
+
#filter.highpass_fir.plot_freq_response false
|
20
|
+
freq_response = filter.highpass_fir.freq_response true
|
21
|
+
|
22
|
+
freq_response.each do |freq, magnitude|
|
23
|
+
if freq <= (0.8 * cutoff)
|
24
|
+
magnitude.should be < -20.0 # using dB
|
25
|
+
elsif freq >= (1.2 * cutoff)
|
26
|
+
magnitude.should be_within(1.0).of(0.0) # using dB
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '.lowpass' do
|
35
|
+
it 'should keep magnitude close to 0 dB below cutoff and below-20 dB above cutoff' do
|
36
|
+
@orders.each do |order|
|
37
|
+
@cutoffs.each do |cutoff|
|
38
|
+
if order % 2 == 1
|
39
|
+
order += 1
|
40
|
+
end
|
41
|
+
filter = SincFilter.new :order => order, :cutoff_freq => cutoff, :sample_rate => @sample_rate, :window_class => BlackmanWindow
|
42
|
+
#filter.lowpass_fir.plot_freq_response false
|
43
|
+
freq_response = filter.lowpass_fir.freq_response true
|
44
|
+
|
45
|
+
freq_response.each do |freq, magnitude|
|
46
|
+
if freq <= (0.8 * cutoff)
|
47
|
+
magnitude.should be_within(1.0).of(0.0) # using dB
|
48
|
+
elsif freq >= (1.2 * cutoff)
|
49
|
+
magnitude.should be < -20.0 # using dB
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|