dsp/window: refactor Gaussian and Tukey windows to be each a single type

This commit is contained in:
Dan Kortschak
2020-09-16 06:01:44 +09:30
parent 880b710e3c
commit 8c25d69b14
4 changed files with 181 additions and 222 deletions

View File

@@ -293,111 +293,3 @@ func FlatTopComplex(seq []complex128) []complex128 {
}
return seq
}
// GaussianComplex can modify a sequence by the Gaussian window and return the result.
// See https://en.wikipedia.org/wiki/Window_function#Gaussian_window
// and https://www.recordingblogs.com/wiki/gaussian-window for details.
//
// The Gaussian window is an adjustable window.
//
// The sequence weights are
// w[k] = exp(-0.5 * ((k-M)/(σ*M))² ), M = (N-1)/2,
// for k=0,1,...,N-1 where N is the length of the window.
//
// The properties of the window depend on the value of σ (sigma).
// It can be used as high or low resolution window, depending of the σ value.
//
// Spectral leakage parameters are summarized in the table:
// | σ=0.3 | σ=0.5 | σ=1.2 |
// -------|---------------------------|
// ΔF_0 | 8 | 3.4 | 2.2 |
// ΔF_0.5 | 1.82 | 1.2 | 0.94 |
// K | 4 | 1.7 | 1.1 |
// ɣ_max | -65 | -31.5 | -15.5 |
// β | -8.52 | -4.48 | -0.96 |
type GaussianComplex struct {
Sigma float64
}
// Transform applies the Gaussian transformation to seq in place, using the value
// of the receiver as the sigma parameter, and returning the result.
func (g GaussianComplex) Transform(seq []complex128) []complex128 {
a := float64(len(seq)-1) / 2
for i := range seq {
x := -0.5 * math.Pow((float64(i)-a)/(g.Sigma*a), 2)
seq[i] *= complex(math.Exp(x), 0)
}
return seq
}
// TukeyComplex can modify a sequence using the Tukey window and return the result.
// See https://en.wikipedia.org/wiki/Window_function#Tukey_window
// and https://www.recordingblogs.com/wiki/tukey-window for details.
//
// The Tukey window is an adjustable window.
//
// The sequence weights are
// w[k] = 0.5 * (1 + cos(π*(|k - M| - αM)/((1-α) * M))), |k - M| ≥ αM
// = 1, |k - M| < αM
// with M = (N - 1)/2 for k=0,1,...,N-1 where N is the length of the window.
//
// Spectral leakage parameters are summarized in the table:
// | α=0.3 | α=0.5 | α=0.7 |
// -------|--------------------------|
// ΔF_0 | 1.33 | 1.22 | 1.13 |
// ΔF_0.5 | 1.28 | 1.16 | 1.04 |
// K | 0.67 | 0.61 | 0.57 |
// ɣ_max | -18.2 | -15.1 | -13.8 |
// β | -1.41 | -2.50 | -3.74 |
type TukeyComplex struct {
Alpha float64
}
// Transform applies the Tukey transformation to seq in place, using the value
// of the receiver as the Alpha parameter, and returning the result
func (t TukeyComplex) Transform(seq []complex128) []complex128 {
switch {
case t.Alpha <= 0:
return RectangularComplex(seq)
case t.Alpha >= 1:
return HannComplex(seq)
default:
alphaL := t.Alpha * float64(len(seq)-1)
width := int(0.5*alphaL) + 1
for i := range seq[:width] {
w := complex(0.5*(1-math.Cos(2*math.Pi*float64(i)/alphaL)), 0)
seq[i] *= w
seq[len(seq)-1-i] *= w
}
return seq
}
}
// ValuesComplex is an arbitrary complex window function.
type ValuesComplex []complex128
// NewValuesComplex returns a ValuesComplex of length n with weights corresponding
// to the provided window function.
func NewValuesComplex(window func([]complex128) []complex128, n int) ValuesComplex {
v := make(ValuesComplex, n)
for i := range v {
v[i] = 1
}
return window(v)
}
// Transform applies the weights in the receiver to seq in place, returning the
// result. If v is nil, Transform is a no-op, otherwise the length of v must
// match the length of seq.
func (v ValuesComplex) Transform(seq []complex128) []complex128 {
if v == nil {
return seq
}
if len(v) != len(seq) {
panic("window: length mismatch")
}
for i, w := range v {
seq[i] *= w
}
return seq
}