Documentation
¶
Overview ¶
Package estimate builds one estimated result per day from DEXA scans and scale/body-composition readings. Stored readings are only ever read, never changed.
Per field, the estimate is built in these steps:
- Readings are averaged per day and per group (measurement method + athlete mode).
- For every day the most accurate group wins, using the user's method ranking.
- A day with at least MinWindowReadings real readings in its centred 7-day window gets the average of those readings; interpolated days are never part of an average. Any other day with a reading keeps that reading. Peaks are kept, not capped.
- All remaining days are a straight line between the days from step 3, and the last of those is held up to today. Together this is our per-day series.
- DEXA scans calibrate that series: the offset (dexa − the day's reading) is stored per group, because e.g. an athlete-mode scale reading is biased differently from a normal-mode one. A group needs its own reading on the scan day to get an offset; the 7-day average is not used.
- Between two calibrations the offset is blended with a smoothstep curve, so the first half of the gap stays close to the earlier DEXA scan and the second half moves towards the later one. Days before a group's first calibration use its offset, days after the last one use that one.
- Only body fat % and bone mass have offsets. Fat mass is calculated from body fat % and the day's weight, and fat-free mass is the weight minus that fat mass, so the two add up to the weight. The scale's own values of these two are only used when they can't be calculated. Lean mass is the fat-free mass minus the bone mass.
For every field a DEXA value on its own day wins over the calibrated series.
Index ¶
Constants ¶
const MinWindowReadings = 3
MinWindowReadings is how many days with a real reading a window needs before it is averaged.
const WindowDays = 3
WindowDays is the number of days on each side of a day that the rolling average covers (3 + 1 + 3 = a 7-day window).
Variables ¶
var Fields = []Field{ {"weight_kg", "Weight", "kg", false}, {"fat_ratio_pct", "Body fat", "%", true}, {"fat_mass_kg", "Fat mass", "kg", false}, {"fat_free_mass_kg", "Fat-free mass", "kg", false}, {"bone_mass_kg", "Bone mass", "kg", true}, {"lean_mass_kg", "Lean mass", "kg", false}, {"hydration_kg", "Hydration", "kg", false}, {"visceral_fat_index", "Visceral fat index", "", false}, {"bmi", "BMI", "kg/m²", false}, {"fmi", "FMI", "kg/m²", false}, {"lmi", "LMI", "kg/m²", false}, {"ag_ratio", "A/G ratio", "", false}, }
Fields lists the estimated measurements in display order.
Functions ¶
This section is empty.
Types ¶
type Calibration ¶
type Calibration struct {
Date time.Time `json:"date"`
Method string `json:"method"`
AthleteMode bool `json:"athlete_mode"`
Offsets map[string]float64 `json:"offsets"`
}
Calibration is the reading-to-DEXA offset (dexa − daily reading) observed at a DEXA scan, for one reading group.
type DayEstimate ¶
DayEstimate is the estimate for a single day.
func WithinDays ¶
func WithinDays(days []DayEstimate, n int) []DayEstimate
WithinDays returns the part of days (newest first) within n days of the newest day, the newest day counting as the first. An n of 0 or less returns all of them. The result shares the slice.
type Field ¶
type Field struct {
Key string
Label string
Unit string
// Comparable fields are corrected with a DEXA offset: only body fat % and bone mass. Fat mass
// and fat-free mass are calculated from the corrected body fat % instead, and weight is
// deliberately left uncorrected.
Comparable bool
}
Field describes one estimated measurement.
type Options ¶
type Options struct {
// Today is the last day of the series (the day it falls on in Location); the last known value
// is held up to it. The zero value ends each series at its last reading.
Today time.Time
// Location is the user's time zone: it decides which calendar day a scale reading belongs to.
// Nil means UTC. DEXA scans are plain dates and are not moved by it.
Location *time.Location
// Ranking lists methods, most accurate first. Empty means models.DefaultRanking.
Ranking []string
// AthleteDefault breaks ties between the normal and athlete group of the same method.
AthleteDefault bool
// HeightCm is needed for BMI, FMI and LMI. Without it they are left out.
HeightCm *float64
}
Options configure Compute.
type Result ¶
type Result struct {
Days []DayEstimate `json:"days"` // newest first
Calibrations []Calibration `json:"calibrations"` // oldest first
}
Result is the output of Compute.
type Value ¶
type Value struct {
V float64 `json:"value"`
Origin Origin `json:"origin"`
// Raw is the selected raw daily reading, set only on days with a reading.
Raw *float64 `json:"raw,omitempty"`
// Method and AthleteMode name the reading group the value is based on (empty for DEXA).
Method string `json:"method,omitempty"`
AthleteMode bool `json:"athlete_mode,omitempty"`
// Corrected is set when a DEXA offset was applied.
Corrected bool `json:"corrected,omitempty"`
}
Value is an estimated value with its origin.