> ## Documentation Index
> Fetch the complete documentation index at: https://developer.neurofit.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Metrics

> What each metric means, its units, the quality tier, and the validated accuracy against a Polar H10 chest strap.

One 60-second reading returns the metrics below in a `VitalsResult`. Readings are processed entirely on the
phone with deterministic signal processing, not machine learning. No images are stored or transmitted; the SDK
returns only these metrics, a quality tier and diagnostics.

<Note>
  The NEUROFIT Vitals SDK is not a medical device. The metrics describe autonomic and cardiorespiratory state for
  wellness and self-tracking; they do not diagnose, treat or prevent any condition.
</Note>

## Result fields

| Field | Unit | Meaning |
| - | - | - |
| `heartRateBpm` | beats per minute | Mean heart rate over the reading. |
| `rmssdMs` | ms | HRV (RMSSD): the root mean square of successive differences between normal heartbeat intervals. The HRV number wearables report. Higher generally reflects more parasympathetic (rest-and-digest) activity. |
| `sdnnMs` | ms | SDNN: the standard deviation of normal heartbeat intervals over the reading. Reflects overall variability. |
| `baevskyStressIndex` | dimensionless | Baevsky stress index. Higher values reflect a more rigid, sympathetic-dominant rhythm. Often shown on a log scale. |
| `sd2sd1` | ratio | Poincaré SD2:SD1. The ratio of long-term to short-term variability. It is derived from SDNN and RMSSD (SD1 = RMSSD / sqrt 2; SD2 = sqrt(2 SDNN squared minus SD1 squared)) and is absent when either input is missing or the expression is undefined. |
| `breathingRateBrpm` | breaths per minute | Breathing rate over the reading. Returned at any confidence. |
| `breathingRateConfidence` | 0 to 1 | Confidence in the breathing rate. Present exactly when `breathingRateBrpm` is present (0.0 when the engine gives none). Below 0.5 the rate is still returned; label it an estimate or hide it, your choice. |
| `confidence` | 0 to 1 | Confidence in the primary metrics: the HRV confidence, falling back to the heart-rate confidence, else 0. |
| `quality` | tier | `clean` (3), `usable` (2) or `withheld` (1). See below. |
| `flags` | list of strings | Reasons the engine attached to the reading: `hrv_unreliable_beats`, `too_short_for_freq_hrv`, `too_short_for_rr`, `insufficient_contact`, `low_quality`, `consistency_gate_rejected`. |
| `diagnostics` | object | Capture and quality internals for support and analytics. See [Diagnostics](/diagnostics). |
| `id`, `capturedAt`, `durationSeconds` | | A UUID (iOS `UUID`, Android `String`), the completion time (iOS `Date`, Android epoch milliseconds; ISO 8601 in the wrappers) and the reading duration in seconds. |
| `engineVersion`, `sdkVersion` | | `1.3.0` and `1.0.0` for this release. Store them with the result. |

Metrics are optional (`nil` / `null`) when the engine could not recover them; every value is finite (no NaN
reaches a result or its JSON). Heart rate is the one metric a completed reading always has; a reading with no
heart rate ends in `failed(noHeartRate)` rather than a result.

During the reading, `LivePreview` carries provisional values: heart rate from the first seconds, HRV (RMSSD) from
about 20 s, breathing rate from about 30 s. They are for display only; store the final `VitalsResult`.

## Quality tier

The SDK grades every reading and withholds the ones it cannot stand behind.

| Tier | Meaning | What to do |
| - | - | - |
| `clean` | High-quality signal throughout. | Show the metrics. |
| `usable` | Good signal with some imperfect stretches. Accuracy figures below include both `clean` and `usable` readings. | Show the metrics. |
| `withheld` | The SDK is not confident in the HRV metrics. `rmssdMs`, `sdnnMs`, `baevskyStressIndex` and `sd2sd1` may be absent. | Do not show HRV numbers. Offer a retry. Do not store the reading as an outcome. |

