github.com/gopherd/gonum@v0.0.4/dsp/window/window_parametric.go (about)

     1  // Copyright ©2020 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package window
     6  
     7  import "math"
     8  
     9  // Gaussian can modify a sequence using the Gaussian window and return the
    10  // result.
    11  // See https://en.wikipedia.org/wiki/Window_function#Gaussian_window
    12  // and https://www.recordingblogs.com/wiki/gaussian-window for details.
    13  //
    14  // The Gaussian window is an adjustable window.
    15  //
    16  // The sequence weights are
    17  //  w[k] = exp(-0.5 * ((k-M)/(σ*M))² ), M = (N-1)/2,
    18  // for k=0,1,...,N-1 where N is the length of the window.
    19  //
    20  // The properties of the window depend on the value of σ (sigma).
    21  // It can be used as high or low resolution window, depending of the σ value.
    22  //
    23  // Spectral leakage parameters are summarized in the table:
    24  //         |  σ=0.3  |  σ=0.5 |  σ=1.2 |
    25  //  -------|---------------------------|
    26  //  ΔF_0   |   8     |   3.4  |   2.2  |
    27  //  ΔF_0.5 |   1.82  |   1.2  |   0.94 |
    28  //  K      |   4     |   1.7  |   1.1  |
    29  //  ɣ_max  | -65     | -31.5  | -15.5  |
    30  //  β      |  -8.52  |  -4.48 |  -0.96 |
    31  type Gaussian struct {
    32  	Sigma float64
    33  }
    34  
    35  // Transform applies the Gaussian transformation to seq in place, using the
    36  // value of the receiver as the sigma parameter, and returning the result.
    37  func (g Gaussian) Transform(seq []float64) []float64 {
    38  	a := float64(len(seq)-1) / 2
    39  	for i := range seq {
    40  		x := -0.5 * math.Pow((float64(i)-a)/(g.Sigma*a), 2)
    41  		seq[i] *= math.Exp(x)
    42  	}
    43  	return seq
    44  }
    45  
    46  // TransformComplex applies the Gaussian transformation to seq in place,
    47  // using the value of the receiver as the sigma parameter, and returning
    48  // the result.
    49  func (g Gaussian) TransformComplex(seq []complex128) []complex128 {
    50  	a := float64(len(seq)-1) / 2
    51  	for i, v := range seq {
    52  		x := -0.5 * math.Pow((float64(i)-a)/(g.Sigma*a), 2)
    53  		w := math.Exp(x)
    54  		seq[i] = complex(w*real(v), w*imag(v))
    55  	}
    56  	return seq
    57  }
    58  
    59  // Tukey can modify a sequence using the Tukey window and return the result.
    60  // See https://en.wikipedia.org/wiki/Window_function#Tukey_window
    61  // and https://www.recordingblogs.com/wiki/tukey-window for details.
    62  //
    63  // The Tukey window is an adjustable window.
    64  //
    65  // The sequence weights are
    66  //  w[k] = 0.5 * (1 + cos(π*(|k - M| - αM)/((1-α) * M))), |k - M| ≥ αM
    67  //       = 1, |k - M| < αM
    68  // with M = (N - 1)/2 for k=0,1,...,N-1 where N is the length of the window.
    69  //
    70  // Spectral leakage parameters are summarized in the table:
    71  //         |  α=0.3 |  α=0.5 |  α=0.7 |
    72  //  -------|--------------------------|
    73  //  ΔF_0   |   1.33 |   1.22 |   1.13 |
    74  //  ΔF_0.5 |   1.28 |   1.16 |   1.04 |
    75  //  K      |   0.67 |   0.61 |   0.57 |
    76  //  ɣ_max  | -18.2  | -15.1  | -13.8  |
    77  //  β      |  -1.41 |  -2.50 |  -3.74 |
    78  type Tukey struct {
    79  	Alpha float64
    80  }
    81  
    82  // Transform applies the Tukey transformation to seq in place, using the
    83  // value of the receiver as the Alpha parameter, and returning the result.
    84  func (t Tukey) Transform(seq []float64) []float64 {
    85  	switch {
    86  	case t.Alpha <= 0:
    87  		return Rectangular(seq)
    88  	case t.Alpha >= 1:
    89  		return Hann(seq)
    90  	default:
    91  		alphaL := t.Alpha * float64(len(seq)-1)
    92  		width := int(0.5*alphaL) + 1
    93  		for i := range seq[:width] {
    94  			w := 0.5 * (1 - math.Cos(2*math.Pi*float64(i)/alphaL))
    95  			seq[i] *= w
    96  			seq[len(seq)-1-i] *= w
    97  		}
    98  		return seq
    99  	}
   100  }
   101  
   102  // TransformComplex applies the Tukey transformation to seq in place, using
   103  // the value of the receiver as the Alpha parameter, and returning the result.
   104  func (t Tukey) TransformComplex(seq []complex128) []complex128 {
   105  	switch {
   106  	case t.Alpha <= 0:
   107  		return RectangularComplex(seq)
   108  	case t.Alpha >= 1:
   109  		return HannComplex(seq)
   110  	default:
   111  		alphaL := t.Alpha * float64(len(seq)-1)
   112  		width := int(0.5*alphaL) + 1
   113  		for i, v := range seq[:width] {
   114  			w := 0.5 * (1 - math.Cos(2*math.Pi*float64(i)/alphaL))
   115  			v = complex(w*real(v), w*imag(v))
   116  			seq[i] = v
   117  			seq[len(seq)-1-i] = v
   118  		}
   119  		return seq
   120  	}
   121  }
   122  
   123  // Values is an arbitrary real window function.
   124  type Values []float64
   125  
   126  // NewValues returns a Values of length n with weights corresponding to the
   127  // provided window function.
   128  func NewValues(window func([]float64) []float64, n int) Values {
   129  	v := make(Values, n)
   130  	for i := range v {
   131  		v[i] = 1
   132  	}
   133  	return window(v)
   134  }
   135  
   136  // Transform applies the weights in the receiver to seq in place, returning the
   137  // result. If v is nil, Transform is a no-op, otherwise the length of v must
   138  // match the length of seq.
   139  func (v Values) Transform(seq []float64) []float64 {
   140  	if v == nil {
   141  		return seq
   142  	}
   143  	if len(v) != len(seq) {
   144  		panic("window: length mismatch")
   145  	}
   146  	for i, w := range v {
   147  		seq[i] *= w
   148  	}
   149  	return seq
   150  }
   151  
   152  // TransformTo applies the weights in the receiver to src placing the result
   153  // in dst. If v is nil, TransformTo is a no-op, otherwise the length of v must
   154  // match the length of src and dst.
   155  func (v Values) TransformTo(dst, src []float64) {
   156  	if v == nil {
   157  		return
   158  	}
   159  	if len(v) != len(src) {
   160  		panic("window: seq length mismatch")
   161  	}
   162  	if len(v) != len(dst) {
   163  		panic("window: dst length mismatch")
   164  	}
   165  	for i, w := range v {
   166  		dst[i] = w * src[i]
   167  	}
   168  }
   169  
   170  // TransformComplex applies the weights in the receiver to seq in place,
   171  // returning the result. If v is nil, TransformComplex is a no-op, otherwise
   172  // the length of v must match the length of seq.
   173  func (v Values) TransformComplex(seq []complex128) []complex128 {
   174  	if v == nil {
   175  		return seq
   176  	}
   177  	if len(v) != len(seq) {
   178  		panic("window: length mismatch")
   179  	}
   180  	for i, w := range v {
   181  		sv := seq[i]
   182  		seq[i] = complex(w*real(sv), w*imag(sv))
   183  	}
   184  	return seq
   185  }
   186  
   187  // TransformComplexTo applies the weights in the receiver to src placing the
   188  // result in dst. If v is nil, TransformComplexTo is a no-op, otherwise the
   189  // length of v must match the length of src and dst.
   190  func (v Values) TransformComplexTo(dst, src []complex128) {
   191  	if v == nil {
   192  		return
   193  	}
   194  	if len(v) != len(src) {
   195  		panic("window: seq length mismatch")
   196  	}
   197  	if len(v) != len(dst) {
   198  		panic("window: dst length mismatch")
   199  	}
   200  	for i, w := range v {
   201  		sv := src[i]
   202  		dst[i] = complex(w*real(sv), w*imag(sv))
   203  	}
   204  }