gonum.org/v1/gonum@v0.14.0/dsp/fourier/fourier.go (about)

     1  // Copyright ©2018 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 fourier
     6  
     7  import "gonum.org/v1/gonum/dsp/fourier/internal/fftpack"
     8  
     9  // FFT implements Fast Fourier Transform and its inverse for real sequences.
    10  type FFT struct {
    11  	work []float64
    12  	ifac [15]int
    13  
    14  	// real temporarily store complex data as
    15  	// pairs of real values to allow passing to
    16  	// the backing code. The length of real
    17  	// must always be half the length of work.
    18  	real []float64
    19  }
    20  
    21  // NewFFT returns an FFT initialized for work on sequences of length n.
    22  func NewFFT(n int) *FFT {
    23  	var t FFT
    24  	t.Reset(n)
    25  	return &t
    26  }
    27  
    28  // Len returns the length of the acceptable input.
    29  func (t *FFT) Len() int { return len(t.real) }
    30  
    31  // Reset reinitializes the FFT for work on sequences of length n.
    32  func (t *FFT) Reset(n int) {
    33  	if 2*n <= cap(t.work) {
    34  		t.work = t.work[:2*n]
    35  		t.real = t.real[:n]
    36  	} else {
    37  		t.work = make([]float64, 2*n)
    38  		t.real = make([]float64, n)
    39  	}
    40  	fftpack.Rffti(n, t.work, t.ifac[:])
    41  }
    42  
    43  // Coefficients computes the Fourier coefficients of the input sequence,
    44  // converting the time series in seq into the frequency spectrum, placing
    45  // the result in dst and returning it. This transform is unnormalized; a
    46  // call to Coefficients followed by a call of Sequence will multiply the
    47  // input sequence by the length of the sequence.
    48  //
    49  // If the length of seq is not t.Len(), Coefficients will panic.
    50  // If dst is nil, a new slice is allocated and returned. If dst is not nil and
    51  // the length of dst does not equal t.Len()/2+1, Coefficients will panic.
    52  func (t *FFT) Coefficients(dst []complex128, seq []float64) []complex128 {
    53  	if len(seq) != t.Len() {
    54  		panic("fourier: sequence length mismatch")
    55  	}
    56  	if dst == nil {
    57  		dst = make([]complex128, t.Len()/2+1)
    58  	} else if len(dst) != t.Len()/2+1 {
    59  		panic("fourier: destination length mismatch")
    60  	}
    61  	copy(t.real, seq)
    62  	fftpack.Rfftf(len(t.real), t.real, t.work, t.ifac[:])
    63  	dst[0] = complex(t.real[0], 0)
    64  	if len(seq) < 2 {
    65  		return dst
    66  	}
    67  	if len(seq)%2 == 1 {
    68  		dst[len(dst)-1] = complex(t.real[len(t.real)-2], t.real[len(t.real)-1])
    69  	} else {
    70  		dst[len(dst)-1] = complex(t.real[len(t.real)-1], 0)
    71  	}
    72  	for i := 1; i < len(dst)-1; i++ {
    73  		dst[i] = complex(t.real[2*i-1], t.real[2*i])
    74  	}
    75  	return dst
    76  }
    77  
    78  // Sequence computes the real periodic sequence from the Fourier coefficients,
    79  // converting the frequency spectrum in coeff into a time series, placing the
    80  // result in dst and returning it. This transform is unnormalized; a call to
    81  // Coefficients followed by a call of Sequence will multiply the input sequence
    82  // by the length of the sequence.
    83  //
    84  // If the length of coeff is not t.Len()/2+1, Sequence will panic.
    85  // If dst is nil, a new slice is allocated and returned. If dst is not nil and
    86  // the length of dst does not equal the length of coeff, Sequence will panic.
    87  func (t *FFT) Sequence(dst []float64, coeff []complex128) []float64 {
    88  	if len(coeff) != t.Len()/2+1 {
    89  		panic("fourier: coefficients length mismatch")
    90  	}
    91  	if dst == nil {
    92  		dst = make([]float64, t.Len())
    93  	} else if len(dst) != t.Len() {
    94  		panic("fourier: destination length mismatch")
    95  	}
    96  	dst[0] = real(coeff[0])
    97  	if len(dst) < 2 {
    98  		return dst
    99  	}
   100  	nf := coeff[len(coeff)-1]
   101  	if len(dst)%2 == 1 {
   102  		dst[len(dst)-2] = real(nf)
   103  		dst[len(dst)-1] = imag(nf)
   104  	} else {
   105  		dst[len(dst)-1] = real(nf)
   106  	}
   107  
   108  	for i, cv := range coeff[1 : len(coeff)-1] {
   109  		dst[2*i+1] = real(cv)
   110  		dst[2*i+2] = imag(cv)
   111  	}
   112  	fftpack.Rfftb(len(dst), dst, t.work, t.ifac[:])
   113  	return dst
   114  }
   115  
   116  // Freq returns the relative frequency center for coefficient i.
   117  // Freq will panic if i is negative or greater than or equal to t.Len().
   118  func (t *FFT) Freq(i int) float64 {
   119  	if i < 0 || t.Len() <= i {
   120  		panic("fourier: index out of range")
   121  	}
   122  	step := 1 / float64(t.Len())
   123  	return step * float64(i)
   124  }
   125  
   126  // CmplxFFT implements Fast Fourier Transform and its inverse for complex sequences.
   127  type CmplxFFT struct {
   128  	work []float64
   129  	ifac [15]int
   130  
   131  	// real temporarily store complex data as
   132  	// pairs of real values to allow passing to
   133  	// the backing code. The length of real
   134  	// must always be half the length of work.
   135  	real []float64
   136  }
   137  
   138  // NewCmplxFFT returns an CmplxFFT initialized for work on sequences of length n.
   139  func NewCmplxFFT(n int) *CmplxFFT {
   140  	var t CmplxFFT
   141  	t.Reset(n)
   142  	return &t
   143  }
   144  
   145  // Len returns the length of the acceptable input.
   146  func (t *CmplxFFT) Len() int { return len(t.work) / 4 }
   147  
   148  // Reset reinitializes the FFT for work on sequences of length n.
   149  func (t *CmplxFFT) Reset(n int) {
   150  	if 4*n <= cap(t.work) {
   151  		t.work = t.work[:4*n]
   152  		t.real = t.real[:2*n]
   153  	} else {
   154  		t.work = make([]float64, 4*n)
   155  		t.real = make([]float64, 2*n)
   156  	}
   157  	fftpack.Cffti(n, t.work, t.ifac[:])
   158  }
   159  
   160  // Coefficients computes the Fourier coefficients of a complex input sequence,
   161  // converting the time series in seq into the frequency spectrum, placing
   162  // the result in dst and returning it. This transform is unnormalized; a call
   163  // to Coefficients followed by a call of Sequence will multiply the input
   164  // sequence by the length of the sequence.
   165  //
   166  // If the length of seq is not t.Len(), Coefficients will panic.
   167  // If dst is nil, a new slice is allocated and returned. If dst is not nil and
   168  // the length of dst does not equal the length of seq, Coefficients will panic.
   169  // It is safe to use the same slice for dst and seq.
   170  func (t *CmplxFFT) Coefficients(dst, seq []complex128) []complex128 {
   171  	if len(seq) != t.Len() {
   172  		panic("fourier: sequence length mismatch")
   173  	}
   174  	if dst == nil {
   175  		dst = make([]complex128, len(seq))
   176  	} else if len(dst) != len(seq) {
   177  		panic("fourier: destination length mismatch")
   178  	}
   179  	for i, cv := range seq {
   180  		t.real[2*i] = real(cv)
   181  		t.real[2*i+1] = imag(cv)
   182  	}
   183  	fftpack.Cfftf(len(dst), t.real, t.work, t.ifac[:])
   184  	for i := range dst {
   185  		dst[i] = complex(t.real[2*i], t.real[2*i+1])
   186  	}
   187  	return dst
   188  }
   189  
   190  // Sequence computes the complex periodic sequence from the Fourier coefficients,
   191  // converting the frequency spectrum in coeff into a time series, placing the
   192  // result in dst and returning it. This transform is unnormalized; a call to
   193  // Coefficients followed by a call of Sequence will multiply the input sequence
   194  // by the length of the sequence.
   195  //
   196  // If the length of coeff is not t.Len(), Sequence will panic.
   197  // If dst is nil, a new slice is allocated and returned. If dst is not nil and
   198  // the length of dst does not equal the length of coeff, Sequence will panic.
   199  // It is safe to use the same slice for dst and coeff.
   200  func (t *CmplxFFT) Sequence(dst, coeff []complex128) []complex128 {
   201  	if len(coeff) != t.Len() {
   202  		panic("fourier: coefficients length mismatch")
   203  	}
   204  	if dst == nil {
   205  		dst = make([]complex128, len(coeff))
   206  	} else if len(dst) != len(coeff) {
   207  		panic("fourier: destination length mismatch")
   208  	}
   209  	for i, cv := range coeff {
   210  		t.real[2*i] = real(cv)
   211  		t.real[2*i+1] = imag(cv)
   212  	}
   213  	fftpack.Cfftb(len(dst), t.real, t.work, t.ifac[:])
   214  	for i := range dst {
   215  		dst[i] = complex(t.real[2*i], t.real[2*i+1])
   216  	}
   217  	return dst
   218  }
   219  
   220  // Freq returns the relative frequency center for coefficient i.
   221  // Freq will panic if i is negative or greater than or equal to t.Len().
   222  func (t *CmplxFFT) Freq(i int) float64 {
   223  	if i < 0 || t.Len() <= i {
   224  		panic("fourier: index out of range")
   225  	}
   226  	step := 1 / float64(t.Len())
   227  	if i < (t.Len()-1)/2+1 {
   228  		return step * float64(i)
   229  	}
   230  	return step * float64(i-t.Len())
   231  }
   232  
   233  // ShiftIdx returns a shifted index into a slice of coefficients
   234  // returned by the CmplxFFT so that indexing into the coefficients
   235  // places the zero frequency component at the center of the spectrum.
   236  // ShiftIdx will panic if i is negative or greater than or equal to
   237  // t.Len().
   238  func (t *CmplxFFT) ShiftIdx(i int) int {
   239  	if i < 0 || t.Len() <= i {
   240  		panic("fourier: index out of range")
   241  	}
   242  	h := t.Len() / 2
   243  	if i < h {
   244  		return i + (t.Len()+1)/2
   245  	}
   246  	return i - h
   247  }
   248  
   249  // UnshiftIdx returns inverse of ShiftIdx. UnshiftIdx will panic if i is
   250  // negative or greater than or equal to t.Len().
   251  func (t *CmplxFFT) UnshiftIdx(i int) int {
   252  	if i < 0 || t.Len() <= i {
   253  		panic("fourier: index out of range")
   254  	}
   255  	h := (t.Len() + 1) / 2
   256  	if i < h {
   257  		return i + t.Len()/2
   258  	}
   259  	return i - h
   260  }