mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 15:16:59 +08:00

Changes made in dsp/fourier/internal/fftpack break the formatting used there, so these are reverted. There will be complaints in CI. [git-generate] gofmt -w . go generate gonum.org/v1/gonum/blas go generate gonum.org/v1/gonum/blas/gonum go generate gonum.org/v1/gonum/unit go generate gonum.org/v1/gonum/unit/constant go generate gonum.org/v1/gonum/graph/formats/dot go generate gonum.org/v1/gonum/graph/formats/rdf go generate gonum.org/v1/gonum/stat/card git checkout -- dsp/fourier/internal/fftpack
211 lines
5.9 KiB
Go
211 lines
5.9 KiB
Go
// 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))
|
||
}
|
||
}
|