SciDart

Fourier transform

FFT, inverse FFT, real FFT, frequency bins and spectra in dB.

Like scipy.fft

Function Does NumPy / SciPy
fft complex FFT numpy.fft.fft
ifft inverse complex FFT numpy.fft.ifft
rfft FFT of a real signal, positive frequencies only numpy.fft.rfft
rifft inverse of rfft numpy.fft.irfft
fftFreq frequency of each bin numpy.fft.fftfreq
dbfft spectrum in dB with a window

Lengths that are a power of two use the Cooley-Tukey algorithm. Other lengths use a direct DFT, which is much slower for long signals.

Spectrum of a signal

Spectrum of a signal with the FFTSample two sine waves, take the real FFT and read the strongest frequencies.
import 'dart:math' as math;

import 'package:scidart/numdart.dart';
import 'package:scidart/scidart.dart';

void main() {
  var fs = 64.0; // sampling rate in Hz
  var t = linspace(0, 1, num: 64, endpoint: false);
  var signal = arraySin(arrayMultiplyToScalar(t, 2 * math.pi * 5)) +
      arrayMultiplyToScalar(
          arraySin(arrayMultiplyToScalar(t, 2 * math.pi * 12)), 0.5);

  var spectrum = arrayComplexAbs(rfft(signal));
  var freqs = fftFreq(signal.length, d: 1 / fs, realFrequenciesOnly: true);

  for (var i = 0; i < spectrum.length; i++) {
    if (spectrum[i] > 1) {
      print('${freqs[i]} Hz: magnitude ${spectrum[i].toStringAsFixed(3)}');
    }
  }
}
Output
5.0 Hz: magnitude 32.000
12.0 Hz: magnitude 16.000

Precise frequency estimation

A tone between two bins spreads over several of them. A window reduces the leakage and parabolic interpolation on the log magnitude finds the true peak.

Estimate a frequency preciselyWindow the signal, find the FFT peak and refine it with parabolic interpolation.
import 'dart:math' as math;

import 'package:scidart/numdart.dart';
import 'package:scidart/scidart.dart';

void main() {
  var fs = 128.0;
  var f0 = 3.7; // between two FFT bins
  var n = linspace(0, 4, num: (4 * fs).toInt(), endpoint: false);
  var signal = arraySin(arrayMultiplyToScalar(n, 2 * math.pi * f0));

  var windowed = signal * blackmanharris(signal.length);
  var spectrum = arrayComplexAbs(rfft(windowed));
  var i = arrayArgMax(spectrum);
  var refined = parabolic(arrayLog(spectrum), i)[0];

  print('naive estimate: ${fs * i / windowed.length} Hz');
  print(
      'refined estimate: ${(fs * refined / windowed.length).toStringAsFixed(4)} Hz');
}
Output
naive estimate: 3.75 Hz
refined estimate: 3.6993 Hz

Change the tones, the window and the number of samples:

Loading the interactive demo…

Edit this guide on GitHub