Digital Signal Processing with golang
Digital signal processing (DSP) in golang, frequency spectrum analysis with FFT, and r-peak detection.
This article will show how golang can be used for digital signal processing (DSP) on a raw PPG or ECG heartbeat signal. We will use the fast Fourier transform (FFT) to look at the frequency spectrum, clean up noise from the signal using low and highpass filter, and finish by implementing r-peak detection on the signal to calculate the r-interval and the average heartbeat.
To outline the different steps,
- Read and visualize the signal
- Frequency analysis with FFT and signal filtering
- R-peak detection and heartbeat calculation
Source code and packages used
https://github.com/eripe970/go-dsp-utils is the go package used in this article, it’s a wrapper around some of the most popular packages for DSP in golang. The example used in this article is also available in that repository.
To run the example just do
$ git clone github.com/eripe970/go-dsp-utils
$ cd go-dsp-utils
$ go run examples/advanced/main.go
And then go to localhost:4000
1. Read and visualize the signal
The chart below shows the signal that we will use as an example in this article. Each peak in the signal is an r-peak and our goal is to calculate the time between the peaks and by that calculate the heart rate.
Importing a signal from a file can be done with the utility function ReadSignalFile.
signal, _ := dsp.ReadSignalFile("example_signal_100_hz.txt", 100)
We can then also look at a 10 seconds sample of the signal with the code.
signal10s := signal.Sample(10 * time.Second)
2. Frequency analysis with FFT
We need to normalize the amplitude of the signal between -1 and 1 to more easily look at the frequency spectrum. An algorithm for that is implemented so we can easily get a normalized signal with the code.
normalized, err := signal.Normalize()
Calculating the Fast Fourier Transform (FFT) on the signal means that we can look at the signal in the frequency domain instead of the time domain. The code is not that complicated, the FFT is first calculated from the signal. Then we compute the two-sided spectrum
spectrum2and after that, we compute the single-sided spectrum
spectrum1 based on
spectrum2 and the length of the signal.
If we now plot the frequency spectrum of the signal we see something like below.
We see that the peak around 0.92Hz (55 bpm). The next step that we want to do is to run the signal through a bandpass filter to remove some of the noise from the low and high frequencies that we don’t care about. The code to do that is,
filtered, err := signal.BandPassFilter(0.5, 1.2)
We assume that we in our example only care about heart rates between 30 BMP (0.5Hz)and 70 bpm (1.2Hz).
If we then look at the final signal it looks like below.
3. R-peak detection
Detecting the heart-beats is as simple as doing a peak detection on the signal. In our library, it’s done with the code.
rPeaks := dsp.GetRPeaks(signal)
Not easy to see in the graph but each red line is where an r-peak is detected. Displaying the r-interval time and the heartbeat gives us the following charts.
That’s all for the first part! The source code is available at https://github.com/eripe970/go-dsp-utils.
The next article will continue on this one and we will use TimescaleDB and Grafana to calculate and plot the heartbeats in real-time.