spcore 0.1.7 → 0.1.8
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.
- data/ChangeLog.rdoc +6 -2
- data/README.rdoc +1 -0
- data/lib/spcore.rb +3 -1
- data/lib/spcore/core/envelope.rb +58 -0
- data/lib/spcore/core/extrema.rb +55 -0
- data/lib/spcore/core/signal.rb +121 -25
- data/lib/spcore/interpolation/interpolation.rb +11 -11
- data/lib/spcore/resampling/hybrid_resampling.rb +1 -1
- data/lib/spcore/resampling/polynomial_resampling.rb +1 -2
- data/lib/spcore/{core → util}/envelope_detector.rb +0 -0
- data/lib/spcore/util/plotter.rb +8 -1
- data/lib/spcore/util/signal_generator.rb +2 -2
- data/lib/spcore/version.rb +1 -1
- data/spec/core/envelope_spec.rb +29 -0
- data/spec/core/extrema_spec.rb +42 -0
- data/spec/resampling/hybrid_resampling_spec.rb +2 -2
- data/spec/{core → util}/envelope_detector_spec.rb +0 -0
- metadata +13 -7
data/ChangeLog.rdoc
CHANGED
@@ -37,6 +37,10 @@ Adjust project documentation.
|
|
37
37
|
|
38
38
|
Update to be compatible with hashmake-0.1.6.
|
39
39
|
|
40
|
-
=== 0.1.7
|
40
|
+
=== 0.1.7 / 2013-04-18
|
41
41
|
|
42
|
-
Require all sample rate args to be Fixnum.
|
42
|
+
Require all sample rate args to be Fixnum.
|
43
|
+
|
44
|
+
=== 0.1.8 / 2013-05-03
|
45
|
+
|
46
|
+
Add Envelope and Extrema classes, for signal analysis.
|
data/README.rdoc
CHANGED
data/lib/spcore.rb
CHANGED
@@ -4,9 +4,10 @@ require 'spcore/version'
|
|
4
4
|
require 'spcore/core/circular_buffer'
|
5
5
|
require 'spcore/core/constants'
|
6
6
|
require 'spcore/core/delay_line'
|
7
|
-
require 'spcore/core/envelope_detector'
|
8
7
|
require 'spcore/core/oscillator'
|
9
8
|
require 'spcore/core/signal'
|
9
|
+
require 'spcore/core/extrema'
|
10
|
+
require 'spcore/core/envelope'
|
10
11
|
|
11
12
|
require 'spcore/windows/bartlett_hann_window'
|
12
13
|
require 'spcore/windows/bartlett_window'
|
@@ -48,3 +49,4 @@ require 'spcore/util/plotter'
|
|
48
49
|
require 'spcore/util/saturation'
|
49
50
|
require 'spcore/util/scale'
|
50
51
|
require 'spcore/util/signal_generator'
|
52
|
+
require 'spcore/util/envelope_detector'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Determines the envelope of given samples. Unlike the EnvelopeDetector, this
|
3
|
+
# operates on a signal (time-series data) after it is recieved, not
|
4
|
+
# sample-by-sample. As a result, it provides the envelope as an entire signal.
|
5
|
+
#
|
6
|
+
# @author James Tunnell
|
7
|
+
class Envelope < Signal
|
8
|
+
|
9
|
+
attr_reader :data
|
10
|
+
|
11
|
+
def initialize samples
|
12
|
+
# combine absolute values of positive maxima and negative minima
|
13
|
+
extrema = Extrema.new(samples)
|
14
|
+
points = {}
|
15
|
+
extrema.minima.each do |idx,val|
|
16
|
+
if val <= 0.0
|
17
|
+
points[idx] = val.abs
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
extrema.maxima.each do |idx,val|
|
22
|
+
if val >= 0.0
|
23
|
+
points[idx] = val.abs
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
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
|
30
|
+
|
31
|
+
indices = points.keys.sort
|
32
|
+
@data = Array.new(samples.count, 0)
|
33
|
+
|
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
|
+
for i in 1...indices.count
|
38
|
+
l_idx = indices[i-1]
|
39
|
+
r_idx = indices[i]
|
40
|
+
|
41
|
+
l_val = points[l_idx]
|
42
|
+
r_val = points[r_idx]
|
43
|
+
|
44
|
+
@data[l_idx] = l_val
|
45
|
+
@data[r_idx] = r_val
|
46
|
+
|
47
|
+
idx_span = r_idx - l_idx
|
48
|
+
|
49
|
+
for j in (l_idx + 1)...(r_idx)
|
50
|
+
x = (j - l_idx).to_f / idx_span
|
51
|
+
y = Interpolation.linear l_val, r_val, x
|
52
|
+
@data[j] = y
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SPCore
|
2
|
+
# Finds extrema (minima and maxima).
|
3
|
+
class Extrema
|
4
|
+
|
5
|
+
attr_reader :minima, :maxima, :extrema
|
6
|
+
|
7
|
+
def initialize samples
|
8
|
+
@minima = {}
|
9
|
+
@maxima = {}
|
10
|
+
|
11
|
+
global_min_idx = 0
|
12
|
+
global_min_val = samples[0]
|
13
|
+
global_max_idx = 0
|
14
|
+
global_max_val = samples[0]
|
15
|
+
|
16
|
+
diffs = []
|
17
|
+
for i in (1...samples.count)
|
18
|
+
diffs.push(samples[i] - samples[i-1])
|
19
|
+
|
20
|
+
if samples[i] < global_min_val
|
21
|
+
global_min_idx = i
|
22
|
+
global_min_val = samples[i]
|
23
|
+
end
|
24
|
+
|
25
|
+
if samples[i] > global_max_val
|
26
|
+
global_max_idx = i
|
27
|
+
global_max_val = samples[i]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@minima[global_min_idx] = global_min_val
|
31
|
+
@maxima[global_max_idx] = global_max_val
|
32
|
+
|
33
|
+
is_positive = diffs.first > 0.0 # starting off with positive difference?
|
34
|
+
|
35
|
+
# at zero crossings there is a local maxima/minima
|
36
|
+
for i in (1...diffs.count)
|
37
|
+
if is_positive
|
38
|
+
# at positive-to-negative transition there is a local maxima
|
39
|
+
if diffs[i] <= 0.0
|
40
|
+
@maxima[i] = samples[i]
|
41
|
+
is_positive = false
|
42
|
+
end
|
43
|
+
else
|
44
|
+
# at negative-to-positive transition there is a local minima
|
45
|
+
if diffs[i] > 0.0
|
46
|
+
@minima[i] = samples[i]
|
47
|
+
is_positive = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
@extrema = @minima.merge(@maxima)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/spcore/core/signal.rb
CHANGED
@@ -89,7 +89,7 @@ class Signal
|
|
89
89
|
# polynomial interpolation.
|
90
90
|
# @param [Fixnum] upsample_factor Increase the sample rate by this factor.
|
91
91
|
def upsample_polynomial upsample_factor
|
92
|
-
@data = PolynomialResampling.upsample @data,
|
92
|
+
@data = PolynomialResampling.upsample @data, upsample_factor
|
93
93
|
@sample_rate *= upsample_factor
|
94
94
|
return self
|
95
95
|
end
|
@@ -134,24 +134,14 @@ class Signal
|
|
134
134
|
return @data.inject(0.0){|sum,x| sum + (x * x)}
|
135
135
|
end
|
136
136
|
|
137
|
-
#
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
env_detector = EnvelopeDetector.new(:attack_time => attack_time, :release_time => release_time, :sample_rate => @sample_rate)
|
143
|
-
|
144
|
-
envelope = Array.new(@data.count)
|
145
|
-
|
146
|
-
for i in 0...@data.count do
|
147
|
-
envelope[i] = env_detector.process_sample @data[i]
|
148
|
-
end
|
149
|
-
|
150
|
-
return envelope
|
137
|
+
# Determine the envelope of the current Signal and return the resulting data
|
138
|
+
# in a new Signal object.
|
139
|
+
def envelope
|
140
|
+
return Envelope.new(@data)
|
151
141
|
end
|
152
142
|
|
153
143
|
# Add data in array or other signal to the beginning of current data.
|
154
|
-
def prepend other
|
144
|
+
def prepend! other
|
155
145
|
if other.is_a?(Array)
|
156
146
|
@data = other.concat @data
|
157
147
|
elsif other.is_a?(Signal)
|
@@ -161,7 +151,7 @@ class Signal
|
|
161
151
|
end
|
162
152
|
|
163
153
|
# Add data in array or other signal to the end of current data.
|
164
|
-
def append other
|
154
|
+
def append! other
|
165
155
|
if other.is_a?(Array)
|
166
156
|
@data = @data.concat other
|
167
157
|
elsif other.is_a?(Signal)
|
@@ -173,7 +163,7 @@ class Signal
|
|
173
163
|
# Add value, values in array, or values in other signal to the current
|
174
164
|
# data values, and update the current data with the results.
|
175
165
|
# @param other Can be Numeric (add same value to all data values), Array, or Signal.
|
176
|
-
def
|
166
|
+
def add!(other)
|
177
167
|
if other.is_a?(Numeric)
|
178
168
|
@data.each_index do |i|
|
179
169
|
@data[i] += other
|
@@ -193,11 +183,37 @@ class Signal
|
|
193
183
|
end
|
194
184
|
return self
|
195
185
|
end
|
186
|
+
|
187
|
+
# Add value, values in array, or values in other signal to the current
|
188
|
+
# data values, and return a new Signal object with the results.
|
189
|
+
# @param other Can be Numeric (add same value to all data values), Array, or Signal.
|
190
|
+
def add(other)
|
191
|
+
new_data = Array.new(@data.size)
|
192
|
+
|
193
|
+
if other.is_a?(Numeric)
|
194
|
+
@data.each_index do |i|
|
195
|
+
new_data[i] = @data[i] + other
|
196
|
+
end
|
197
|
+
elsif other.is_a?(Signal)
|
198
|
+
raise ArgumentError, "other.data.size #{other.size} is not equal to data.size #{@data.size}" if other.data.size != @data.size
|
199
|
+
@data.each_index do |i|
|
200
|
+
new_data[i] = @data[i] + other.data[i]
|
201
|
+
end
|
202
|
+
elsif other.is_a?(Array)
|
203
|
+
raise ArgumentError, "other.size #{other.size} is not equal to data.size #{@data.size}" if other.size != @data.size
|
204
|
+
@data.each_index do |i|
|
205
|
+
new_data[i] = @data[i] + other[i]
|
206
|
+
end
|
207
|
+
else
|
208
|
+
raise ArgumentError, "other is not a Numeric, Signal, or Array"
|
209
|
+
end
|
210
|
+
return Signal.new(:data => new_data, :sample_rate => @sample_rate)
|
211
|
+
end
|
196
212
|
|
197
213
|
# Subtract value, values in array, or values in other signal from the current
|
198
214
|
# data values, and update the current data with the results.
|
199
215
|
# @param other Can be Numeric (subtract same value from all data values), Array, or Signal.
|
200
|
-
def
|
216
|
+
def subtract!(other)
|
201
217
|
if other.is_a?(Numeric)
|
202
218
|
@data.each_index do |i|
|
203
219
|
@data[i] -= other
|
@@ -218,11 +234,37 @@ class Signal
|
|
218
234
|
return self
|
219
235
|
end
|
220
236
|
|
237
|
+
# Subtract value, values in array, or values in other signal from the current
|
238
|
+
# data values, and return a new Signal object with the results.
|
239
|
+
# @param other Can be Numeric (subtract same value from all data values), Array, or Signal.
|
240
|
+
def subtract(other)
|
241
|
+
new_data = Array.new(@data.size)
|
242
|
+
|
243
|
+
if other.is_a?(Numeric)
|
244
|
+
@data.each_index do |i|
|
245
|
+
new_data[i] = @data[i] - other
|
246
|
+
end
|
247
|
+
elsif other.is_a?(Signal)
|
248
|
+
raise ArgumentError, "other.data.size #{other.size} is not equal to data.size #{@data.size}" if other.data.size != @data.size
|
249
|
+
@data.each_index do |i|
|
250
|
+
new_data[i] = @data[i] - other.data[i]
|
251
|
+
end
|
252
|
+
elsif other.is_a?(Array)
|
253
|
+
raise ArgumentError, "other.size #{other.size} is not equal to data.size #{@data.size}" if other.size != @data.size
|
254
|
+
@data.each_index do |i|
|
255
|
+
new_data[i] = @data[i] - other[i]
|
256
|
+
end
|
257
|
+
else
|
258
|
+
raise ArgumentError, "other is not a Numeric, Signal, or Array"
|
259
|
+
end
|
260
|
+
return Signal.new(:data => new_data, :sample_rate => @sample_rate)
|
261
|
+
end
|
262
|
+
|
221
263
|
# Multiply value, values in array, or values in other signal with the current
|
222
264
|
# data values, and update the current data with the results.
|
223
265
|
# @param other Can be Numeric (multiply all data values by the same value),
|
224
266
|
# Array, or Signal.
|
225
|
-
def
|
267
|
+
def multiply!(other)
|
226
268
|
if other.is_a?(Numeric)
|
227
269
|
@data.each_index do |i|
|
228
270
|
@data[i] *= other
|
@@ -243,11 +285,38 @@ class Signal
|
|
243
285
|
return self
|
244
286
|
end
|
245
287
|
|
288
|
+
# Multiply value, values in array, or values in other signal with the current
|
289
|
+
# data values, and return a new Signal object with the results.
|
290
|
+
# @param other Can be Numeric (multiply all data values by the same value),
|
291
|
+
# Array, or Signal.
|
292
|
+
def multiply(other)
|
293
|
+
new_data = Array.new(@data.size)
|
294
|
+
|
295
|
+
if other.is_a?(Numeric)
|
296
|
+
@data.each_index do |i|
|
297
|
+
new_data[i] = @data[i] * other
|
298
|
+
end
|
299
|
+
elsif other.is_a?(Signal)
|
300
|
+
raise ArgumentError, "other.data.size #{other.size} is not equal to data.size #{@data.size}" if other.data.size != @data.size
|
301
|
+
@data.each_index do |i|
|
302
|
+
new_data[i] = @data[i] * other.data[i]
|
303
|
+
end
|
304
|
+
elsif other.is_a?(Array)
|
305
|
+
raise ArgumentError, "other.size #{other.size} is not equal to data.size #{@data.size}" if other.size != @data.size
|
306
|
+
@data.each_index do |i|
|
307
|
+
new_data[i] = @data[i] * other[i]
|
308
|
+
end
|
309
|
+
else
|
310
|
+
raise ArgumentError, "other is not a Numeric, Signal, or Array"
|
311
|
+
end
|
312
|
+
return Signal.new(:data => new_data, :sample_rate => @sample_rate)
|
313
|
+
end
|
314
|
+
|
246
315
|
# Divide value, values in array, or values in other signal into the current
|
247
316
|
# data values, and update the current data with the results.
|
248
317
|
# @param other Can be Numeric (divide same all data values by the same value),
|
249
318
|
# Array, or Signal.
|
250
|
-
def
|
319
|
+
def divide!(other)
|
251
320
|
if other.is_a?(Numeric)
|
252
321
|
@data.each_index do |i|
|
253
322
|
@data[i] /= other
|
@@ -268,10 +337,37 @@ class Signal
|
|
268
337
|
return self
|
269
338
|
end
|
270
339
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
340
|
+
# Divide value, values in array, or values in other signal into the current
|
341
|
+
# data values, and return a new Signal object with the results.
|
342
|
+
# @param other Can be Numeric (divide same all data values by the same value),
|
343
|
+
# Array, or Signal.
|
344
|
+
def divide(other)
|
345
|
+
new_data = Array.new(@data.size)
|
346
|
+
|
347
|
+
if other.is_a?(Numeric)
|
348
|
+
@data.each_index do |i|
|
349
|
+
new_data[i] = @data[i] / other
|
350
|
+
end
|
351
|
+
elsif other.is_a?(Signal)
|
352
|
+
raise ArgumentError, "other.data.size #{other.size} is not equal to data.size #{@data.size}" if other.data.size != @data.size
|
353
|
+
@data.each_index do |i|
|
354
|
+
new_data[i] = @data[i] / other.data[i]
|
355
|
+
end
|
356
|
+
elsif other.is_a?(Array)
|
357
|
+
raise ArgumentError, "other.size #{other.size} is not equal to data.size #{@data.size}" if other.size != @data.size
|
358
|
+
@data.each_index do |i|
|
359
|
+
new_data[i] = @data[i] / other[i]
|
360
|
+
end
|
361
|
+
else
|
362
|
+
raise ArgumentError, "other is not a Numeric, Signal, or Array"
|
363
|
+
end
|
364
|
+
return Signal.new(:data => new_data, :sample_rate => @sample_rate)
|
365
|
+
end
|
366
|
+
|
367
|
+
alias_method :+, :add
|
368
|
+
alias_method :-, :subtract
|
369
|
+
alias_method :*, :multiply
|
370
|
+
alias_method :/, :divide
|
275
371
|
|
276
372
|
# Determine how well the another signal (g) correlates to the current signal (f).
|
277
373
|
# Correlation is determined at every point in f. The signal g must not be
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module SPCore
|
2
2
|
# Provide interpolation methods, including linear and polynomial.
|
3
3
|
class Interpolation
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def self.linear x1, y1, x2, y2, x3
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
4
|
+
## Linear Interpolation Equation:
|
5
|
+
##
|
6
|
+
## (x3 - x1)(y2 - y1)
|
7
|
+
## y3 = ------------------ + y1
|
8
|
+
## (x2 - x1)
|
9
|
+
##
|
10
|
+
#def self.linear x1, y1, x2, y2, x3
|
11
|
+
# y3 = ((x3 - x1) * (y2 - y1)) / (x2 - x1);
|
12
|
+
# y3 += y1;
|
13
|
+
# return y3;
|
14
|
+
#end
|
15
15
|
|
16
16
|
# Linear interpolator
|
17
17
|
# Given 2 sample points, interpolates a value anywhere between the two points.
|
@@ -9,7 +9,7 @@ class HybridResampling
|
|
9
9
|
raise ArgumentError, "downsample_factor is not greater than 1" unless downsample_factor > 1
|
10
10
|
raise ArgumentError, "sample_rate is not greater than 0" unless sample_rate > 0
|
11
11
|
|
12
|
-
upsampled = PolynomialResampling.upsample input,
|
12
|
+
upsampled = PolynomialResampling.upsample input, upsample_factor
|
13
13
|
|
14
14
|
needed_samples = upsampled.size % downsample_factor
|
15
15
|
if needed_samples == 0
|
@@ -3,10 +3,9 @@ module SPCore
|
|
3
3
|
# polynomial interpolation.
|
4
4
|
class PolynomialResampling
|
5
5
|
|
6
|
-
def self.upsample input,
|
6
|
+
def self.upsample input, upsample_factor
|
7
7
|
raise ArgumentError, "input.size is less than four" unless input.size >= 4
|
8
8
|
raise ArgumentError, "upsample_factor is not greater than 1" unless upsample_factor > 1
|
9
|
-
raise ArgumentError, "sample_rate is not greater than 0" unless sample_rate > 0
|
10
9
|
|
11
10
|
output = Array.new((upsample_factor * input.size).to_i)
|
12
11
|
|
File without changes
|
data/lib/spcore/util/plotter.rb
CHANGED
@@ -33,7 +33,14 @@ class Plotter
|
|
33
33
|
def plot_2d titled_hashes
|
34
34
|
datasets = []
|
35
35
|
titled_hashes.each do |title, hash|
|
36
|
-
|
36
|
+
# sort the data
|
37
|
+
|
38
|
+
sorted = {}
|
39
|
+
hash.keys.sort.each do |key|
|
40
|
+
sorted[key] = hash[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
dataset = Gnuplot::DataSet.new( [sorted.keys, sorted.values] ){ |ds|
|
37
44
|
ds.with = @linestyle
|
38
45
|
ds.title = title
|
39
46
|
ds.linewidth = @linewidth
|
@@ -35,8 +35,8 @@ class SignalGenerator
|
|
35
35
|
oscs.push Oscillator.new args.merge(:frequency => freq)
|
36
36
|
end
|
37
37
|
|
38
|
-
output = Array.new(size, 0.0)
|
39
|
-
size.times do |n|
|
38
|
+
output = Array.new(@size, 0.0)
|
39
|
+
@size.times do |n|
|
40
40
|
oscs.each do |osc|
|
41
41
|
output[n] += osc.sample
|
42
42
|
end
|
data/lib/spcore/version.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe SPCore::Envelope do
|
4
|
+
it 'should produce an output that follows the amplitude of the input' do
|
5
|
+
sample_rate = 400
|
6
|
+
sample_count = sample_rate
|
7
|
+
generator = SignalGenerator.new(:size => sample_count, :sample_rate => sample_rate)
|
8
|
+
base_signal = generator.make_signal [sample_rate / 5.0]
|
9
|
+
modulation_signal = generator.make_signal [sample_rate / 80.0]
|
10
|
+
base_signal.multiply! modulation_signal
|
11
|
+
|
12
|
+
envelope = base_signal.envelope
|
13
|
+
|
14
|
+
Plotter.new(
|
15
|
+
:title => "signal and envelope",
|
16
|
+
:xlabel => "sample",
|
17
|
+
:ylabel => "values",
|
18
|
+
).plot_1d(
|
19
|
+
"signal" => base_signal.data,
|
20
|
+
"modulation" => modulation_signal.data,
|
21
|
+
"envelope" => envelope.data,
|
22
|
+
)
|
23
|
+
|
24
|
+
max_diff = 0.1
|
25
|
+
sample_count.times do |n|
|
26
|
+
envelope.data[n].should be_within(max_diff).of(modulation_signal.data[n].abs)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe SPCore::Extrema do
|
4
|
+
context '#minima' do
|
5
|
+
it 'should return points where local and global minima occur' do
|
6
|
+
cases = {
|
7
|
+
[3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 2 => 2.9, 9 => 2.0 },
|
8
|
+
[3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 3 => 2.7, 7 => 2.2, 10 => 2.0 },
|
9
|
+
}
|
10
|
+
|
11
|
+
cases.each do |samples, minima|
|
12
|
+
Extrema.new(samples).minima.should eq minima
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context '#maxima' do
|
18
|
+
it 'should return points where local and global maxima occur' do
|
19
|
+
cases = {
|
20
|
+
[3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 0 => 3.8, 4 => 3.6 },
|
21
|
+
[3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 1 => 3.5, 4 => 2.8, 8 => 2.4},
|
22
|
+
}
|
23
|
+
|
24
|
+
cases.each do |samples, maxima|
|
25
|
+
Extrema.new(samples).maxima.should eq maxima
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#extrema' do
|
31
|
+
it 'should return points where local and global extrema occur' do
|
32
|
+
cases = {
|
33
|
+
[3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 0 => 3.8, 2 => 2.9, 4 => 3.6, 9 => 2.0},
|
34
|
+
[3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 1 => 3.5, 3 => 2.7, 4 => 2.8, 7 => 2.2, 8 => 2.4, 10 => 2.0},
|
35
|
+
}
|
36
|
+
|
37
|
+
cases.each do |samples, extrema|
|
38
|
+
Extrema.new(samples).extrema.should eq extrema
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -8,7 +8,7 @@ describe SPCore::HybridResampling do
|
|
8
8
|
sample_rate = 400
|
9
9
|
test_freq = 10.0
|
10
10
|
size = 64
|
11
|
-
upsample_factor =
|
11
|
+
upsample_factor = 10
|
12
12
|
downsample_factor = 4
|
13
13
|
order = (sample_rate / test_freq).to_i
|
14
14
|
|
@@ -16,7 +16,7 @@ describe SPCore::HybridResampling do
|
|
16
16
|
signal1 *= BlackmanWindow.new(size).data
|
17
17
|
signal2 = signal1.clone.resample_hybrid upsample_factor, downsample_factor, order
|
18
18
|
|
19
|
-
#plotter = Plotter.new(:title => "Discrete resampling, up by #{
|
19
|
+
#plotter = Plotter.new(:title => "Discrete resampling, up by #{upsample_factor}, down by #{downsample_factor}")
|
20
20
|
#plotter.plot_1d("original signal" => signal1.data, "resampled signal" => signal2.data)
|
21
21
|
|
22
22
|
signal2.size.should eq(signal1.size * upsample_factor / downsample_factor)
|
File without changes
|
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.
|
4
|
+
version: 0.1.8
|
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-04
|
12
|
+
date: 2013-05-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: hashmake
|
@@ -147,7 +147,8 @@ files:
|
|
147
147
|
- lib/spcore/core/circular_buffer.rb
|
148
148
|
- lib/spcore/core/constants.rb
|
149
149
|
- lib/spcore/core/delay_line.rb
|
150
|
-
- lib/spcore/core/
|
150
|
+
- lib/spcore/core/envelope.rb
|
151
|
+
- lib/spcore/core/extrema.rb
|
151
152
|
- lib/spcore/core/oscillator.rb
|
152
153
|
- lib/spcore/core/signal.rb
|
153
154
|
- lib/spcore/filters/fir/dual_sinc_filter.rb
|
@@ -165,6 +166,7 @@ files:
|
|
165
166
|
- lib/spcore/resampling/polynomial_resampling.rb
|
166
167
|
- lib/spcore/transforms/dft.rb
|
167
168
|
- lib/spcore/transforms/fft.rb
|
169
|
+
- lib/spcore/util/envelope_detector.rb
|
168
170
|
- lib/spcore/util/gain.rb
|
169
171
|
- lib/spcore/util/limiters.rb
|
170
172
|
- lib/spcore/util/plotter.rb
|
@@ -189,7 +191,8 @@ files:
|
|
189
191
|
- spcore.gemspec
|
190
192
|
- spec/core/circular_buffer_spec.rb
|
191
193
|
- spec/core/delay_line_spec.rb
|
192
|
-
- spec/core/
|
194
|
+
- spec/core/envelope_spec.rb
|
195
|
+
- spec/core/extrema_spec.rb
|
193
196
|
- spec/core/oscillator_spec.rb
|
194
197
|
- spec/filters/fir/dual_sinc_filter_spec.rb
|
195
198
|
- spec/filters/fir/sinc_filter_spec.rb
|
@@ -202,6 +205,7 @@ files:
|
|
202
205
|
- spec/spec_helper.rb
|
203
206
|
- spec/transforms/dft_spec.rb
|
204
207
|
- spec/transforms/fft_spec.rb
|
208
|
+
- spec/util/envelope_detector_spec.rb
|
205
209
|
- spec/util/gain_spec.rb
|
206
210
|
- spec/util/limiters_spec.rb
|
207
211
|
- spec/util/saturate_spec.rb
|
@@ -222,7 +226,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
222
226
|
version: '0'
|
223
227
|
segments:
|
224
228
|
- 0
|
225
|
-
hash:
|
229
|
+
hash: -1351761362878137001
|
226
230
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
231
|
none: false
|
228
232
|
requirements:
|
@@ -231,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
235
|
version: '0'
|
232
236
|
segments:
|
233
237
|
- 0
|
234
|
-
hash:
|
238
|
+
hash: -1351761362878137001
|
235
239
|
requirements: []
|
236
240
|
rubyforge_project:
|
237
241
|
rubygems_version: 1.8.23
|
@@ -241,7 +245,8 @@ summary: A library of signal processing methods and classes.
|
|
241
245
|
test_files:
|
242
246
|
- spec/core/circular_buffer_spec.rb
|
243
247
|
- spec/core/delay_line_spec.rb
|
244
|
-
- spec/core/
|
248
|
+
- spec/core/envelope_spec.rb
|
249
|
+
- spec/core/extrema_spec.rb
|
245
250
|
- spec/core/oscillator_spec.rb
|
246
251
|
- spec/filters/fir/dual_sinc_filter_spec.rb
|
247
252
|
- spec/filters/fir/sinc_filter_spec.rb
|
@@ -254,6 +259,7 @@ test_files:
|
|
254
259
|
- spec/spec_helper.rb
|
255
260
|
- spec/transforms/dft_spec.rb
|
256
261
|
- spec/transforms/fft_spec.rb
|
262
|
+
- spec/util/envelope_detector_spec.rb
|
257
263
|
- spec/util/gain_spec.rb
|
258
264
|
- spec/util/limiters_spec.rb
|
259
265
|
- spec/util/saturate_spec.rb
|