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