Two rules apply on top of the engine's grading. A reading whose values are outside physiological plausibility
(heart rate outside 20 to 200 bpm, HRV (RMSSD) outside 5 to 400 ms, breathing rate above 60 breaths per minute;
an absent breathing rate counts as 0) is downgraded to `withheld` and `sd2sd1` and `baevskyStressIndex` are
dropped. And a breathing rate with confidence below 0.5 is still returned with its confidence, so the host decides
how to present it.

In the validation study the SDK withheld 3.3% of readings.

## Validated accuracy

The figures come from the NEUROFIT validation study: 30 participants, 265 accepted readings, each a 60-second
seated reading taken while wearing a Polar H10 chest strap, with the strap's beat-to-beat timing as the
reference. Half the readings were at rest and half during paced breathing at 6 breaths per minute. Three
readings with frequent irregular heartbeats were excluded. The two investigators are excluded from every figure.

| Metric | Mean absolute error | r | 95% limits of agreement | Notes |
| - | - | - | - | - |
| Heart rate | 0.46 bpm | 0.999 | -1.4 to +0.9 bpm | 90% of readings within 1 bpm |
| HRV (RMSSD) | 3.39 ms | 0.979 | -9.1 to +9.8 ms | 94.7% within 10 ms, none off by 20 ms or more |
| SDNN | 4.13 ms | 0.984 | -9.8 to +12.0 ms | |
| Baevsky stress index (ln) | 0.18 | 0.970 | -0.51 to +0.42 | 76% of readings within 25% of the strap value |
| Poincaré SD2:SD1 | 0.28 | 0.908 | -0.81 to +0.77 | Error tracks RMSSD error |
| Breathing rate (paced 6 brpm) | 0.46 brpm | n/a | n/a | 89% within 1 brpm, confidence-gated |

### Consistent across groups

No group had a reading off by 20 ms or more. Groups with one or two people describe those individuals, not the
whole group.

| Group | Participants | Readings | RMSSD error (ms) | Within 10 ms |
| - | - | - | - | - |
| iOS | 21 | 178 | 2.81 | 97% |
| Android | 9 | 87 | 4.58 | 91% |
| Resting | 30 | 134 | 2.98 | 96% |
| Paced breathing | 30 | 131 | 3.80 | 93% |
| Monk skin tone 2 | 4 | 33 | 2.16 | 97% |
| Monk skin tone 3 | 3 | 28 | 1.48 | 100% |
| Monk skin tone 4 | 5 | 46 | 4.14 | 91% |
| Monk skin tone 5 | 9 | 79 | 2.97 | 96% |
| Monk skin tone 6 | 6 | 53 | 5.13 | 91% |
| Monk skin tone 8 | 3 | 26 | 3.39 | 96% |
| Female | 12 | 105 | 3.50 | 95% |
| Male | 18 | 160 | 3.32 | 94% |
| Age 18–24 | 1 | 8 | 2.00 | 100% |
| Age 25–34 | 21 | 181 | 3.66 | 94% |
| Age 35–44 | 5 | 47 | 3.37 | 91% |
| Age 45–54 | 1 | 10 | 1.01 | 100% |
| Age 55–64 | 2 | 19 | 2.70 | 100% |

The full method and plots are in the
[validation report (PDF)](https://neurofit.app/assets/pdf/neurofit-vitals-sdk-validation-report-2026-09.pdf).
These are interim development results; the finished system is being tested blind on new participants, and the
figures will be updated as the study grows.

## Limits of the validation

The accuracy figures apply to **seated, still, 60-second readings**. Not yet claimed:

* Accuracy for Monk skin tones 7, 9 and 10 (no participants in the study so far).
* Accuracy during movement.
* Breathing rate outside paced breathing at 6 breaths per minute.
* Readings shorter or longer than 60 seconds.

Most participants were 25 to 34 years old.

## Presenting the numbers

* Round heart rate and HRV (RMSSD) to whole numbers, breathing rate to one decimal.
* Say "HRV (RMSSD)" the first time you show HRV, so users comparing with a wearable know which HRV it is.
* Compare a person with their own baseline, not with population tables; see
  [Outcomes best practices](/outcomes-best-practices).
* When `quality` is `withheld`, show a calm retry prompt rather than a number.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.