1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Copyright ©2020 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package window
import "math"
// Gaussian can modify a sequence using 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 Gaussian 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 Gaussian) Transform(seq []float64) []float64 {
a := float64(len(seq)-1) / 2
for i := range seq {
x := -0.5 * math.Pow((float64(i)-a)/(g.Sigma*a), 2)
seq[i] *= math.Exp(x)
}
return seq
}
// TransformComplex applies the Gaussian transformation to seq in place,
// using the value of the receiver as the sigma parameter, and returning
// the result.
func (g Gaussian) TransformComplex(seq []complex128) []complex128 {
a := float64(len(seq)-1) / 2
for i, v := range seq {
x := -0.5 * math.Pow((float64(i)-a)/(g.Sigma*a), 2)
w := math.Exp(x)
seq[i] = complex(w*real(v), w*imag(v))
}
return seq
}
// Tukey 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 Tukey 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 Tukey) Transform(seq []float64) []float64 {
switch {
case t.Alpha <= 0:
return Rectangular(seq)
case t.Alpha >= 1:
return Hann(seq)
default:
alphaL := t.Alpha * float64(len(seq)-1)
width := int(0.5*alphaL) + 1
for i := range seq[:width] {
w := 0.5 * (1 - math.Cos(2*math.Pi*float64(i)/alphaL))
seq[i] *= w
seq[len(seq)-1-i] *= w
}
return seq
}
}
// TransformComplex applies the Tukey transformation to seq in place, using
// the value of the receiver as the Alpha parameter, and returning the result.
func (t Tukey) TransformComplex(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, v := range seq[:width] {
w := 0.5 * (1 - math.Cos(2*math.Pi*float64(i)/alphaL))
v = complex(w*real(v), w*imag(v))
seq[i] = v
seq[len(seq)-1-i] = v
}
return seq
}
}
// Values is an arbitrary real window function.
type Values []float64
// NewValues returns a Values of length n with weights corresponding to the
// provided window function.
func NewValues(window func([]float64) []float64, n int) Values {
v := make(Values, 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 Values) Transform(seq []float64) []float64 {
if v == nil {
return seq
}
if len(v) != len(seq) {
panic("window: length mismatch")
}
for i, w := range v {
seq[i] *= w
}
return seq
}
// TransformTo applies the weights in the receiver to src placing the result
// in dst. If v is nil, TransformTo is a no-op, otherwise the length of v must
// match the length of src and dst.
func (v Values) TransformTo(dst, src []float64) {
if v == nil {
return
}
if len(v) != len(src) {
panic("window: seq length mismatch")
}
if len(v) != len(dst) {
panic("window: dst length mismatch")
}
for i, w := range v {
dst[i] = w * src[i]
}
}
// TransformComplex applies the weights in the receiver to seq in place,
// returning the result. If v is nil, TransformComplex is a no-op, otherwise
// the length of v must match the length of seq.
func (v Values) TransformComplex(seq []complex128) []complex128 {
if v == nil {
return seq
}
if len(v) != len(seq) {
panic("window: length mismatch")
}
for i, w := range v {
sv := seq[i]
seq[i] = complex(w*real(sv), w*imag(sv))
}
return seq
}
// TransformComplexTo applies the weights in the receiver to src placing the
// result in dst. If v is nil, TransformComplexTo is a no-op, otherwise the
// length of v must match the length of src and dst.
func (v Values) TransformComplexTo(dst, src []complex128) {
if v == nil {
return
}
if len(v) != len(src) {
panic("window: seq length mismatch")
}
if len(v) != len(dst) {
panic("window: dst length mismatch")
}
for i, w := range v {
sv := src[i]
dst[i] = complex(w*real(sv), w*imag(sv))
}
}
|