spcore 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -48,4 +48,10 @@ Add Envelope and Extrema classes, for signal analysis.
48
48
  === 0.1.9 / 2013-05-06
49
49
 
50
50
  Add TukeyWindow.
51
- Add Signal#keep_frequences, Signal#remove_frequencies, and Plotter#plot_signals.
51
+ Add Signal#keep_frequences, Signal#remove_frequencies, and Plotter#plot_signals.
52
+
53
+ === 0.2.0 / 2013-05-21
54
+
55
+ Add instance methods to Signal class: #duration, #normalize, #derivative, #lowpass, #highpass, #bandpass, #bandstop, #plot_1d, and #plot_2d.
56
+ Add Correlation class to find similarity between signals (used to find a feature in an image).
57
+ Make envelopes smoother with polynomial upsampling. In Signal#envelope and Signal, always return a Signal object (so also remove make_signal flag).
@@ -25,6 +25,9 @@ A library of signal processing methods and classes.
25
25
  * Oscillator with selectable wave type (sine, square, triangle, sawtooth)
26
26
  * Signal abstraction class
27
27
  * Extrema & Envelope measurement
28
+ * Normalization
29
+ * Derivatives
30
+ * Correlation
28
31
 
29
32
  == Examples
30
33
 
@@ -41,6 +41,7 @@ require 'spcore/resampling/discrete_resampling'
41
41
  require 'spcore/resampling/polynomial_resampling'
42
42
  require 'spcore/resampling/hybrid_resampling'
43
43
 
44
+ require 'spcore/analysis/correlation'
44
45
  require 'spcore/analysis/extrema'
45
46
  require 'spcore/analysis/envelope'
46
47
  require 'spcore/analysis/signal'
@@ -0,0 +1,55 @@
1
+ module SPCore
2
+ # Determines the normalized cross-correlation of a feature with an image.
3
+ # Normalization is from -1 to +1, where +1 is high correlation, -1 is high
4
+ # correlation (of inverse), and 0 is no correlation.
5
+ #
6
+ # For autocorrelation, just cross-correlate a signal with itself.
7
+ #
8
+ # @author James Tunnell
9
+ class Correlation
10
+ attr_reader :data
11
+
12
+ # @param [Array] image The values which are actually recieved/measured.
13
+ # @param [Array] feature The values to be searched for in the image. Size must not be greater than size of image.
14
+ # @param [Fixnum] zero_padding Number of zeros to surround the image with.
15
+ def initialize image, feature, zero_padding = 0
16
+ raise ArgumentError, "feature size is > image size" if feature.size > image.size
17
+
18
+ unless zero_padding == 0
19
+ image = Array.new(zero_padding, 0) + image + Array.new(zero_padding, 0)
20
+ end
21
+
22
+ feature_mean = feature.inject(0){ |s, x| s + x } / feature.size.to_f
23
+ feature_diff = feature.map {|x| x - feature_mean }
24
+ sx = feature_diff.inject(0){ |s, x| s + x**2 }
25
+
26
+ @data = []
27
+ for i in 0...(1 + image.size - feature.size)
28
+ region = image[i...(i + feature.size)]
29
+ region_mean = region.inject(0){|s,x| s + x } / feature.size.to_f
30
+ region_diff = region.map {|x| x - region_mean }
31
+ sy = region_diff.inject(0){ |s, x| s + x**2 }
32
+
33
+ if sx == 0 || sy == 0
34
+ if sx == 0 && sy == 0
35
+ @data.push 1.0
36
+ else
37
+ @data.push 0.0
38
+ end
39
+
40
+ next
41
+ end
42
+
43
+ denom = Math.sqrt(sx*sy)
44
+
45
+ sum = 0
46
+ feature.size.times do |j|
47
+ sum += (region_diff[j] * feature_diff[j])
48
+ end
49
+
50
+ r = sum / denom
51
+ @data.push(r)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -11,47 +11,65 @@ class Envelope
11
11
  def initialize samples
