feat: implement Haptika PPG and Watch V1
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import type {NativePPGProcessingResult} from 'react-native-haptika-ppg';
|
||||
|
||||
import {mapNativeProcessingResult} from '../measurement/measurementCoordinator';
|
||||
|
||||
const nativeResult = (): NativePPGProcessingResult => ({
|
||||
valid: false,
|
||||
invalidReason: 'Signal exploitable insuffisant',
|
||||
bpm: {
|
||||
bpm: 72,
|
||||
temporalBpm: Number.NaN,
|
||||
frequencyBpm: 72.4,
|
||||
methodAgreementBpm: 0.4,
|
||||
validPeakCount: 19.7,
|
||||
rejectedPeakCount: 8.2,
|
||||
confidence: 0.51,
|
||||
selectedChannel: 'red-inverted',
|
||||
},
|
||||
prv: {
|
||||
estimatedRmssdMs: Number.POSITIVE_INFINITY,
|
||||
estimatedSdnnMs: 31,
|
||||
meanIpiMs: 833,
|
||||
medianIpiMs: 829,
|
||||
pnn50Percent: 7,
|
||||
rangeIpiMs: 118,
|
||||
modeIpiMs: 825,
|
||||
amo50Percent: 42,
|
||||
coefficientOfVariation: 0.05,
|
||||
validIntervalCount: 18.8,
|
||||
rejectedIntervalCount: 7.4,
|
||||
rejectedIntervalRatio: 0.28,
|
||||
reliable: false,
|
||||
},
|
||||
quality: {
|
||||
score: 0.43,
|
||||
level: 'invalid',
|
||||
usableDurationSeconds: 21.5,
|
||||
droppedFrameRatio: 0.08,
|
||||
saturationRatio: 0.12,
|
||||
rejectedIntervalRatio: 0.28,
|
||||
signalToNoiseRatio: 3.2,
|
||||
motionArtifactRatio: 0.31,
|
||||
methodAgreementScore: 0.6,
|
||||
timestampJitterRatio: 0.04,
|
||||
longestGapSeconds: 0.38,
|
||||
issues: ['motionArtifacts', 'futureNativeDiagnostic'],
|
||||
},
|
||||
startedAtMonotonicSeconds: 100,
|
||||
endedAtMonotonicSeconds: 160,
|
||||
rawSampleCount: 1_642.4,
|
||||
resampledSampleCount: 5_501.7,
|
||||
});
|
||||
|
||||
describe('measurementCoordinator diagnostics', () => {
|
||||
test('preserves acquisition diagnostics used to explain an invalid result', () => {
|
||||
const result = mapNativeProcessingResult(nativeResult());
|
||||
|
||||
expect(result).toMatchObject({
|
||||
valid: false,
|
||||
invalidReason: 'Signal exploitable insuffisant',
|
||||
rawSampleCount: 1_642,
|
||||
resampledSampleCount: 5_502,
|
||||
quality: {
|
||||
usableDurationSeconds: 21.5,
|
||||
droppedFrameRatio: 0.08,
|
||||
saturationRatio: 0.12,
|
||||
rejectedIntervalRatio: 0.28,
|
||||
signalToNoiseRatio: 3.2,
|
||||
motionArtifactRatio: 0.31,
|
||||
timestampJitterRatio: 0.04,
|
||||
longestGapSeconds: 0.38,
|
||||
},
|
||||
bpm: {
|
||||
selectedChannel: 'red-inverted',
|
||||
validPeakCount: 20,
|
||||
rejectedPeakCount: 8,
|
||||
},
|
||||
prv: {
|
||||
validIntervalCount: 19,
|
||||
rejectedIntervalCount: 7,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('normalizes non-finite values and unknown native issues safely', () => {
|
||||
const result = mapNativeProcessingResult(nativeResult());
|
||||
|
||||
expect(result.bpm.temporalBpm).toBeNull();
|
||||
expect(result.prv.estimatedRmssdMs).toBeNull();
|
||||
expect(result.quality.issues).toEqual([
|
||||
'motionArtifacts',
|
||||
'nonFiniteProcessingResult',
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user