spcore 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|