12
12
  # combine absolute values of positive maxima and negative minima
13
13
  extrema = Extrema.new(samples)
14
- points = {}
14
+ starting_outline = {}
15
15
  extrema.minima.each do |idx,val|
16
16
  if val <= 0.0
17
- points[idx] = val.abs
17
+ starting_outline[idx] =val.abs
18
18
  end
19
19
  end
20
20
 
21
21
  extrema.maxima.each do |idx,val|
22
22
  if val >= 0.0
23
- points[idx] = val.abs
23
+ starting_outline[idx] = val.abs
24
24
  end
25
25
  end
26
26
 
27
27
  # add in first and last samples so the envelope follows entire signal
28
- points[0] = samples[0].abs
29
- points[samples.count - 1] = samples[samples.count - 1].abs
28
+ starting_outline[0] = samples[0].abs
29
+ starting_outline[samples.count - 1] = samples[samples.count - 1].abs
30
30
 
31
- indices = points.keys.sort
32
- @data = Array.new(samples.count, 0)
31
+ # the extrema we have now are probably not spaced evenly. Upsampling at
32
+ # this point would lead to a time-distorted signal. So the next step is to
33
+ # interpolate between all the extrema to make a crude but properly sampled
34
+ # envelope.
35
+
36
+ proper_outline = Array.new(samples.count, 0)
37
+ indices = starting_outline.keys.sort
33
38
 
34
- # interpolate between all the extrema for the rest of the values. We
35
- # manually added first/last index so the whole signal should be covered
36
- # by this
37
39
  for i in 1...indices.count
38
40
  l_idx = indices[i-1]
39
41
  r_idx = indices[i]
40
42
 
41
- l_val = points[l_idx]
42
- r_val = points[r_idx]
43
+ l_val = starting_outline[l_idx]
44
+ r_val = starting_outline[r_idx]
43
45
 
44
- @data[l_idx] = l_val
45
- @data[r_idx] = r_val
46
+ proper_outline[l_idx] = l_val
47
+ proper_outline[r_idx] = r_val
46
48
 
47
49
  idx_span = r_idx - l_idx
48
50
 
49
51
  for j in (l_idx + 1)...(r_idx)
50
52
  x = (j - l_idx).to_f / idx_span
51
53
  y = Interpolation.linear l_val, r_val, x
52
- @data[j] = y
54
+ proper_outline[j] = y
53
55
  end
54
56
  end
57
+
58
+ # Now downsample by dropping samples, back to the number of starting_outline we had
59
+ # with just the extrema, but this time with samples properly spaced so as
60
+ # to avoid time distortion after upsampling.
61
+
62
+ downsample_factor = (samples.count / starting_outline.count).to_i
63
+ downsampled_outline = []
64
+
65
+ (0...proper_outline.count).step(downsample_factor) do |n|
66
+ downsampled_outline.push proper_outline[n]
67
+ end
68
+
69
+ # finally, use polynomial interpolation to upsample to the original sample rate.
70
+
71
+ upsample_factor = samples.count / downsampled_outline.count.to_f
72
+ @data = PolynomialResampling.upsample(downsampled_outline, upsample_factor)
55
73
  end
56
74
 
57
75
  end
@@ -37,22 +37,107 @@ class Signal
37
37
  def size
38
38
  @data.size
39
39
  end
40
+
41
+ # Size of the signal data.
42
+ def count
43
+ @data.size
44
+ end
45
+
46
+ # Signal duration in seconds.
47
+ def duration
48
+ return @data.size.to_f / @sample_rate
49
+ end
40
50
 
41
51
  # Access signal data.
42
52
  def [](arg)
43
53
  @data[arg]
44
54
  end
