roctave 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +33 -0
- data/ext/roctave/cu8_file_reader.c +331 -0
- data/ext/roctave/cu8_file_reader.h +30 -0
- data/ext/roctave/extconf.rb +6 -0
- data/ext/roctave/fir_filter.c +795 -0
- data/ext/roctave/fir_filter.h +29 -0
- data/ext/roctave/freq_shifter.c +410 -0
- data/ext/roctave/freq_shifter.h +29 -0
- data/ext/roctave/iir_filter.c +462 -0
- data/ext/roctave/iir_filter.h +29 -0
- data/ext/roctave/roctave.c +38 -0
- data/ext/roctave/roctave.h +27 -0
- data/lib/roctave.rb +168 -0
- data/lib/roctave/bilinear.rb +92 -0
- data/lib/roctave/butter.rb +87 -0
- data/lib/roctave/cheby.rb +180 -0
- data/lib/roctave/cu8_file_reader.rb +45 -0
- data/lib/roctave/dft.rb +280 -0
- data/lib/roctave/filter.rb +64 -0
- data/lib/roctave/finite_difference_coefficients.rb +73 -0
- data/lib/roctave/fir.rb +121 -0
- data/lib/roctave/fir1.rb +134 -0
- data/lib/roctave/fir2.rb +246 -0
- data/lib/roctave/fir_design.rb +311 -0
- data/lib/roctave/firls.rb +380 -0
- data/lib/roctave/firpm.rb +499 -0
- data/lib/roctave/freq_shifter.rb +47 -0
- data/lib/roctave/freqz.rb +233 -0
- data/lib/roctave/iir.rb +80 -0
- data/lib/roctave/interp1.rb +78 -0
- data/lib/roctave/plot.rb +748 -0
- data/lib/roctave/poly.rb +46 -0
- data/lib/roctave/roots.rb +73 -0
- data/lib/roctave/sftrans.rb +157 -0
- data/lib/roctave/version.rb +3 -0
- data/lib/roctave/window.rb +116 -0
- data/roctave.gemspec +79 -0
- data/samples/butter.rb +12 -0
- data/samples/cheby.rb +28 -0
- data/samples/dft.rb +18 -0
- data/samples/differentiator.rb +48 -0
- data/samples/differentiator_frequency_scaling.rb +52 -0
- data/samples/fft.rb +40 -0
- data/samples/finite_difference_coefficient.rb +53 -0
- data/samples/fir1.rb +13 -0
- data/samples/fir2.rb +14 -0
- data/samples/fir2_windows.rb +29 -0
- data/samples/fir2bank.rb +30 -0
- data/samples/fir_low_pass.rb +44 -0
- data/samples/firls.rb +77 -0
- data/samples/firpm.rb +78 -0
- data/samples/hilbert_transformer.rb +20 -0
- data/samples/hilbert_transformer_frequency_scaling.rb +47 -0
- data/samples/plot.rb +45 -0
- data/samples/stem.rb +8 -0
- data/samples/type1.rb +25 -0
- data/samples/type3.rb +24 -0
- data/samples/windows.rb +25 -0
- metadata +123 -0
data/lib/roctave/fir.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
## Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
|
2
|
+
##
|
3
|
+
## This file is part of Roctave
|
4
|
+
##
|
5
|
+
## Roctave is free software: you can redistribute it and/or modify
|
6
|
+
## it under the terms of the GNU General Public License as published by
|
7
|
+
## the Free Software Foundation, either version 3 of the License, or
|
8
|
+
## (at your option) any later version.
|
9
|
+
##
|
10
|
+
## Roctave is distributed in the hope that it will be useful,
|
11
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
## GNU General Public License for more details.
|
14
|
+
##
|
15
|
+
## You should have received a copy of the GNU General Public License
|
16
|
+
## along with Roctave. If not, see <https://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
|
19
|
+
module Roctave
|
20
|
+
class FirFilter
|
21
|
+
include Roctave::Filter
|
22
|
+
|
23
|
+
# @see Roctave.freqz
|
24
|
+
def freqz (*args)
|
25
|
+
Roctave.freqz(numerator, *args)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Roctave.stem
|
29
|
+
def stem (*args)
|
30
|
+
Roctave.stem(numerator, *args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @see Roctave.zplane
|
34
|
+
def zplane
|
35
|
+
Roctave.zplane(numerator)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Roctave::FirFilter]
|
39
|
+
def clone
|
40
|
+
Roctave::FirFilter.new numerator
|
41
|
+
end
|
42
|
+
|
43
|
+
# Matlab-like +fir1+ function to generate a FIR filter.
|
44
|
+
# @see Roctave.fir1
|
45
|
+
# @return [Roctave::FirFilter]
|
46
|
+
def self.fir1 (*args)
|
47
|
+
Roctave::FirFilter.new Roctave.fir1(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Matlab-like +fir2+ function to generate a FIR filter.
|
51
|
+
# @see Roctave.fir2
|
52
|
+
# @return [Roctave::FirFilter]
|
53
|
+
def self.fir2 (*args)
|
54
|
+
Roctave::FirFilter.new Roctave.fir2(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns a low-pass FIR filter.
|
58
|
+
# @see Roctave.fir_low_pass
|
59
|
+
# @return [Roctave::FirFilter]
|
60
|
+
def self.low_pass (*args)
|
61
|
+
Roctave::FirFilter.new Roctave.fir_low_pass(*args)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns a high-pass FIR filter.
|
65
|
+
# @see Roctave.fir_high_pass
|
66
|
+
# @return [Roctave::FirFilter]
|
67
|
+
def self.high_pass (*args)
|
68
|
+
Roctave::FirFilter.new Roctave.fir_high_pass(*args)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a band-pass FIR filter.
|
72
|
+
# @see Roctave.fir_band_pass
|
73
|
+
# @return [Roctave::FirFilter]
|
74
|
+
def self.band_pass (*args)
|
75
|
+
Roctave::FirFilter.new Roctave.fir_band_pass(*args)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns a band-stop FIR filter.
|
79
|
+
# @see Roctave.fir_band_stop
|
80
|
+
# @return [Roctave::FirFilter]
|
81
|
+
def self.band_stop (*args)
|
82
|
+
Roctave::FirFilter.new Roctave.fir_band_stop(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns a Hilbert transformer FIR filter.
|
86
|
+
# @see Roctave.fir_hilbert
|
87
|
+
# @return [Roctave::FirFilter]
|
88
|
+
def self.hilbert (*args)
|
89
|
+
Roctave::FirFilter.new Roctave.fir_hilbert(*args)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns a discriminator FIR filter.
|
93
|
+
# @see Roctave.fir_differentiator
|
94
|
+
# @return [Roctave::FirFilter]
|
95
|
+
def self.differentiator (*args)
|
96
|
+
Roctave::FirFilter.new Roctave.fir_differentiator(*args)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a kind of discriminator FIR filter.
|
100
|
+
# @see Roctave.finite_difference_coefficients
|
101
|
+
# @return [Roctave::FirFilter]
|
102
|
+
def self.finite_difference_coefficients (*args)
|
103
|
+
Roctave::FirFilter.new Roctave.finite_difference_coefficients(*args)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Least square method to generate a FIR filter.
|
107
|
+
# @see Roctave.firls
|
108
|
+
# @return [Roctave::FirFilter]
|
109
|
+
def self.firls (*args)
|
110
|
+
Roctave::FirFilter.new Roctave.firls(*args)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Least Parks-McClellan method to generate a FIR filter.
|
114
|
+
# @see Roctave.firpm
|
115
|
+
# @return [Roctave::FirFilter]
|
116
|
+
def self.firpm (*args)
|
117
|
+
Roctave::FirFilter.new Roctave.firpm(*args)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
data/lib/roctave/fir1.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
## Copyright (C) 2000 Paul Kienzle <pkienzle@users.sf.net> (GNU Octave implementation)
|
2
|
+
## Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
|
3
|
+
##
|
4
|
+
## This file is part of Roctave
|
5
|
+
##
|
6
|
+
## Roctave is free software: you can redistribute it and/or modify
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
9
|
+
## (at your option) any later version.
|
10
|
+
##
|
11
|
+
## Roctave is distributed in the hope that it will be useful,
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
## GNU General Public License for more details.
|
15
|
+
##
|
16
|
+
## You should have received a copy of the GNU General Public License
|
17
|
+
## along with Roctave. If not, see <https://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
|
20
|
+
module Roctave
|
21
|
+
# @!group FIR filters
|
22
|
+
|
23
|
+
def self.fir1 (n, w, *args, ramp: nil, window: :hamming)
|
24
|
+
raise ArgumentError.new "Expecting no more than 4 arguments" if args.length > 2
|
25
|
+
|
26
|
+
n = [1, n.to_i].max
|
27
|
+
|
28
|
+
if w.kind_of?(Range) then
|
29
|
+
w = [w.begin, w.end]
|
30
|
+
elsif w.kind_of?(Numeric) then
|
31
|
+
w = [w]
|
32
|
+
end
|
33
|
+
raise ArgumentError.new "W must be a scalar, an array or a range" unless w.kind_of?(Array)
|
34
|
+
w.collect! do |e|
|
35
|
+
if e.kind_of?(Range) then
|
36
|
+
[e.begin, e.end]
|
37
|
+
else
|
38
|
+
e
|
39
|
+
end
|
40
|
+
end
|
41
|
+
w = w.flatten
|
42
|
+
|
43
|
+
scale = 1
|
44
|
+
ftype = (w.length == 1) ? 1 : 0
|
45
|
+
|
46
|
+
args.each do |arg|
|
47
|
+
case arg
|
48
|
+
when Symbol
|
49
|
+
arg = arg.to_s.downcase.to_sym
|
50
|
+
when String
|
51
|
+
arg = arg.downcase.to_sym
|
52
|
+
else
|
53
|
+
raise ArgumentError.new "Not expecting a #{arg.class}"
|
54
|
+
end
|
55
|
+
|
56
|
+
case arg
|
57
|
+
when :low
|
58
|
+
ftype = 1
|
59
|
+
when :stop
|
60
|
+
ftype = 1
|
61
|
+
when :dc1
|
62
|
+
ftype = 1
|
63
|
+
when :high
|
64
|
+
ftype = 0
|
65
|
+
when :pass
|
66
|
+
ftype = 0
|
67
|
+
when :bandpass
|
68
|
+
ftype = 0
|
69
|
+
when :dc0
|
70
|
+
ftype = 0
|
71
|
+
when :scale
|
72
|
+
scale = 1
|
73
|
+
when :noscale
|
74
|
+
scale = 0
|
75
|
+
else
|
76
|
+
raise ArgumentError.new "Unexpected argument #{arg}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
## Build response function according to fir2 requirements
|
81
|
+
bands = w.length + 1
|
82
|
+
f = Roctave.zeros(2*bands)
|
83
|
+
f[0] = 0.0
|
84
|
+
f[-1] = 1.0
|
85
|
+
w.each.with_index do |e, i|
|
86
|
+
j = 1+2*i
|
87
|
+
break if j >= 2*bands - 1
|
88
|
+
f[j] = e
|
89
|
+
end
|
90
|
+
w.each.with_index do |e, i|
|
91
|
+
j = 2+2*i
|
92
|
+
break if j >= 2*bands - 1
|
93
|
+
f[j] = e
|
94
|
+
end
|
95
|
+
m = Roctave.zeros(2*bands)
|
96
|
+
r = (1..bands).collect{|e| (e - (1-ftype)) % 2}
|
97
|
+
r.each.with_index do |e, i|
|
98
|
+
j = 2*i
|
99
|
+
break if j >= 2*bands
|
100
|
+
m[j] = e
|
101
|
+
end
|
102
|
+
r = (0...2*bands).step(2).collect{|i| m[i]}
|
103
|
+
r.each.with_index do |e, i|
|
104
|
+
j = 1+2*i
|
105
|
+
break if j >= 2*bands
|
106
|
+
m[j] = e
|
107
|
+
end
|
108
|
+
|
109
|
+
if (n & 1) == 1 and m[-1].abs > 0.0 then
|
110
|
+
raise ArgumentError.new "Cannot do an odd order FIR filter with non-zero magnitude response at the nyquist frequency"
|
111
|
+
end
|
112
|
+
|
113
|
+
b = Roctave.fir2(n, f, m, ramp: ramp, window: window)
|
114
|
+
|
115
|
+
if scale == 1 then
|
116
|
+
if m[0] == 1 then
|
117
|
+
w_o = 0.0
|
118
|
+
elsif f[3] == 1 then
|
119
|
+
w_o = 1.0
|
120
|
+
else
|
121
|
+
w_o = f[2] + (f[3] - f[2]) / 2.0
|
122
|
+
end
|
123
|
+
renorm = 1.0 / b.collect.with_index{ |e, i|
|
124
|
+
j = b.length - 1 - i
|
125
|
+
e * (Complex.polar(1.0, -Math::PI*w_o)**j)
|
126
|
+
}.inject(&:+).abs
|
127
|
+
#puts "w_o: #{w_o}, renorm: #{renorm}"
|
128
|
+
b.collect!{|e| e * renorm}
|
129
|
+
end
|
130
|
+
|
131
|
+
b
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
data/lib/roctave/fir2.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
## Copyright (C) 2000 Paul Kienzle <pkienzle@users.sf.net> (implementation in octave)
|
2
|
+
## Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
|
3
|
+
##
|
4
|
+
## This file is part of Roctave
|
5
|
+
##
|
6
|
+
## Roctave is free software: you can redistribute it and/or modify
|
7
|
+
## it under the terms of the GNU General Public License as published by
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or
|
9
|
+
## (at your option) any later version.
|
10
|
+
##
|
11
|
+
## Roctave is distributed in the hope that it will be useful,
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
## GNU General Public License for more details.
|
15
|
+
##
|
16
|
+
## You should have received a copy of the GNU General Public License
|
17
|
+
## along with Roctave. If not, see <https://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
|
20
|
+
module Roctave
|
21
|
+
# @!group FIR filters
|
22
|
+
|
23
|
+
##
|
24
|
+
# Frequency sampling-based FIR filter design.
|
25
|
+
#
|
26
|
+
# Produce an order N FIR filter with arbitrary frequency response M
|
27
|
+
# over frequency bands F, returning the N+1 filter coefficients in B.
|
28
|
+
# The vector F specifies the frequency band edges of the filter
|
29
|
+
# response and M specifies the magnitude response at each frequency.
|
30
|
+
#
|
31
|
+
# The vector F must be nondecreasing over the range [0,1], and the
|
32
|
+
# first and last elements must be 0 and 1, respectively. A
|
33
|
+
# discontinuous jump in the frequency response can be specified by
|
34
|
+
# duplicating a band edge in F with different values in M.
|
35
|
+
#
|
36
|
+
# The resolution over which the frequency response is evaluated can
|
37
|
+
# be controlled with the GRID_N argument. The default is 512 or the
|
38
|
+
# next larger power of 2 greater than the filter length.
|
39
|
+
#
|
40
|
+
# The band transition width for discontinuities can be controlled
|
41
|
+
# with the RAMP_N argument. The default is GRID_N/25. Larger values
|
42
|
+
# will result in wider band transitions but better stopband
|
43
|
+
# rejection.
|
44
|
+
#
|
45
|
+
# An optional shaping WINDOW can be given as a vector with length
|
46
|
+
# N+1, or a symbol (ex: :blackman).
|
47
|
+
# If not specified, a Hamming window of length N+1 is used.
|
48
|
+
# @param n [Integer] The filter order
|
49
|
+
# @param f [Array<Float>] Frequency bands
|
50
|
+
# @param m [Array<Numeric>] Magnitude at frequency bands, can be complex numbers.
|
51
|
+
# @param type [nil, :odd_symmetry, :even_symmetry] Specify the symmetry of the filter. nil and :even_symmetry are the same and default, for type 1 and 2 filters, :odd_symmetry is for type 3 and 4 filters.
|
52
|
+
# @param grid [Integer] The length of the grid over which the frequency response is evaluated. Defaults to 512 or the next larger power of 2 greater than the filter length.
|
53
|
+
# @param ramp [Integer, Float] The band transition width for discontinuities, expressed in percentage relative to the niquist frequency.
|
54
|
+
# @param window [Array<Float>, Symbol] The window to use, either an array of length N+1, or a symbol. Defaults to :hamming
|
55
|
+
# @return [Array<Float>] The filter coefficients B
|
56
|
+
def self.fir2 (n, f, m, type = nil, grid: nil, ramp: nil, window: :hamming)
|
57
|
+
## Taken from Octave /usr/share/octave/packages/signal-1.3.2/fir2.m
|
58
|
+
f = f.map{|e| e}
|
59
|
+
m = m.map{|e| e}
|
60
|
+
n = [1, n.to_i].max
|
61
|
+
len = n + 1
|
62
|
+
|
63
|
+
raise ArgumentError.new "Argument 2 must be an array" unless f.kind_of?(Array)
|
64
|
+
raise ArgumentError.new "Argument 3 must be an array" unless m.kind_of?(Array)
|
65
|
+
case type
|
66
|
+
when nil
|
67
|
+
type = :even_symmetry
|
68
|
+
when :even_symmetry
|
69
|
+
when :odd_symmetry
|
70
|
+
else
|
71
|
+
raise ArgumentError.new "Argument 4 (type) must either be nil, :odd_symmetry or :even_symmetry" unless m.kind_of?(Array)
|
72
|
+
end
|
73
|
+
|
74
|
+
case window
|
75
|
+
when Array
|
76
|
+
raise ArgumentError.new "Window array must be of length #{len}" unless window.length == len
|
77
|
+
when Symbol
|
78
|
+
window = self.send(window, len)
|
79
|
+
when nil
|
80
|
+
else
|
81
|
+
raise ArgumentError.new "Window must either be an array of length N+1 or the symbol of a window"
|
82
|
+
end
|
83
|
+
|
84
|
+
if grid then
|
85
|
+
grid = [((n+1)/2.0).ceil.to_i, grid.to_i].max
|
86
|
+
else
|
87
|
+
grid = 512
|
88
|
+
grid = (2**Math.log2(len).ceil).to_i if grid < len
|
89
|
+
end
|
90
|
+
|
91
|
+
if ramp then
|
92
|
+
ramp = ramp.abs
|
93
|
+
else
|
94
|
+
ramp = 4
|
95
|
+
end
|
96
|
+
ramp = ramp.to_f / 200.0
|
97
|
+
|
98
|
+
if f.length < 2 or f[0] != 0.0 or f[-1] != 1.0 or (1 ... f.length).collect{|i| (f[i] - f[i-1]) < 0.0}.include?(true) then
|
99
|
+
raise ArgumentError.new "frequency must be nondecreasing starting from 0 and ending at 1"
|
100
|
+
elsif f.length != m.length then
|
101
|
+
raise ArgumentError.new "frequency and magnitude arrays must be the same length"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
if type == :odd_symmetry and m.find{|v| v.is_a?(Complex)}.nil? then
|
106
|
+
m.collect!{|v| Complex(0, -v)}
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
## Apply ramps to discontinuities
|
111
|
+
if ramp > 0 then
|
112
|
+
## Remember the original frequency points prior to applying ramp
|
113
|
+
basef = f.collect{|e| e}
|
114
|
+
basem = m.collect{|e| e}
|
115
|
+
|
116
|
+
## Separate identical frequencies, but keep the midpoints
|
117
|
+
idx = (1 ... f.length).collect{|i| ((f[i] - f[i-1]) == 0.0) ? i-1 : nil}.reject(&:nil?)
|
118
|
+
idx.each do |i|
|
119
|
+
f[i] -= ramp
|
120
|
+
f[i+1] += ramp
|
121
|
+
end
|
122
|
+
## Make sure the frequency points stay monotonic in [0, 1]
|
123
|
+
f = (f + basef).collect{|e| [1.0, [0.0, e].max].min}.uniq.sort
|
124
|
+
|
125
|
+
## Preserve window shape even though f may have changed
|
126
|
+
m = f.collect.with_index do |framped, i|
|
127
|
+
leftmost = nil
|
128
|
+
leftmost_index = nil
|
129
|
+
basef.each.with_index do |e, j|
|
130
|
+
if (e < framped) or (e == framped and leftmost != framped) then
|
131
|
+
leftmost = e
|
132
|
+
leftmost_index = j
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
rightmost = nil
|
137
|
+
rightmost_index = nil
|
138
|
+
basef.reverse.each.with_index do |e, j|
|
139
|
+
if (e > framped) or (e == framped and rightmost != framped) then
|
140
|
+
rightmost = e
|
141
|
+
rightmost_index = basef.length - 1 - j
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if leftmost_index > rightmost_index then
|
146
|
+
leftmost,rightmost = rightmost,leftmost
|
147
|
+
leftmost_index,rightmost_index = rightmost_index,leftmost_index
|
148
|
+
end
|
149
|
+
|
150
|
+
#puts "[#{i}] #{leftmost_index} - #{rightmost_index}"
|
151
|
+
|
152
|
+
ml = basem[leftmost_index]
|
153
|
+
mr = basem[rightmost_index]
|
154
|
+
|
155
|
+
if (rightmost - leftmost) == 0 then
|
156
|
+
(ml + mr) / 2.0
|
157
|
+
else
|
158
|
+
ml*(rightmost - framped)/(rightmost - leftmost) + mr*(framped - leftmost)/(rightmost - leftmost)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
#Roctave.plot(basef, basem, '-or;original;', f, m, '-*b;ramped;', xlim: (-0.1 .. 1.1), ylim: (-0.1 .. 1.1), grid: true, title: 'Ramp')
|
162
|
+
end
|
163
|
+
|
164
|
+
## Interpolate between grid points
|
165
|
+
fgrid = Roctave.linspace(0.0, 1.0, grid + 1)
|
166
|
+
mgrid = fgrid.collect.with_index do |fg, i|
|
167
|
+
leftmost = nil
|
168
|
+
leftmost_index = nil
|
169
|
+
f.each.with_index do |e, j|
|
170
|
+
if (e < fg) or (e == fg and leftmost != e) then
|
171
|
+
leftmost = e
|
172
|
+
leftmost_index = j
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
rightmost = nil
|
177
|
+
rightmost_index = nil
|
178
|
+
f.reverse.each.with_index do |e, j|
|
179
|
+
if (e > fg) or (e == fg and rightmost != e) then
|
180
|
+
rightmost = e
|
181
|
+
rightmost_index = f.length - 1 - j
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if leftmost_index > rightmost_index then
|
186
|
+
leftmost,rightmost = rightmost,leftmost
|
187
|
+
leftmost_index,rightmost_index = rightmost_index,leftmost_index
|
188
|
+
end
|
189
|
+
|
190
|
+
ml = m[leftmost_index]
|
191
|
+
mr = m[rightmost_index]
|
192
|
+
|
193
|
+
if (rightmost - leftmost) == 0 then
|
194
|
+
(ml + mr) / 2.0
|
195
|
+
else
|
196
|
+
ml*(rightmost - fg)/(rightmost - leftmost) + mr*(fg - leftmost)/(rightmost - leftmost)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
## Transform frequency response into time response and
|
202
|
+
## center the response about n/2, truncating the excess
|
203
|
+
if type == :even_symmetry then
|
204
|
+
if (n & 1) == 0 then # Type I
|
205
|
+
#puts 'Type I'
|
206
|
+
b = mgrid + mgrid[1...grid].reverse
|
207
|
+
b = idft(b)
|
208
|
+
mid = (n+1) / 2.0
|
209
|
+
b = (b[-(mid.floor) .. -1] + b[0 ... mid.ceil]).collect{|e| e.real}
|
210
|
+
else # Type II
|
211
|
+
#puts 'Type II'
|
212
|
+
## Add zeros to interpolate by 2, then pick the odd values below
|
213
|
+
b = mgrid + Array.new(grid*2){0.0} + mgrid[1 ... grid].reverse
|
214
|
+
b = idft(b)
|
215
|
+
b = (((b.length - n) ... b.length).step(2).to_a + (1 .. n).step(2).to_a).collect{|i| b[i].real * 2.0}
|
216
|
+
end
|
217
|
+
else
|
218
|
+
if (n & 1) == 0 then # Type III
|
219
|
+
#puts 'Type III'
|
220
|
+
b = mgrid + mgrid[1...grid].conj.reverse
|
221
|
+
####
|
222
|
+
#br = b.collect{|v| v.real}
|
223
|
+
#bi = b.collect{|v| v.imag}
|
224
|
+
#t = (-b.length/2...b.length/2).to_a
|
225
|
+
#plot(t, br, ';Real;', t, bi, ';Imaginary;')
|
226
|
+
####
|
227
|
+
b = idft(b)
|
228
|
+
mid = (n+1) / 2.0
|
229
|
+
b = (b[-(mid.floor) .. -1] + b[0 ... mid.ceil]).collect{|e| e.real}
|
230
|
+
else # Type IV
|
231
|
+
#puts 'Type IV'
|
232
|
+
## Add zeros to interpolate by 2, then pick the odd values below
|
233
|
+
b = mgrid + Array.new(grid*2){0.0} + mgrid.conj[1 ... grid].reverse
|
234
|
+
b = idft(b)
|
235
|
+
b = (((b.length - n) ... b.length).step(2).to_a + (1 .. n).step(2).to_a).collect{|i| b[i].real * 2.0}
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
b.collect!.with_index{|e, i| e*window[i]} if window
|
240
|
+
|
241
|
+
#Roctave.plot(f, m, '-or;original;', fgrid, mgrid, '-xb;grid;', Roctave.linspace(0, 1, 1024), Roctave.dft(b, 2048).abs[0...1024], 'g;response;', xlim: (-0.1 .. 1.1), ylim: (-0.1 .. 1.1), grid: true, title: 'Grid')
|
242
|
+
|
243
|
+
b
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|