Digital Signal Processing with golang

Erik
Dreamwod tech
Published in
4 min readApr 28, 2021

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,

  1. Read and visualize the signal
  2. Frequency analysis with FFT and signal filtering
  3. R-peak detection and heartbeat calculation
  4. Conclusions

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.

Original 50s signal

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)
10 seconds sample

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.

Frequency spectrum

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.

Normalized signal after low and high pass filters

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) 
R-peak detected

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.

R-interval time
Heartbeats

4. Conclusions

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.

--

--

Erik
Dreamwod tech

Developer, backend, frontend, ML. Likes crossfit and training. Building on the app dreamwod.app.