55
+
56
+ # Plot the signal data against sample numbers.
57
+ def plot_1d
58
+ plotter = Plotter.new(:title => "Signal: values vs. sample number", :xtitle => "sample number", :ytitle => "sample value")
59
+ plotter.plot_1d "signal data" => @data
60
+ end
61
+
62
+ # Plot the signal data against time.
63
+ def plot_2d
64
+ plotter = Plotter.new(:title => "Signal: values vs. time", :xtitle => "time (sec)", :ytitle => "sample value")
65
+
66
+ data_vs_time = {}
67
+ sp = 1.0 / @sample_rate
68
+ @data.each_index do |i|
69
+ data_vs_time[i * sp] = @data[i]
70
+ end
71
+
72
+ plotter.plot_2d "signal data" => data_vs_time
73
+ end
74
+
75
+ # Run a discrete lowpass filter over the signal data (using SincFilter).
76
+ # Modifies current object.
77
+ def lowpass! cutoff_freq, order
78
+ filter = SincFilter.new(:sample_rate => @sample_rate, :order => order, :cutoff_freq => cutoff_freq)
79
+ @data = filter.lowpass(@data)
80
+ return self
81
+ end
82
+
83
+ # Run a discrete lowpass filter over the signal data (using SincFilter).
84
+ # Return result in a new Signal object.
85
+ def lowpass cutoff_freq, order
86
+ self.clone.lowpass! cutoff_freq, order
87
+ end
88
+
89
+ # Run a discrete highpass filter over the signal data (using SincFilter).
90
+ # Modifies current object.
91
+ def highpass! cutoff_freq, order
92
+ filter = SincFilter.new(:sample_rate => @sample_rate, :order => order, :cutoff_freq => cutoff_freq)
93
+ @data = filter.highpass(@data)
94
+ return self
95
+ end
96
+
97
+ # Run a discrete highpass filter over the signal data (using SincFilter).
98
+ # Return result in a new Signal object.
99
+ def highpass cutoff_freq, order
100
+ self.clone.highpass! cutoff_freq, order
101
+ end
102
+
103
+ # Run a discrete bandpass filter over the signal data (using DualSincFilter).
104
+ # Modifies current object.
105
+ def bandpass! left_cutoff, right_cutoff, order
106
+ filter = DualSincFilter.new(
107
+ :sample_rate => @sample_rate,
108
+ :order => order,
109
+ :left_cutoff_freq => left_cutoff,
110
+ :right_cutoff_freq => right_cutoff
111
+ )
112
+ @data = filter.bandpass(@data)
113
+ return self
114
+ end
45
115
 
46
- # Plot the signal data, either against sample numbers or fraction of total samples.
47
- # @param plot_against_fraction If false, plot data against sample number. If true,
48
- # plot against fraction of total samples.
49
- def plot_data plot_against_fraction
50
- xtitle = (plot_against_fraction ? "fraction of total samples" : "sample numbers")
51
- plotter = Plotter.new(:title => "signal data sequence", :xtitle => xtitle, :ytitle => "sample values")
52
- titled_sequence = {"signal data" => @data}
53
- plotter.plot_1d titled_sequence, plot_against_fraction
116
+ # Run a discrete bandpass filter over the signal data (using DualSincFilter).
117
+ # Return result in a new Signal object.
118
+ def bandpass left_cutoff, right_cutoff, order
119
+ self.clone.bandpass! left_cutoff, right_cutoff, order
120
+ end
121
+
122
+ # Run a discrete bandstop filter over the signal data (using DualSincFilter).
123
+ # Modifies current object.
124
+ def bandstop! left_cutoff, right_cutoff, order
125
+ filter = DualSincFilter.new(
126
+ :sample_rate => @sample_rate,
127
+ :order => order,
128
+ :left_cutoff_freq => left_cutoff,
129
+ :right_cutoff_freq => right_cutoff
130
+ )
131
+ @data = filter.bandstop(@data)
132
+ return self
54
133
  end
55
134
 
135
+ # Run a discrete bandstop filter over the signal data (using DualSincFilter).
136
+ # Return result in a new Signal object.
137
+ def bandstop left_cutoff, right_cutoff, order
138
+ self.clone.bandstop! left_cutoff, right_cutoff, order
139
+ end
140
+
56
141
  # Increase the sample rate of signal data by the given factor using
