estimate

package
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

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:

  1. Readings are averaged per day and per group (measurement method + athlete mode).
  2. For every day the most accurate group wins, using the user's method ranking.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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

View Source
const MinWindowReadings = 3

MinWindowReadings is how many days with a real reading a window needs before it is averaged.

View Source
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

View Source
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

type DayEstimate struct {
	Date   time.Time        `json:"date"`
	Values map[string]Value `json:"values"`
}

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 Origin

type Origin string

Origin tells where an estimated value came from.

const (
	OriginDexa         Origin = "dexa"         // a DEXA scan on this day
	OriginMeasured     Origin = "measured"     // a reading exists for this day
	OriginInterpolated Origin = "interpolated" // between two days with readings
	OriginHeld         Origin = "held"         // after the last reading, repeating it
)

type Result

type Result struct {
	Days         []DayEstimate `json:"days"`         // newest first
	Calibrations []Calibration `json:"calibrations"` // oldest first
}

Result is the output of Compute.

func Compute

func Compute(scans []models.DexaScan, readings []models.ScaleReading, o Options) Result

Compute returns the per-day estimates and the calibrations used. The inputs are not modified.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL