2021-01-10 17:57:26 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <array>
|
2022-02-20 13:20:43 +01:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
2023-04-30 08:50:18 -05:00
|
|
|
// Note: Change internal define 'sqrt_internal sqrt' to
|
|
|
|
// 'sqrt_internal sqrtf' to save ~3KB of flash.
|
2023-05-28 03:03:49 +02:00
|
|
|
#define sqrt_internal sqrtf
|
2023-04-30 08:50:18 -05:00
|
|
|
#define FFT_SPEED_OVER_PRECISION
|
2023-05-28 03:03:49 +02:00
|
|
|
#include "libs/arduinoFFT/src/arduinoFFT.h"
|
2021-01-10 17:57:26 +01:00
|
|
|
|
|
|
|
namespace Pinetime {
|
|
|
|
namespace Controllers {
|
|
|
|
class Ppg {
|
2021-04-24 12:00:45 +03:00
|
|
|
public:
|
2021-06-06 15:56:03 +02:00
|
|
|
Ppg();
|
2024-09-21 23:29:15 +01:00
|
|
|
int8_t Preprocess(uint16_t hrs, uint16_t als);
|
2022-12-18 19:14:36 +02:00
|
|
|
int HeartRate();
|
2023-04-30 08:50:18 -05:00
|
|
|
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;
|
2021-01-10 17:57:26 +01:00
|
|
|
|
2021-04-24 12:00:45 +03:00
|
|
|
private:
|
2023-04-30 08:50:18 -05:00
|
|
|
// 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;
|
2021-01-10 17:57:26 +01:00
|
|
|
|
2023-04-30 08:50:18 -05:00
|
|
|
int ProcessHeartRate(bool init);
|
|
|
|
float HeartRateAverage(float hr);
|
|
|
|
void SpectrumAverage(const float* data, float* spectrum, int length, bool reset);
|
2021-01-10 17:57:26 +01:00
|
|
|
};
|
|
|
|
}
|
2021-05-01 12:10:44 +02:00
|
|
|
}
|