57
142
  # discrete upsampling method. Modifies current object.
58
143
  # @param [Fixnum] upsample_factor Increase the sample rate by this factor.
@@ -181,21 +266,43 @@ class Signal
181
266
  Math.sqrt(energy / size)
182
267
  end
183
268
 
269
+ # Compute the mean of signal data.
270
+ def mean
271
+ sum = @data.inject(0){ |s, x| s + x }
272
+ return sum.to_f / size
273
+ end
274
+
184
275
  # Find extrema (maxima, minima) within signal data.
185
276
  def extrema
186
277
  return Extrema.new(@data)
187
278
  end
188
279
 
280
+ # Operate on the signal data (in place) with the absolute value function.
281
+ def abs!
282
+ @data = @data.map {|x| x.abs }
283
+ return self
284
+ end
285
+
286
+ # Operate on copy of the Signal object with the absolute value function.
287
+ def abs
288
+ self.clone.abs!
289
+ end
290
+
291
+ def normalize! level = 1.0
292
+ self.divide!(@data.max / level)
293
+ end
294
+
295
+ # reduce all samples to
296
+ def normalize level = 1.0
297
+ self.clone.normalize! level
298
+ end
299
+
189
300
  # Determine the envelope of the current Signal and return either a Envelope
190
301
  # or a new Signal object as a result.
191
302
  # @param [True/False] make_signal If true, return envelope data in a new
192
303
  # Otherwise, return an Envelope object.
193
- def envelope make_signal = false
194
- if make_signal
195
- return Signal.new(:sample_rate => @sample_rate, :data => Envelope.new(@data).data)
196
- else
197
- return Envelope.new(@data)
198
- end
304
+ def envelope
305
+ Signal.new(:sample_rate => @sample_rate, :data => Envelope.new(@data).data)
199
306
  end
200
307
 
201
308
  # Add data in array or other signal to the beginning of current data.
@@ -437,7 +544,26 @@ class Signal
437
544
  end
438
545
 
439
546
  return cross_correlation
440
- end
547
+ end
548
+
549
+ # Differentiates the signal data.
550
+ # @param [true/false] make_signal If true, return the result as a new
551
+ # Signal object. Otherwise, return result
552
+ # as an array.
553
+ def derivative
554
+ raise "Signal does not have at least 2 samples" unless @data.size > 2
555
+
556
+ derivative = Array.new(@data.size)
557
+ sample_period = 1.0 / @sample_rate
558
+
559
+ for i in 1...@data.count
560
+ derivative[i] = (@data[i] - @data[i-1]) / sample_period
561
+ end
562
+
563
+ derivative[0] = derivative[1]
564
+
565
+ return Signal.new(:sample_rate => @sample_rate, :data => derivative)
566
+ end
441
567
 
442
568
  # Removes all but the given range of frequencies from the signal, using
443
569
  # frequency domain filtering. Modifes and returns the current object.
@@ -25,11 +25,12 @@ class Plotter
25
25
  end
26
26
 
27
27
  # Plot XY datapoints.
28
- # @param [Hash] titled_hashes A hash that maps dataset titles to data. The data itself
29
- # is a hash also, that maps x values to y values.
28
+ # @param [Hash] titled_hashes A hash that maps title strings to 2d datasets.
29
+ # The dataset itself is a hash also, that maps
30
+ # x values to y values.
30
31
  #
31
32
  # @example
32
- # Plotter.plot_2d "somedata" => {0.0 => 4.0, 1.0 => 2.0}
33
+ # Plotter.new.plot_2d "somedata" => {0.0 => 4.0, 1.0 => 2.0}
33
34
  def plot_2d titled_hashes
34
35
  datasets = []
35
36
  titled_hashes.each do |title, hash|
@@ -51,24 +52,34 @@ class Plotter
51
52
  plot_datasets datasets
52
53
  end
53
54
 
