aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/heartrate/Ppg.h
diff options
context:
space:
mode:
authorCeimour <113631258+Ceimour@users.noreply.github.com>2023-04-30 08:50:18 -0500
committerGitHub <noreply@github.com>2023-04-30 15:50:18 +0200
commitc22e30a4a6ef014c7a5086ad47eaab7740a75ff2 (patch)
tree5afdf4ed624a8b41dc4aea723a8c8f38d726545a /src/components/heartrate/Ppg.h
parent40f7e1c7be6882e01058b5ccf64d5005c6105346 (diff)
Refactored Ppg for frequency based algorithm. (#1486)
New implementation of the heart rate sensor data processing using a frequency based PPG algorithm. The HRS3300 settings are fine-tuned for better signal to noise at 10Hz. The measurement delay is now set to 100ms. Enable and use the ambient light sensor. FFT implementation based on ArduinoFFT (https://github.com/kosme/arduinoFFT, GPLv3.0).
Diffstat (limited to 'src/components/heartrate/Ppg.h')
-rw-r--r--src/components/heartrate/Ppg.h74
1 files changed, 61 insertions, 13 deletions
diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h
index 1f709bab..2f8a1faa 100644
--- a/src/components/heartrate/Ppg.h
+++ b/src/components/heartrate/Ppg.h
@@ -3,29 +3,77 @@
#include <array>
#include <cstddef>
#include <cstdint>
-#include "components/heartrate/Biquad.h"
-#include "components/heartrate/Ptagc.h"
+// Note: Change internal define 'sqrt_internal sqrt' to
+// 'sqrt_internal sqrtf' to save ~3KB of flash.
+#define FFT_SPEED_OVER_PRECISION
+#include "libs/arduinoFFT-develop/src/arduinoFFT.h"
namespace Pinetime {
namespace Controllers {
class Ppg {
public:
Ppg();
- int8_t Preprocess(float spl);
+ int8_t Preprocess(uint32_t hrs, uint32_t als);
int HeartRate();
-
- void SetOffset(uint16_t offset);
- void Reset();
+ void Reset(bool resetDaqBuffer);
+ static constexpr int deltaTms = 100;
+ // Daq dataLength: Must be power of 2
+ static constexpr uint16_t dataLength = 64;
+ static constexpr uint16_t spectrumLength = dataLength >> 1;
private:
- std::array<int8_t, 200> data;
- size_t dataIndex = 0;
- float offset;
- Biquad hpf;
- Ptagc agc;
- Biquad lpf;
+ // The sampling frequency (Hz) based on sampling time in milliseconds (DeltaTms)
+ static constexpr float sampleFreq = 1000.0f / static_cast<float>(deltaTms);
+ // The frequency resolution (Hz)
+ static constexpr float freqResolution = sampleFreq / dataLength;
+ // Number of samples before each analysis
+ // 0.5 second update rate at 10Hz
+ static constexpr uint16_t overlapWindow = 5;
+ // Maximum number of spectrum running averages
+ // Note: actual number of spectra averaged = spectralAvgMax + 1
+ static constexpr uint16_t spectralAvgMax = 2;
+ // Multiple Peaks above this threshold (% of max) are rejected
+ static constexpr float peakDetectionThreshold = 0.6f;
+ // Maximum peak width (bins) at threshold for valid peak.
+ static constexpr float maxPeakWidth = 2.5f;
+ // Metric for spectrum noise level.
+ static constexpr float signalToNoiseThreshold = 3.0f;
+ // Heart rate Region Of Interest begin (bins)
+ static constexpr uint16_t hrROIbegin = static_cast<uint16_t>((30.0f / 60.0f) / freqResolution + 0.5f);
+ // Heart rate Region Of Interest end (bins)
+ static constexpr uint16_t hrROIend = static_cast<uint16_t>((240.0f / 60.0f) / freqResolution + 0.5f);
+ // Minimum HR (Hz)
+ static constexpr float minHR = 40.0f / 60.0f;
+ // Maximum HR (Hz)
+ static constexpr float maxHR = 230.0f / 60.0f;
+ // Threshold for high DC level after filtering
+ static constexpr float dcThreshold = 0.5f;
+ // ALS detection factor
+ static constexpr float alsFactor = 2.0f;
+
+ // Raw ADC data
+ std::array<uint16_t, dataLength> dataHRS;
+ // Stores Real numbers from FFT
+ std::array<float, dataLength> vReal;
+ // Stores Imaginary numbers from FFT
+ std::array<float, dataLength> vImag;
+ // Stores power spectrum calculated from FFT real and imag values
+ std::array<float, (spectrumLength)> spectrum;
+ // Stores each new HR value (Hz). Non zero values are averaged for HR output
+ std::array<float, 20> dataAverage;
+
+ uint16_t avgIndex = 0;
+ uint16_t spectralAvgCount = 0;
+ float lastPeakLocation = 0.0f;
+ uint16_t alsThreshold = UINT16_MAX;
+ uint16_t alsValue = 0;
+ uint16_t dataIndex = 0;
+ float peakLocation;
+ bool resetSpectralAvg = true;
- int ProcessHeartRate();
+ int ProcessHeartRate(bool init);
+ float HeartRateAverage(float hr);
+ void SpectrumAverage(const float* data, float* spectrum, int length, bool reset);
};
}
}