55
+ # Plot XY datapoints.
56
+ # @param [Hash] titled_hashes A hash that maps title strings to 2d datasets.
57
+ # The dataset itself is a hash also, that maps
58
+ # x values to y values.
59
+ #
60
+ # @example
61
+ # Plotter.plot_2d "somedata" => {0.0 => 4.0, 1.0 => 2.0}
62
+ def self.plot_2d titled_hashes
63
+ return Plotter.new.plot_2d titled_hashes
64
+ end
65
+
54
66
  # Plot a sequence of values.
55
- # @param [Hash] titled_sequences A hash that maps sequence titles to data. The data itself
56
- # is an array of values. In the plot, values will be mapped to
57
- # their index in the sequence.
58
- # @param plot_against_fraction If true, instead of plotting samples against sample number, plot
59
- # them against the fraction (sample_number / total_samples).
67
+ # @param [Hash] titled_sequences A hash that maps title strings to data
68
+ # sequences. The data itself is an array of
69
+ # values. In the plot, values will be mapped to
70
+ # their index in the sequence.
60
71
  #
61
72
  # @example
62
- # Plotter.plot_1d "somedata" => [0,2,3,6,3,-1]
63
- def plot_1d titled_sequences, plot_against_fraction = false
73
+ # Plotter.new.plot_1d "somedata" => [0,2,3,6,3,-1]
74
+ def plot_1d titled_sequences
64
75
  datasets = []
65
76
  titled_sequences.each do |title, sequence|
66
77
  indices = Array.new(sequence.size)
67
78
  sequence.each_index do |i|
68
79
  indices[i] = i
69
- if plot_against_fraction
70
- indices[i] /= sequence.size.to_f
71
- end
80
+ #if plot_against_fraction
81
+ # indices[i] /= sequence.size.to_f
82
+ #end
72
83
  end
73
84
 
74
85
  dataset = Gnuplot::DataSet.new( [indices, sequence] ){ |ds|
@@ -82,6 +93,18 @@ class Plotter
82
93
  plot_datasets datasets
83
94
  end
84
95
 
96
+ # Plot a sequence of values.
97
+ # @param [Hash] titled_sequences A hash that maps title strings to data
98
+ # sequences. The data itself is an array of
99
+ # values. In the plot, values will be mapped to
100
+ # their index in the sequence.
101
+ #
102
+ # @example
103
+ # Plotter.plot_1d "somedata" => [0,2,3,6,3,-1]
104
+ def self.plot_1d titled_sequences
105
+ return Plotter.new.plot_1d titled_sequences
106
+ end
107
+
85
108
  # Plot Gnuplot::DataSet objects.
86
109
  # @param [Array] datasets An array of Gnuplot::DataSet objects.
87
110
  def plot_datasets datasets
@@ -98,7 +121,15 @@ class Plotter
98
121
  end
99
122
  end
100
123
  end
124
+
125
+ # Plot Gnuplot::DataSet objects.
126
+ # @param [Array] datasets An array of Gnuplot::DataSet objects.
127
+ def self.plot_datasets datasets
128
+ Plotter.new.plot_datasets datasets
129
+ end
101
130
 
131
+ # Plot data from Signal objects.
132
+ # @param [Hash] signals_hash A hash that maps title strings Signal objects
102
133
  def plot_signals signals_hash
103
134
  data_hash = {}
104
135
  signals_hash.each do |name, signal|
@@ -106,5 +137,11 @@ class Plotter
106
137
  end
107
138
  plot_1d data_hash
108
139
  end
140
+
141
+ # Plot data from Signal objects.
142
+ # @param [Hash] signals_hash A hash that maps title strings Signal objects
143
+ def self.plot_signals signals_hash
144
+ Plotter.new.plot_signals signals_hash
145
+ end
109
146
  end
110
147
  end
@@ -1,5 +1,5 @@
1
1
  # A library of signal processing methods and classes.
2
2
  module SPCore
3
3
  # spcore version
4
- VERSION = "0.1.9"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -0,0 +1,28 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe SPCore::Correlation do
4
+ context 'image => triangular window' do
5
+ before :all do
6
+ @size = size = 48
7
+ @triangle = TriangularWindow.new(size * 2).data
8
+ end
9
+
10
+ context 'feature => rising ramp (half size of triangular window)' do
11
+ it 'should have maximum correlation at beginning' do
12
+ rising_ramp = Array.new(@size){|i| i / @size.to_f }
13
+ correlation = Correlation.new(@triangle, rising_ramp)
14
+ correlation.data.first.should eq(correlation.data.max)
15
+ end
16
+ end
17
+
18
+ context 'feature => falling ramp (half size of triangular window)' do
19
+ it 'should have maximum correlation at end' do
20
+ falling_ramp = Array.new(@size){|i| (@size - i) / @size.to_f }
21
+ correlation = Correlation.new(@triangle, falling_ramp)
22
+ correlation.data.last.should eq(correlation.data.max)
23
+
24
+ #Plotter.plot_1d "correlate falling ramp" => correlation.data
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,22 +1,27 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe SPCore::Envelope do
4
- it 'should produce an output that follows the amplitude of the input' do
4
+ before :all do
5
5
  sample_rate = 1000
6
- sample_count = 512
6
+ sample_count = 512 * 2
7
7
  generator = SignalGenerator.new(:size => sample_count, :sample_rate => sample_rate)
8
8
 
9
- modulation_signal = generator.make_signal [4.0]
10
- modulation_signal.multiply! BlackmanWindow.new(sample_count).data
9
+ @modulation_signal = generator.make_signal [4.0], :amplitude => 0.1
10
+ @modulation_signal.multiply! BlackmanWindow.new(sample_count).data
11
11
 
12
- base_signal = generator.make_signal [64.0]
13
- base_signal.multiply! modulation_signal
14
-
15
- envelope = base_signal.envelope(true)
16
-
12
+ @base_signal = generator.make_signal [64.0]
13
+ @base_signal.multiply! @modulation_signal
14
+ end
15
+
16
+ it 'should produce an output that follows the amplitude of the input' do
17
+ envelope = @base_signal.envelope
18
+ check_envelope(envelope)
19
+ end
20
+
21
+ def check_envelope envelope
17
22
  #signals = {
18
- # "signal" => base_signal,
19
- # "modulation" => modulation_signal,
23
+ # "signal" => @base_signal,
24
+ # "modulation (abs)" => @modulation_signal.abs,
20
25
  # "envelope" => envelope,
21
26
  #}
22
27
  #
@@ -26,15 +31,17 @@ describe SPCore::Envelope do
26
31
  # :ylabel => "values",
27
32
  #).plot_signals(signals)
28
33
 
34
+ #Plotter.new().plot_2d("envelop freq magnitudes" => envelope.freq_magnitudes)
35
+
29
36
  begin
30
- ideal = modulation_signal.energy
37
+ ideal = @modulation_signal.energy
31
38
  actual = envelope.energy
32
39
  error = (ideal - actual).abs / ideal
33
40
  error.should be_within(0.1).of(0.0)
34
41
  end
35
42
 
36
43
  begin
37
- ideal = modulation_signal.rms
44
+ ideal = @modulation_signal.rms
38
45
  actual = envelope.rms
39
46
  error = (ideal - actual).abs / ideal
40
47
  error.should be_within(0.1).of(0.0)
@@ -1,6 +1,74 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe SPCore::Signal do
4
+ describe '#duration' do
5
+ it 'should produce duration in seconds, according to sample_rate' do
6
+ sample_rate = 2000
7
+ [5,50,100,1500].each do |count|
8
+ zeros = Array.new(count, 0)
9
+ signal = SPCore::Signal.new(:data => zeros, :sample_rate => sample_rate)
10
+ expected_duration = count.to_f / sample_rate
11
+ signal.duration.should eq expected_duration
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#derivative' do
17
+ before :all do
18
+ sample_rate = 200
19
+ sample_period = 1.0 / sample_rate
20
+ sample_count = sample_rate
21
+
22
+ range = -Math::PI..Math::PI
23
+ delta = (range.max - range.min) / sample_count
24
+
25
+ sin = []
26
+ cos = []
27
+
28
+ range.step(delta) do |x|
29
+ sin << Math::sin(x)
30
+ cos << (Math::PI * 2 * Math::cos(x))
31
+ end
32
+
33
+ @sin = SPCore::Signal.new(:sample_rate => sample_count, :data => sin)
34
+ @expected = SPCore::Signal.new(:sample_rate => sample_count, :data => cos)
35
+ @actual = @sin.derivative
36
+ end
37
+
38
+ it 'should produce a signal of same size' do
39
+ @actual.size.should eq @expected.size
40
+ end
41
+
42
+ it 'should produce a signal matching the 1st derivative' do
43
+ @actual.data.each_index do |i|
44
+ @actual[i].should be_within(0.1).of(@expected[i])
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '#normalize' do
50
+ before :all do
51
+ generator = SignalGenerator.new(:sample_rate => 2000, :size => 256)
52
+ @signal = generator.make_signal([40.0, 160.0]).multiply(HannWindow.new(256).data)
53
+ @max = @signal.data.max
54
+ end
55
+
56
+ it 'should be able to normalize to a level lower than peak value' do
57
+ normalized = @signal.normalize(@max * 0.75)
58
+ normalized.data.max.should eq(@max * 0.75)
59
+ end
60
+
61
+ it 'should be able to normalize to a level higher than peak value' do
62
+ normalized = @signal.normalize(@max * 1.5)
63
+ normalized.data.max.should eq(@max * 1.5)
64
+ end
65
+
66
+ it 'should be able to normalize to a level equal to peak value' do
67
+ normalized = @signal.normalize(@max)
68
+ normalized.data.max.should eq(@max)
69
+ end
70
+ end
71
+
4
72
  describe '#remove_frequencies' do
5
73
  before :each do
6
74
  generator = SignalGenerator.new(:sample_rate => 2000, :size => 256)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spcore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-06 00:00:00.000000000 Z
12
+ date: 2013-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashmake
@@ -144,6 +144,7 @@ files:
144
144
  - README.rdoc
145
145
  - Rakefile
146
146
  - lib/spcore.rb
147
+ - lib/spcore/analysis/correlation.rb
147
148
  - lib/spcore/analysis/envelope.rb
148
149
  - lib/spcore/analysis/extrema.rb
149
150
  - lib/spcore/analysis/signal.rb
@@ -190,6 +191,7 @@ files:
190
191
  - lib/spcore/windows/triangular_window.rb
191
192
  - lib/spcore/windows/tukey_window.rb
192
193
  - spcore.gemspec
194
+ - spec/analysis/correlation_spec.rb
193
195
  - spec/analysis/envelope_spec.rb
194
196
  - spec/analysis/extrema_spec.rb
195
197
  - spec/analysis/signal_spec.rb
@@ -228,7 +230,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
228
230
  version: '0'
229
231
  segments:
230
232
  - 0
231
- hash: -94862182169118755
233
+ hash: 688071795
232
234
  required_rubygems_version: !ruby/object:Gem::Requirement
233
235
  none: false
234
236
  requirements:
@@ -237,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
239
  version: '0'
238
240
  segments:
239
241
  - 0
240
- hash: -94862182169118755
242
+ hash: 688071795
241
243
  requirements: []
242
244
  rubyforge_project:
243
245
  rubygems_version: 1.8.23
@@ -245,6 +247,7 @@ signing_key:
245
247
  specification_version: 3
246
248
  summary: A library of signal processing methods and classes.
247
249
  test_files:
250
+ - spec/analysis/correlation_spec.rb
248
251
  - spec/analysis/envelope_spec.rb
249
252
  - spec/analysis/extrema_spec.rb
250
253
  - spec/analysis/signal_spec.rb