Loading...
Searching...
No Matches
Core Helpers

Pure maths, clock, text, and unit helpers that need no watch SDK. More...

Files

file  src/c/core/clock/astro.c
 Pure astronomy math, host-testable off-device.
file  src/c/core/clock/astro.h
 Pure astronomy math you can test off the watch. Right now just the Julian Date, worked out from a UTC timestamp with plain integer math, so it needs no Pebble services and no floats.
file  src/c/core/clock/beats.c
 Pure swatch .beats math, host-testable off-device.
file  src/c/core/clock/beats.h
 Pure swatch .beats math, operates on ms since Biel Mean Time (UTC+1) midnight. Also holds the token a date format uses to ask for a .beats reading.
file  src/c/core/clock/date.c
 Pure calendar math, host-testable off-device.
file  src/c/core/clock/date.h
 Pure calendar math you can test off the watch. Week numbers, day counts, and month shapes worked out from plain numbers, so it needs no Pebble services and no struct tm.
file  src/c/core/clock/duration.c
 A minute count turned into a short human duration.
file  src/c/core/clock/duration.h
 A minute count turned into a short human duration, no SDK.
file  src/c/core/clock/hour_latch.c
 The hourly buzz latch, host-testable off-device.
file  src/c/core/clock/hour_latch.h
 Decides when an hourly buzz fires, so it fires once for each real hour.
file  src/c/core/clock/moon.c
 Pure moon-phase math, host-testable off-device.
file  src/c/core/clock/moon.h
 Pure moon-phase math you can test off the watch. It counts from a known new moon using the average length of a moon cycle, so it needs nothing but a UTC timestamp.
file  src/c/core/clock/nightsched.c
 The night window, by containment rather than by edge.
file  src/c/core/clock/nightsched.h
 Whether a night-time window is open right now, from plain clock readings.
file  src/c/core/clock/rotate.c
 Working out which one is up from the clock.
file  src/c/core/clock/rotate.h
 Which one of a set to show right now, on a timed rotation.
file  src/c/core/clock/solar.c
 How the day's light is going, worked out from three plain clock readings.
file  src/c/core/clock/solar.h
 How the day's light is going, worked out from three plain clock readings.
file  src/c/core/clock/tide.c
 The rise and fall of a semidiurnal tide, from a plain running minute count.
file  src/c/core/clock/tide.h
 The rise and fall of a semidiurnal tide, from a plain running minute count.
file  src/c/core/clock/timeband.c
 The window sums: build one, place a moment on it, and clip a span to it.
file  src/c/core/clock/timeband.h
 A stretch of the day as an axis, and the sums that put a moment somewhere along it.
file  src/c/core/clock/weekday.c
 Weekday names, host-testable off-device.
file  src/c/core/clock/weekday.h
 Short weekday names shared by the panels that label a day.
file  src/c/core/clock/zone_setting.c
 Reads a time zone setting the way the phone sends it.
file  src/c/core/clock/zone_setting.h
 Reads a time zone setting the way the phone sends it, the minutes ahead of UTC then a comma and the place's name, as in "60,London". An empty setting means no zone is picked, which is what the phone sends once the wearer clears the picker.
file  src/c/core/health/minute_window.c
 Where a run of minute records lands in a window that ends at a given minute.
file  src/c/core/health/minute_window.h
 Where a run of minute records lands in a window that ends at a given minute.
file  src/c/core/health/step_hours.c
 The hour arithmetic behind the step chart's buckets.
file  src/c/core/health/step_hours.h
 The hour arithmetic behind the step chart's buckets.
file  src/c/core/io/bytes_le.h
 Little-endian byte readers shared by the wire-decoding stores.
file  src/c/core/io/callback_list.c
 Adding to and running a callback list.
file  src/c/core/io/callback_list.h
 A short fixed list of callbacks, run in the order they were added.
file  src/c/core/layout/layout_role.c
 The deprecated layout_role_pick, forwarding to role_pick.
file  src/c/core/layout/layout_role.h
 Deprecated names for the day/night/Quiet Time picker that role.h provides.
file  src/c/core/layout/layout_string.c
 Walking a layout wire string far enough to answer whether it holds anything.
file  src/c/core/layout/layout_string.h
 Reading a layout wire string without parsing the whole thing.
file  src/c/core/math/pct.c
 Progress towards a goal, as a percent.
file  src/c/core/math/pct.h
 Progress towards a goal, as a percent.
file  src/c/core/math/scale.c
 The clamp and segment maths a bar or gauge is built from.
file  src/c/core/math/scale.h
 The clamp and segment maths a bar or gauge is built from, no SDK.
file  src/c/core/math/series.c
 Reducing a run of samples to the few numbers a chart draws from.
file  src/c/core/math/series.h
 Reducing a run of samples to the few numbers a chart draws from, no SDK.
file  src/c/core/role/role.c
 The order the day, night, and Quiet Time triggers are asked in.
file  src/c/core/role/role.h
 Which of day, night, and Quiet Time should be on screen right now.
file  src/c/core/text/cstring_fit.h
 Comparing a string against a fixed buffer it is about to be copied into.
file  src/c/core/text/number_format.c
 Pure number and string formatting, no SDK behind it.
file  src/c/core/text/number_format.h
 Pure number and string formatting, no SDK behind it.
file  src/c/core/text/text_case.c
 Pure ascii case helpers, no SDK behind them.
file  src/c/core/text/text_case.h
 Pure ascii case helpers, no SDK behind them.
file  src/c/core/units/distance.c
 Pure distance conversions and formatting, no SDK behind them.
file  src/c/core/units/distance.h
 Pure distance conversions and formatting, no SDK behind them.
file  src/c/core/units/wind.c
 Pure wind-speed conversions and unit labels, no SDK behind them.
file  src/c/core/units/wind.h
 Pure wind-speed conversions and unit labels, no SDK behind them.
file  src/c/core/weather/forecast_age.c
 How much of a forecast strip has already gone by.
file  src/c/core/weather/forecast_age.h
 How much of a forecast strip has already gone by, so a strip that stays on the watch after a missed fetch shows what is still to come rather than hours and days already over.
file  src/c/core/weather/weather_reading.c
 Turns one weather message into the reading a store keeps.
file  src/c/core/weather/weather_reading.h
 One weather message off the phone, and the reading it leaves behind.
file  src/c/core/weather/wind_dir.c
 The compass direction a wind is blowing from, as a bearing and as a sideways component.
file  src/c/core/weather/wind_dir.h
 The compass direction a wind is blowing from, as a bearing and as a sideways component.
file  src/c/core/weather/wx_label.c
 The short word a small slot shows for the sky.
file  src/c/core/weather/wx_label.h
 The short word a small slot shows for the sky, from the condition token the phone sends.
file  src/c/core/wire/calendar_wire.c
 The agenda as the phone packs it, and the reader that unpacks it.
file  src/c/core/wire/calendar_wire.h
 The agenda as the phone packs it, and the reader that unpacks it.
file  src/c/core/wire/coords.c
 Telling a real fix from the phone's way of saying it has none.
file  src/c/core/wire/coords.h
 Telling a real fix from the phone's way of saying it has none.
file  src/c/core/wire/stock_wire.c
 The watchlist as the phone packs it, and the reader that unpacks it.
file  src/c/core/wire/stock_wire.h
 The watchlist as the phone packs it, and the reader that unpacks it.
file  src/c/core/wire/weather_wire.c
 The forecast strips as the phone packs them, and the readers that unpack them.
file  src/c/core/wire/weather_wire.h
 The forecast strips as the phone packs them, and the readers that unpack them.
file  src/c/core/wire/wire_caps.g.h
 Strip caps and marker values shared by the phone and the watch.
file  src/c/core/wire/wire_read.h
 Reads the values the phone packs into a message.

Data Structures

struct  HourLatch
 Remembers the hour that last buzzed. More...
struct  TimeBand
 The stretch of day a band shows: where it starts and how long it runs. More...
struct  TimeBandSpan
 A span clipped to the window, in minutes past the window's start. More...
struct  CallbackList
 A list over storage its owner provides. Start it as {entries, max, 0}. More...
struct  WeatherMessage
 One weather message as the transport read it, before anything is kept. More...
struct  WeatherState
 The weather a store keeps, laid out as the blob it saves to flash. More...
struct  WeatherClock
 The watch's clock at the moment a message lands, broken apart. More...
struct  CalendarEvent
 One event. start and end are absolute epochs so the readouts stay fresh against the watch's own clock. The phone fills end with a sensible default when the feed omits DTEND. More...
struct  CalendarStrip
 The agenda strip. count is 0 until a reading lands. More...
struct  StockSlot
 One quote slot. When ok is false the symbol field carries a short status word ("RATE LIMIT" and friends) instead of a ticker so the watch has something to show. More...
struct  StockStrip
 The watchlist strip. count is 0 until a reading lands. More...
struct  WeatherHourCol
 One hourly forecast column: a sky code plus a temperature (WEATHER_NO_TEMP for none). More...
struct  WeatherDayCol
 One daily forecast column: a sky code plus the day's high and low. More...
struct  WeatherHourly
 The hourly strip. count is 0 until a reading lands. More...
struct  WeatherDaily
 The 7-day strip. count is 0 until a reading lands. More...

Macros

#define HOUR_LATCH_EMPTY   {.last_start = -1}
 a latch that has not buzzed yet
#define MOON_SYNODIC_SEC   2551443
 Average length of one moon cycle in seconds (about 29.53 days).
#define MOON_EPOCH_UTC   947182440
 A known new moon to count from: 2000-01-06 18:14 UTC.
#define TIDE_PERIOD_MIN   745
 The lunar semidiurnal period, 12h 25m. Two of these make one tidal day.
#define TIMEBAND_DAY_MINUTES   1440
 Minutes in a day, the modulus every wrap here is taken over.
#define STEP_HOURS_PER_DAY   24
#define LAYOUT_ROLE_DAY   ROLE_DAY
#define LAYOUT_ROLE_NIGHT   ROLE_NIGHT
#define LAYOUT_ROLE_QUIET   ROLE_QUIET
#define LAYOUT_ROLE_COUNT   ROLE_COUNT
#define LAYOUT_INT_MAX   9999
 The biggest number a layout field holds. Nothing the builder writes comes close.
#define WEATHER_GROUP_CURRENT   (1u << 0)
 The current temperature and sky arrived.
#define WEATHER_GROUP_EXTRA   (1u << 1)
 At least one of the extras (humidity, wind, direction, sunrise, sunset) arrived.
#define WEATHER_GROUP_FORECAST   (1u << 2)
 At least one of today's forecast readings (UV, high, low, rain chance) arrived.
#define WEATHER_GROUP_AIR   (1u << 3)
 At least one of the air readings (feels-like, pressure, dew point) arrived.

Typedefs

typedef void(*) CallbackListFn(void)
 One callback in a list.
typedef Role LayoutRole

Enumerations

enum  NightSchedMode { NIGHT_SCHED_OFF = 0 , NIGHT_SCHED_SOLAR , NIGHT_SCHED_FIXED , NIGHT_SCHED_COUNT }
 What the schedule is following. The caller's own enum should line up with these. More...
enum  Role { ROLE_DAY = 0 , ROLE_NIGHT , ROLE_QUIET , ROLE_COUNT }
 Which of the three is showing. The caller's own storage should line up with these. More...
enum  WindUnit {
  WIND_UNIT_KMH = 0 , WIND_UNIT_MPH , WIND_UNIT_KTS , WIND_UNIT_MS ,
  WIND_UNIT_COUNT
}
 Which unit a wind speed reads out in. More...

Functions

int32_t astro_jd_centi (time_t utc)
 The Julian Date multiplied by 100, so the whole part is the day and the last two digits are hundredths. Kept scaled to stay in plain 32-bit integers.
int beats_from_ms (int32_t ms_into_bmt_day)
 Calculates swatch internet time in .beats (0..999) for a given ms.
uint32_t ms_until_next_beat (int32_t ms_into_bmt_day)
 Calculates milliseconds until the next .beats boundary.
int32_t beats_ms_from_hms (int hour, int minute, int second, uint16_t ms)
 Milliseconds into the Biel Mean Time day, from a UTC wall clock.
bool beats_has_token (const char *format)
 Whether a date format asks for a .beats reading.
void beats_expand_token (char *text, int beats)
 Overwrite every BEATS_TOKEN in a string with a three digit .beats reading.
int date_iso_week (int year, int yday, int wday)
 The ISO 8601 week number, which always counts weeks from Monday.
int date_iso_week_year (int year, int yday, int wday)
 The ISO 8601 year the week number belongs to, which is not always the calendar year.
int date_iso_weeks_in_year (int year)
 How many ISO 8601 weeks a year holds, either 52 or 53.
int date_day_of_year (int yday)
 Which day of the year it is, counting January 1st as day 1.
int date_days_left_in_year (int year, int yday)
 How many days are left before the year ends, so December 31st reads 0.
int date_days_in_month (int year, int mon0)
 How many days a month has, with February getting its leap day.
int date_first_wday (int wday, int mday)
 The weekday the 1st of the month lands on, walked back from today's weekday.
void duration_hm (char *out, size_t n, int minutes)
 Format minutes as a clock-style "H:MM", e.g. 445 -> "7:25".
void duration_hm_compact (char *out, size_t n, int minutes)
 Format minutes as "Xh Ym", dropping the hours once it is under one ("45m").
time_t hour_latch_start (time_t now, int tm_min, int tm_sec)
 When the local hour holding a moment began, as seconds since the epoch.
bool hour_latch_take (HourLatch *latch, int tick_min, time_t start)
 Whether a tick should buzz the hour, and if so, marks the hour as buzzed.
void hour_latch_seed (HourLatch *latch, time_t start)
 Marks an hour as buzzed without buzzing it.
int32_t moon_age_sec (time_t utc)
 How far into the current moon cycle we are, in seconds.
int moon_glyph_index (time_t utc, int count)
 Picks one of count evenly spaced phase icons. It wraps back round at the end.
int moon_illumination_pct (time_t utc)
 How lit up the moon looks, from 0 to 100. This is a rough straight-line guess, so it reads 0 at new moon and 100 at full.
const char * moon_phase_name (time_t utc)
 Short name for the current phase (one of the eight main phases).
int moon_days_to_phase (time_t utc, bool to_full)
 How many days until the next full or new moon, rounded to the nearest whole day.
bool clock_window_contains (int start, int end, int now)
 Whether now falls inside a window that may run past midnight.
bool night_schedule_active (int mode, int now, int rise, int set, int fixed_start, int fixed_end, bool have_night)
 Whether the night window is open, whichever way the schedule is set.
int rotate_index (int year, int yday, int hour, int minute, int interval_min, int count)
 Which of count is up now, advancing by one every interval_min minutes and wrapping round at the end.
int solar_day_progress (int rise, int set, int now)
 How far through the daylight it is, from 0 at sunrise to 100 at sunset.
int solar_night_progress (int rise, int set, int now)
 How far through the night it is, from 0 at sunset to 100 at the next sunrise.
int solar_next_event (int rise, int set, int now, bool *is_sunrise)
 Minutes until the next sun event, and which one it is.
int tide_level (int32_t minutes)
 How far in the tide is, from 0 at dead low to 100 at high water.
bool tide_rising (int32_t minutes)
 Whether the tide is on its way in.
TimeBand timeband_full_day (void)
 A window running midnight to midnight.
TimeBand timeband_rolling (int now_min, int span_min, int lead_min)
 A window that puts a given moment a fixed way into it.
TimeBand timeband_from_hour (int base_hour, int span_min)
 A window starting at the top of a given hour.
int timeband_offset (TimeBand band, int minute_of_day)
 How far into the window a minute of the day falls.
int timeband_pos (TimeBand band, int length, int minute_of_day)
 Where a minute of the day lands along an axis of a given length.
int timeband_pos_offset (TimeBand band, int length, int offset_min)
 The same mapping for a value already measured from the window's start.
bool timeband_clip (TimeBand band, time_t window_epoch, time_t start, time_t end, TimeBandSpan *out)
 Clip an absolute span onto the window.
int timeband_clip_daily (TimeBand band, int from_min, int to_min, TimeBandSpan *out, int max_out)
 Clip a daily span, one that repeats every day like the daylight, onto the window.
time_t timeband_window_epoch (TimeBand band, time_t now, int now_min)
 The epoch the window's first minute sits at.
const char * weekday_short (int wday)
 The two-letter short name for a weekday, indexed like tm_wday and the phone's base_weekday (0 = Sunday through 6 = Saturday).
bool zone_setting_is_set (const char *value)
 Whether the setting holds a zone at all.
int16_t zone_setting_offset (const char *value)
 How many minutes the zone is ahead of UTC, negative for one behind.
const char * zone_setting_label (const char *value)
 The place's name, everything after the first comma.
int minute_window_first_slot (time_t first_min, time_t end_min, int minutes)
 Where the first of a run of back to back minute records lands in a window.
int step_hours_settled (int cur_hour, int cur_min)
 How many whole hours have settled, meaning they can never gain another step.
static int16_t read_i16_le (const uint8_t *p)
 Reads a little-endian signed 16-bit value out of a byte pair.
static int32_t read_i32_le (const uint8_t *p)
 Reads a little-endian signed 32-bit value out of four bytes.
bool callback_list_add (CallbackList *list, CallbackListFn cb)
 Adds a callback to the end of the list. Adding one that is already there does nothing.
void callback_list_fire (const CallbackList *list)
 Runs every callback in the order it was added.
LayoutRole layout_role_pick (bool quiet_on, bool quiet_set, bool night_on, bool night_set)
 Which layout wins. Forwards to role_pick.
int layout_parse_int (const char **cursor)
 Reads a run of digits and moves the cursor past them.
bool layout_has_any_block (const char *layout, int type_count)
 Whether a layout string holds at least one placeable block.
int pct_of (int value, int goal)
 How far along a goal is, from 0 to 100.
int clamp_int (int value, int lo, int hi)
 Pin a value into a range, so it lands on the nearer end when it falls outside.
int segment_width (int total, int gap, int count)
 The width of one cell when a strip is carved into evenly spaced segments.
int fraction_px (int total, int num, int den)
 A fraction of a length in pixels, e.g. how far along a bar a moment sits.
int segments_filled (int level, int segments)
 How many cells of a gauge a percentage lights, rounding up so any charge lights one.
int plot_y (int y0, int height, int lo, int hi, int value)
 Map a reading to its pixel row in a plot, with the low end at the bottom.
int series_range (const uint8_t *samples, int count, uint8_t no_sample, int *lo, int *hi, int *last)
 Walk a series of samples for its low, high, and most recent reading.
int series_max_u16 (const uint16_t *values, int count)
 The largest value in a series, for scaling a bar strip against its busiest slot.
Role role_pick (bool quiet_on, bool quiet_set, bool night_on, bool night_set)
 Which role wins, given what is firing and what has something behind it.
static void cstring_fit (char *dst, const char *src, size_t size)
 Copy a string into a fixed buffer, keeping only what fits and always terminating it.
static bool cstring_fit_same (const char *current, const char *value, size_t size)
 Whether copying value into a buffer would leave it reading exactly as it does now.
void number_group (char *buffer, size_t size, int value)
 Format an integer with apostrophe thousands separators (e.g. 20358 -> "20'358").
void fmt_int_or_dash (char *buffer, size_t size, int value, const char *fmt)
 Format an integer, or "--" when there is no reading yet.
void fmt_hundredths (char *buffer, size_t size, int value)
 Format hundredths as a two-decimal number, e.g. 26174 -> "261.74".
void fmt_pct_signed (char *buffer, size_t size, int value)
 Format hundredths as a signed two-decimal percent, e.g. 125 -> "+1.25%".
void copy_bounded (char *dst, uint8_t cap, const uint8_t *src, uint8_t len)
 Copy bytes into a fixed buffer, taking what fits and terminating it.
bool cstring_is_clean (const char *s, uint16_t size)
 Whether a byte range holds a string worth keeping.
bool cstring_setting_is_clean (const char *s, uint16_t size, const char *default_str)
 Whether a saved text setting reads as one, where an empty setting can be the real value.
void text_to_upper (char *s)
 Uppercase an ascii string in place.
const char * distance_unit (bool miles)
 Unit label for the current distance mode ("MI" or "KM").
void distance_format_value (char *buffer, size_t size, int meters, bool miles)
 Format meters as "N.N" (rounded to tenth), no unit suffix.
void distance_format (char *buffer, size_t size, int meters, bool miles)
 Format meters as "N.N MI" or "N.N KM" (rounded to tenth).
int wind_from_kmh (int kmh, WindUnit unit)
 Convert a km/h wind speed into the chosen unit.
const char * wind_unit_label (WindUnit unit)
 The short label for a wind unit.
uint8_t forecast_hours_past (int32_t seconds_into_strip, uint8_t step_hours, uint8_t count)
 How many of an hourly strip's columns are over.
uint8_t forecast_days_past (int days_since_first, uint8_t count)
 How many of a daily strip's columns are days before today.
bool weather_reading_apply (WeatherState *state, const WeatherMessage *msg, const WeatherClock *clock)
 Put a message's readings into the kept weather.
void weather_reading_convert (WeatherState *state, bool to_fahrenheit)
 Convert every temperature in the kept weather to the other unit.
int wind_bearing (const char *dir)
 The bearing a 16-point compass abbreviation stands for.
int wind_lean (int bearing)
 The east-west component of a wind, as a signed proportion.
void wx_label_short (char *out, size_t n, const char *condition)
 Writes the short word for a condition token, without its "_NIGHT" suffix.
bool calendar_wire_decode (const uint8_t *buf, uint16_t len, CalendarStrip *out)
 Unpacks an agenda off the wire.
bool coords_look_real (const char *lat, const char *lon)
 Whether a pair of coordinates is a place rather than the phone shrugging.
bool stock_wire_decode (const uint8_t *buf, uint16_t len, StockStrip *out)
 Unpacks a watchlist off the wire.
bool weather_hourly_decode (const uint8_t *buf, uint16_t len, WeatherHourly *out)
 Unpacks an hourly forecast off the wire.
bool weather_daily_decode (const uint8_t *buf, uint16_t len, WeatherDaily *out)
 Unpacks a 7-day forecast off the wire.
static int32_t wire_int_or (bool is_signed, uint16_t length, const uint8_t *value, int32_t fallback)
 Reads a little-endian integer of exactly length bytes.
static bool wire_cstring_terminated (const char *s, uint16_t length)
 Whether a string of length bytes carries its terminator inside itself.

Detailed Description

Pure maths, clock, text, and unit helpers that need no watch SDK.

Needing no SDK is what lets them be tested off the watch. Think .beats time, moon phase, weekday names, number formatting, and unit conversions.

Macro Definition Documentation

◆ LAYOUT_ROLE_COUNT

#define LAYOUT_ROLE_COUNT   ROLE_COUNT
Deprecated
Use ROLE_COUNT.

◆ LAYOUT_ROLE_DAY

#define LAYOUT_ROLE_DAY   ROLE_DAY

◆ LAYOUT_ROLE_NIGHT

#define LAYOUT_ROLE_NIGHT   ROLE_NIGHT

◆ LAYOUT_ROLE_QUIET

#define LAYOUT_ROLE_QUIET   ROLE_QUIET

◆ STEP_HOURS_PER_DAY

#define STEP_HOURS_PER_DAY   24

How many hourly buckets a day holds.

Typedef Documentation

◆ LayoutRole

typedef Role LayoutRole
Deprecated
Use Role. The names line up one for one.

Enumeration Type Documentation

◆ NightSchedMode

What the schedule is following. The caller's own enum should line up with these.

Enumerator
NIGHT_SCHED_OFF 

Never night.

NIGHT_SCHED_SOLAR 

Sunset to sunrise, falling back to the fixed pair with no reading.

NIGHT_SCHED_FIXED 

The fixed pair only.

◆ Role

enum Role

Which of the three is showing. The caller's own storage should line up with these.

Enumerator
ROLE_DAY 

The everyday look, and what everything else falls back to.

ROLE_NIGHT 

The one the night schedule brings in after dark.

ROLE_QUIET 

The one Quiet Time brings in, whatever the hour.

◆ WindUnit

enum WindUnit

Which unit a wind speed reads out in.

KMH is zero so a raw setting byte maps straight across and no unsigned range check ever compares below zero.

Enumerator
WIND_UNIT_KMH 

kilometres per hour

WIND_UNIT_MPH 

miles per hour

WIND_UNIT_KTS 

knots

WIND_UNIT_MS 

metres per second

WIND_UNIT_COUNT 

how many units there are, not a unit itself

Function Documentation

◆ astro_jd_centi()

int32_t astro_jd_centi ( time_t utc)

The Julian Date multiplied by 100, so the whole part is the day and the last two digits are hundredths. Kept scaled to stay in plain 32-bit integers.

Parameters
utcThe time to read, as a UTC timestamp.
Returns
The Julian Date times 100 (e.g. 245154500 for JD 2451545.00).

◆ beats_expand_token()

void beats_expand_token ( char * text,
int beats )

Overwrite every BEATS_TOKEN in a string with a three digit .beats reading.

Works in place because the token and the reading are the same width, so the string neither grows nor needs its tail shifted. A reading outside 0 to 999 is clamped rather than written, which is what keeps that width promise true.

Parameters
textThe string to fill in, NUL terminated. Left alone if NULL or tokenless.
beatsThe reading to write, 0 to 999.

◆ beats_from_ms()

int beats_from_ms ( int32_t ms_into_bmt_day)

Calculates swatch internet time in .beats (0..999) for a given ms.

Parameters
ms_into_bmt_dayMilliseconds elapsed since Biel Mean Time (UTC+1) midnight.
Returns
The current time in .beats (0 to 999).

◆ beats_has_token()

bool beats_has_token ( const char * format)

Whether a date format asks for a .beats reading.

The face asks this to decide whether to run the .beats ticker. A beat is 86.4 seconds and the date line otherwise redraws on the minute, so without the ticker the reading sits stale and reports every rollover up to a minute late.

Parameters
formatThe date format string, or NULL.
Returns
true if the format carries BEATS_TOKEN.

◆ beats_ms_from_hms()

int32_t beats_ms_from_hms ( int hour,
int minute,
int second,
uint16_t ms )

Milliseconds into the Biel Mean Time day, from a UTC wall clock.

Takes the clock already broken apart rather than a time_t, because breaking one apart means gmtime, and on the watch gmtime and struct tm belong to the SDK rather than to time.h. Reaching for either here would drag the whole SDK into a file whose point is not needing it. The caller holds the clock and this holds the sum.

The two above want ms rather than seconds because a beat boundary falls mid-second, so flooring to whole seconds reads a beat behind at exactly the moment a timer fires to redraw.

Parameters
hourThe UTC hour (0 to 23).
minuteThe UTC minute (0 to 59).
secondThe UTC second (0 to 59).
msThe milliseconds within that second.
Returns
Milliseconds into the current BMT day (0 to 86,399,999).

◆ calendar_wire_decode()

bool calendar_wire_decode ( const uint8_t * buf,
uint16_t len,
CalendarStrip * out )

Unpacks an agenda off the wire.

Layout is [count] then per event [start int32 LE][end int32 LE][flags bit0=allDay][titleLen] [title bytes][locLen][loc bytes]. A count past what the strip holds is pinned to it rather than walking off the end.

Nothing is written to out until the whole run reads clean, so a message that stops halfway leaves the caller's last good agenda alone rather than half replacing it.

Parameters
bufThe raw wire bytes.
lenHow many bytes there are.
[out]outReceives the agenda. Untouched unless this returns true.
Returns
Whether the run read clean.

◆ callback_list_add()

bool callback_list_add ( CallbackList * list,
CallbackListFn cb )

Adds a callback to the end of the list. Adding one that is already there does nothing.

Parameters
listThe list.
cbThe callback. NULL is ignored.
Returns
False only when the list is full and cb was dropped, so the owner can say so.

◆ callback_list_fire()

void callback_list_fire ( const CallbackList * list)

Runs every callback in the order it was added.

Parameters
listThe list.

◆ clamp_int()

int clamp_int ( int value,
int lo,
int hi )

Pin a value into a range, so it lands on the nearer end when it falls outside.

Parameters
valueThe value to pin.
loThe low end, returned when the value is below it.
hiThe high end, returned when the value is above it.
Returns
The value, or whichever end it sat outside.

◆ clock_window_contains()

bool clock_window_contains ( int start,
int end,
int now )

Whether now falls inside a window that may run past midnight.

The start is inclusive and the end exclusive, so two windows that meet at the same minute never both claim it. A window that starts and ends at the same minute is empty rather than eternal, which is the reading that does not surprise anyone who set both dials the same by accident.

Parameters
startFirst minute inside the window, 0 to 1439, or -1 for no reading.
endFirst minute past it, same range.
nowMinutes past midnight.
Returns
True while now is inside. False if any reading is missing.

◆ coords_look_real()

bool coords_look_real ( const char * lat,
const char * lon )

Whether a pair of coordinates is a place rather than the phone shrugging.

The phone sends coordinates as text and says "no fix" by sending something that is not one: an empty string, or a word like "NO LOCK". A coordinate always carries digits and those never do, which is the whole of the test.

Both halves have to hold up. Half a fix is not half a place. It is not a place, and keeping one is how a longitude goes missing and stays missing.

Parameters
latThe latitude as the phone sent it.
lonThe longitude as the phone sent it.
Returns
Whether the pair is worth keeping.

◆ copy_bounded()

void copy_bounded ( char * dst,
uint8_t cap,
const uint8_t * src,
uint8_t len )

Copy bytes into a fixed buffer, taking what fits and terminating it.

For the length-prefixed text the phone sends, where the length is the phone's word and the room is ours. What does not fit is dropped rather than written past the end.

Parameters
[out]dstWhere to write. Ends terminated whenever cap is at least 1.
capHow much room dst has, terminator included. A cap of 0 has no room even for the terminator, so dst is left untouched and not terminated.
srcThe bytes to copy.
lenHow many bytes there are.

◆ cstring_fit()

void cstring_fit ( char * dst,
const char * src,
size_t size )
inlinestatic

Copy a string into a fixed buffer, keeping only what fits and always terminating it.

The cut counts bytes, so a character that takes more than one byte can be split at it. The fonts carry only the characters a face shows, which a character past ASCII is not, so it draws as a box whether it is cut or whole, and walking back to a character boundary would cost bytes for nothing.

Parameters
dstThe buffer to write into.
srcThe string to copy, or NULL to empty the buffer.
sizeThe buffer's size, terminator included.

◆ cstring_fit_same()

bool cstring_fit_same ( const char * current,
const char * value,
size_t size )
inlinestatic

Whether copying value into a buffer would leave it reading exactly as it does now.

The copy keeps only what fits, so the incoming string is measured the same way. Comparing it in full instead would call a value longer than the buffer a change every single time, which for a settings field means re-reacting to a setting nobody touched.

Parameters
currentWhat the buffer holds now.
valueThe string that would be copied in. Must not be NULL.
sizeThe buffer's size, terminator included.
Returns
True when the copy would leave the buffer unchanged.

◆ cstring_is_clean()

bool cstring_is_clean ( const char * s,
uint16_t size )

Whether a byte range holds a string worth keeping.

Worth keeping means it ends inside its own room, it says something, and it carries no control bytes. Everything from 0x20 up counts as text, UTF-8 included, so a place name spelled with an umlaut is text and not damage. Getting that wrong is how a saved city turns back into the default one on the next boot.

Parameters
sThe bytes to look at.
sizeHow much room they have.
Returns
Whether it reads as a string.

◆ cstring_setting_is_clean()

bool cstring_setting_is_clean ( const char * s,
uint16_t size,
const char * default_str )

Whether a saved text setting reads as one, where an empty setting can be the real value.

A setting whose default is empty, such as a picker whose "None" is "", holds nothing on purpose, so empty is a value for it and not damage. Any other setting keeps the cstring_is_clean rule, since an empty one there is what a wiped blob reads like.

Parameters
sThe bytes to look at.
sizeHow much room they have.
default_strThe setting's default, or NULL for none.
Returns
Whether the setting is worth keeping.

◆ date_day_of_year()

int date_day_of_year ( int yday)

Which day of the year it is, counting January 1st as day 1.

Parameters
ydayThe day of the year counting January 1st as 0 (like tm_yday).
Returns
The day number from 1 to 366.

◆ date_days_in_month()

int date_days_in_month ( int year,
int mon0 )

How many days a month has, with February getting its leap day.

Parameters
yearThe full year (2026, not 126).
mon0The month indexed like tm_mon (0 January to 11 December).
Returns
The day count, or 0 for a month index that does not exist.

◆ date_days_left_in_year()

int date_days_left_in_year ( int year,
int yday )

How many days are left before the year ends, so December 31st reads 0.

Parameters
yearThe full year (2026, not 126).
ydayThe day of the year counting January 1st as 0 (like tm_yday).
Returns
The days remaining from 0 to 365.

◆ date_first_wday()

int date_first_wday ( int wday,
int mday )

The weekday the 1st of the month lands on, walked back from today's weekday.

Parameters
wdayToday's weekday, 0 Sunday to 6 Saturday.
mdayToday's day of the month, 1 to 31.
Returns
The weekday of the 1st, 0 Sunday to 6 Saturday.

◆ date_iso_week()

int date_iso_week ( int year,
int yday,
int wday )

The ISO 8601 week number, which always counts weeks from Monday.

A week near a year boundary can belong to the neighbouring year, so the number this returns is not always counted within the year you passed in. Pair it with date_iso_week_year to find out which year it belongs to, and total it against that year rather than this one.

Parameters
yearThe full year (2026, not 126).
ydayThe day of the year counting January 1st as 0 (like tm_yday).
wdayThe weekday, 0 Sunday to 6 Saturday.
Returns
The week number from 1 to 53.

◆ date_iso_week_year()

int date_iso_week_year ( int year,
int yday,
int wday )

The ISO 8601 year the week number belongs to, which is not always the calendar year.

Early January can fall in the previous ISO year and late December in the next one. Feeding this to date_iso_weeks_in_year gives the total that matches what date_iso_week returned, so a "week of total" readout can't end up with a number bigger than its own total.

Parameters
yearThe full year (2026, not 126).
ydayThe day of the year counting January 1st as 0 (like tm_yday).
wdayThe weekday, 0 Sunday to 6 Saturday.
Returns
The ISO year, either year, year - 1, or year + 1.

◆ date_iso_weeks_in_year()

int date_iso_weeks_in_year ( int year)

How many ISO 8601 weeks a year holds, either 52 or 53.

Parameters
yearThe full year (2026, not 126).
Returns
The week count, 52 or 53.

◆ distance_format()

void distance_format ( char * buffer,
size_t size,
int meters,
bool miles )

Format meters as "N.N MI" or "N.N KM" (rounded to tenth).

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
metersDistance in meters.
milesFormat as miles if true, otherwise km.

◆ distance_format_value()

void distance_format_value ( char * buffer,
size_t size,
int meters,
bool miles )

Format meters as "N.N" (rounded to tenth), no unit suffix.

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
metersDistance in meters.
milesFormat as miles if true, otherwise km.

◆ distance_unit()

const char * distance_unit ( bool miles)

Unit label for the current distance mode ("MI" or "KM").

Parameters
milesMiles if true, otherwise km.
Returns
Static unit string.

◆ duration_hm()

void duration_hm ( char * out,
size_t n,
int minutes )

Format minutes as a clock-style "H:MM", e.g. 445 -> "7:25".

The minutes are zero-padded, so seven hours and five minutes reads "7:05" and not "7:5". A negative count is nothing and reads "0:00".

Parameters
outOutput buffer.
nBuffer size.
minutesThe duration in minutes.

◆ duration_hm_compact()

void duration_hm_compact ( char * out,
size_t n,
int minutes )

Format minutes as "Xh Ym", dropping the hours once it is under one ("45m").

For a countdown, where the hours are only worth showing once there is at least one. A negative count is nothing and reads "0m".

Parameters
outOutput buffer.
nBuffer size.
minutesThe duration in minutes.

◆ fmt_hundredths()

void fmt_hundredths ( char * buffer,
size_t size,
int value )

Format hundredths as a two-decimal number, e.g. 26174 -> "261.74".

Prices and the like ride the wire as whole hundredths so they never touch a float. The sign is written out front rather than left to the whole part, which is what makes -50 read as "-0.50" instead of losing its minus to a whole part of zero.

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
valueThe number times 100.

◆ fmt_int_or_dash()

void fmt_int_or_dash ( char * buffer,
size_t size,
int value,
const char * fmt )

Format an integer, or "--" when there is no reading yet.

A negative value is the no-data marker the readouts share, so this is the one place that turns "I have nothing" into something to draw. The format takes exactly one int, so "%d" or "%d%%".

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
valueThe reading, or a negative number for no data yet.
fmtThe printf format applied when the value is there.

◆ fmt_pct_signed()

void fmt_pct_signed ( char * buffer,
size_t size,
int value )

Format hundredths as a signed two-decimal percent, e.g. 125 -> "+1.25%".

A gain takes a plus, a loss a minus, and flat takes neither. Same reason as fmt_hundredths for writing the sign out front: a change under one percent has a whole part of zero and would lose it otherwise.

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
valueThe percent times 100, signed.

◆ forecast_days_past()

uint8_t forecast_days_past ( int days_since_first,
uint8_t count )

How many of a daily strip's columns are days before today.

Parameters
days_since_firstWhole days from the strip's first day to today. Zero or less means the strip starts today or later.
countHow many columns the strip holds.
Returns
How many columns from the front are before today, at most count.

◆ forecast_hours_past()

uint8_t forecast_hours_past ( int32_t seconds_into_strip,
uint8_t step_hours,
uint8_t count )

How many of an hourly strip's columns are over.

A column is over once its whole step has gone by, so the column holding the current hour stays.

Parameters
seconds_into_stripSeconds from the start of the first column to now. Zero or less means the strip has not started yet.
step_hoursHours between columns.
countHow many columns the strip holds.
Returns
How many columns from the front are over, at most count.

◆ fraction_px()

int fraction_px ( int total,
int num,
int den )

A fraction of a length in pixels, e.g. how far along a bar a moment sits.

Parameters
totalThe full length.
numThe numerator of the fraction.
denThe denominator of the fraction.
Returns
total * num / den, or 0 for a denominator of zero so there is no divide by it.

◆ hour_latch_seed()

void hour_latch_seed ( HourLatch * latch,
time_t start )

Marks an hour as buzzed without buzzing it.

Parameters
latchThe latch to update.
startWhen the hour began, from hour_latch_start.

◆ hour_latch_start()

time_t hour_latch_start ( time_t now,
int tm_min,
int tm_sec )

When the local hour holding a moment began, as seconds since the epoch.

The minute and second have to come from breaking apart the same reading as now. Taking them from an older tick gives one hour two starts whenever a second turns over between the two reads, and the hour buzzes twice.

Parameters
nowThe moment, as seconds since the epoch.
tm_minThe local minute of now.
tm_secThe local second of now.
Returns
The start of the hour holding now.

◆ hour_latch_take()

bool hour_latch_take ( HourLatch * latch,
int tick_min,
time_t start )

Whether a tick should buzz the hour, and if so, marks the hour as buzzed.

Parameters
latchThe latch to check and update.
tick_minThe minute of the tick that is asking. Only minute 0 can buzz.
startWhen the current hour began, from hour_latch_start.
Returns
True the first time minute 0 of an hour asks, false otherwise.

◆ layout_has_any_block()

bool layout_has_any_block ( const char * layout,
int type_count )

Whether a layout string holds at least one placeable block.

One test covers the ways a layout can be nothing: the "0" marker a cleared grid is sent as, an empty string, and whatever a corrupt blob left behind. A record naming a module the catalogue does not have is not placeable either, so it does not count.

Parameters
layoutThe wire string, or NULL.
type_countHow many module numbers the caller's catalogue has, so anything at or past it is junk.
Returns
True when at least one record names a real module.

◆ layout_parse_int()

int layout_parse_int ( const char ** cursor)

Reads a run of digits and moves the cursor past them.

A number past LAYOUT_INT_MAX comes back as -1 rather than overflowing, since only a corrupt string holds one. Its digits are still skipped, so the cursor lands on the next field either way.

Parameters
cursorThe cursor, moved past the digits.
Returns
The value read, 0 when there are no digits, or -1 when it is too big.

◆ layout_role_pick()

LayoutRole layout_role_pick ( bool quiet_on,
bool quiet_set,
bool night_on,
bool night_set )

Which layout wins. Forwards to role_pick.

Deprecated
Call role_pick. The arguments are the same.
Parameters
quiet_onTrue while Quiet Time is on.
quiet_setTrue when a layout is assigned to Quiet Time.
night_onTrue while the night schedule says it is night.
night_setTrue when a layout is assigned to night.
Returns
The role whose layout should be drawn.

◆ minute_window_first_slot()

int minute_window_first_slot ( time_t first_min,
time_t end_min,
int minutes )

Where the first of a run of back to back minute records lands in a window.

The window's last slot holds end_min and each slot before it holds the minute before. A record older than the window lands below zero, one newer than its end lands past the last slot, and the caller skips both.

Parameters
first_minThe minute the first record belongs to, as minutes since the epoch.
end_minThe minute the window's last slot holds, the same way.
minutesHow many slots the window has.
Returns
The slot for the first record, which can fall outside 0 to minutes - 1.

◆ moon_age_sec()

int32_t moon_age_sec ( time_t utc)

How far into the current moon cycle we are, in seconds.

Parameters
utcThe time to read, as a UTC timestamp.
Returns
Seconds since the last new moon (0 means new moon, up to MOON_SYNODIC_SEC).

◆ moon_days_to_phase()

int moon_days_to_phase ( time_t utc,
bool to_full )

How many days until the next full or new moon, rounded to the nearest whole day.

Within half a day of the moon, before or after, it counts as now.

Parameters
utcThe time to read, as a UTC timestamp.
to_fullTrue counts to the next full moon, false to the next new moon.
Returns
The days to wait, 0 to 29, where 0 means it is about now.

◆ moon_glyph_index()

int moon_glyph_index ( time_t utc,
int count )

Picks one of count evenly spaced phase icons. It wraps back round at the end.

Parameters
utcThe time to read, as a UTC timestamp.
countHow many phase icons there are.
Returns
The icon to show (0 means new moon, or when count is not positive).

◆ moon_illumination_pct()

int moon_illumination_pct ( time_t utc)

How lit up the moon looks, from 0 to 100. This is a rough straight-line guess, so it reads 0 at new moon and 100 at full.

Parameters
utcThe time to read, as a UTC timestamp.
Returns
The lit fraction from 0 to 100.

◆ moon_phase_name()

const char * moon_phase_name ( time_t utc)

Short name for the current phase (one of the eight main phases).

Parameters
utcThe time to read, as a UTC timestamp.
Returns
The phase name.

◆ ms_until_next_beat()

uint32_t ms_until_next_beat ( int32_t ms_into_bmt_day)

Calculates milliseconds until the next .beats boundary.

A single beat is exactly 86,400 milliseconds. This is useful for scheduling timers to trigger exactly when the beat rolls over.

Parameters
ms_into_bmt_dayMilliseconds elapsed since Biel Mean Time (UTC+1) midnight.
Returns
Milliseconds remaining until the next beat boundary (1 to 86400).

◆ night_schedule_active()

bool night_schedule_active ( int mode,
int now,
int rise,
int set,
int fixed_start,
int fixed_end,
bool have_night )

Whether the night window is open, whichever way the schedule is set.

Following the sun needs both a sunrise and a sunset to work from. When either is missing this falls back to the fixed pair rather than giving up, so a watch that has never been told where it is still does something sensible, and the two dials the user set earn their keep twice.

Parameters
modeA NightSchedMode. Anything out of range reads as off.
nowMinutes past midnight.
riseSunrise, or -1 for no reading.
setSunset, same.
fixed_startThe user's own start, used by the fixed mode and as the solar fallback.
fixed_endAnd their end.
have_nightTrue when there is actually a night look to switch to.
Returns
True when the night look should be the one showing.

◆ number_group()

void number_group ( char * buffer,
size_t size,
int value )

Format an integer with apostrophe thousands separators (e.g. 20358 -> "20'358").

Negative values keep their leading sign. The Swiss-style apostrophe keeps big counts readable on the small display.

Parameters
[out]bufferOutput buffer.
sizeBuffer size.
valueThe value to format.

◆ pct_of()

int pct_of ( int value,
int goal )

How far along a goal is, from 0 to 100.

Caps at 100 so a beaten goal fills a bar rather than overflowing it, and answers 0 rather than dividing by a goal of zero.

Parameters
valueHow much is done so far.
goalWhat is being aimed at.
Returns
The progress from 0 to 100.
Note
A value below zero is the readouts' no-data marker, and this answers 0 for it, same as it does for a real zero. A caller that needs to tell "nothing yet" apart from "none of it done" has to check before it asks.

◆ plot_y()

int plot_y ( int y0,
int height,
int lo,
int hi,
int value )

Map a reading to its pixel row in a plot, with the low end at the bottom.

The window runs from lo at the bottom row to hi at the top, so a bigger reading sits higher up the panel. A reading outside the window is pinned to the nearer edge first, so it never lands on a row outside the plot area.

Parameters
y0The top row of the plot area.
heightThe plot area's height in rows.
loThe value at the bottom of the window.
hiThe value at the top of the window.
valueThe reading to place.
Returns
The pixel row for the reading. A window with no height (hi <= lo) gives the bottom row.

◆ read_i16_le()

int16_t read_i16_le ( const uint8_t * p)
inlinestatic

Reads a little-endian signed 16-bit value out of a byte pair.

Parameters
pThe first of the two bytes.
Returns
The value the pair encodes.

◆ read_i32_le()

int32_t read_i32_le ( const uint8_t * p)
inlinestatic

Reads a little-endian signed 32-bit value out of four bytes.

Parameters
pThe first of the four bytes.
Returns
The value the four bytes encode.

◆ role_pick()

Role role_pick ( bool quiet_on,
bool quiet_set,
bool night_on,
bool night_set )

Which role wins, given what is firing and what has something behind it.

Quiet Time sits above the night schedule on purpose. Someone who has set both is saying they want a quieter screen while the watch is quiet, and that holds whether it happens to be dark out or not.

A trigger with nothing assigned is skipped rather than honoured, so the answer walks down to the next one that has something to show and lands on the day role if nothing else does. That is what lets someone use one of these without filling in the other.

Parameters
quiet_onTrue while Quiet Time is on.
quiet_setTrue when something is assigned to Quiet Time.
night_onTrue while the night schedule says it is night.
night_setTrue when something is assigned to night.
Returns
The role whose look should be drawn.

◆ rotate_index()

int rotate_index ( int year,
int yday,
int hour,
int minute,
int interval_min,
int count )

Which of count is up now, advancing by one every interval_min minutes and wrapping round at the end.

The answer holds steady through a slot and steps to the next when the slot turns over, so a screen drawn from it only changes on a boundary. It is 0 for a count of one, and for an interval or a count that is not positive, which is the safe nothing-to-rotate answer.

It counts the year as a round 366 days, so the one boundary at a non-leap new year jumps by more than a single step and can, for a count that divides the jump, land on the same one twice. For a shuffle that one ragged boundary a year does not matter.

The time comes in as the same fields a struct tm carries, so the caller hands them straight from its broken-apart clock.

Parameters
yearYears since 1900, as tm_year.
ydayDays into the year, 0 to 365, as tm_yday.
hourThe hour, 0 to 23, as tm_hour.
minuteThe minute, 0 to 59, as tm_min.
interval_minHow many minutes each one is shown for.
countHow many there are to rotate through.
Returns
The index that is up, from 0 to count - 1.

◆ segment_width()

int segment_width ( int total,
int gap,
int count )

The width of one cell when a strip is carved into evenly spaced segments.

The gaps sit between the cells, so there is one fewer gap than there are cells. The division truncates, which leaves a few pixels of remainder for the caller to centre or absorb.

Parameters
totalThe strip's full width.
gapThe space between two cells.
countHow many cells there are.
Returns
The width of one cell, or 0 for a count of zero so there is no divide by it. Can come back below 1 when the cells do not fit, which the caller is expected to notice.

◆ segments_filled()

int segments_filled ( int level,
int segments )

How many cells of a gauge a percentage lights, rounding up so any charge lights one.

The level is pinned to 0..100 first, so a stray reading cannot light more cells than there are. Rounding up means 1% still shows a cell and 100% fills them all.

Parameters
levelThe fill from 0 to 100.
segmentsHow many cells the gauge has.
Returns
The number of lit cells, from 0 to segments.

◆ series_max_u16()

int series_max_u16 ( const uint16_t * values,
int count )

The largest value in a series, for scaling a bar strip against its busiest slot.

Parameters
valuesThe readings.
countHow many there are.
Returns
The largest value, or 0 for an empty series.

◆ series_range()

int series_range ( const uint8_t * samples,
int count,
uint8_t no_sample,
int * lo,
int * hi,
int * last )

Walk a series of samples for its low, high, and most recent reading.

A slot holding the no-sample value is skipped, so a gappy history still reads its real values. The outputs are filled only when there is at least one real sample. With none they are left as the caller set them, so a caller that seeds them and checks the count first cannot read a value that was never there.

Parameters
samplesThe readings, oldest first so the last real one is the most recent.
countHow many slots there are.
no_sampleThe value that marks an empty slot.
[out]loReceives the lowest real reading.
[out]hiReceives the highest real reading.
[out]lastReceives the most recent real reading.
Returns
How many real samples there were.

◆ solar_day_progress()

int solar_day_progress ( int rise,
int set,
int now )

How far through the daylight it is, from 0 at sunrise to 100 at sunset.

Parameters
riseSunrise, minutes past midnight, or -1 for no data.
setSunset, minutes past midnight, or -1 for no data.
nowThe clock, minutes past midnight, or -1 for no data.
Returns
The progress from 0 to 100, or -1 when it is night or there is no data, so the caller can hide the sun rather than pin it to an end.

◆ solar_next_event()

int solar_next_event ( int rise,
int set,
int now,
bool * is_sunrise )

Minutes until the next sun event, and which one it is.

Sunrise while it is dark, sunset while the sun is up, and tomorrow's sunrise once it has set, wrapping midnight the same way the night progress does.

Parameters
riseSunrise, minutes past midnight, or -1 for no data.
setSunset, minutes past midnight, or -1 for no data.
nowThe clock, minutes past midnight, or -1 for no data.
is_sunriseReceives whether the next event is a sunrise. Untouched when there is no data.
Returns
Minutes until the event, or -1 when there is no data.

◆ solar_night_progress()

int solar_night_progress ( int rise,
int set,
int now )

How far through the night it is, from 0 at sunset to 100 at the next sunrise.

The night usually straddles midnight, so the span runs from sunset round to the next sunrise.

Parameters
riseSunrise, minutes past midnight, or -1 for no data.
setSunset, minutes past midnight, or -1 for no data.
nowThe clock, minutes past midnight, or -1 for no data.
Returns
The progress from 0 to 100, or -1 when it is day or there is no data, so the caller knows to draw the sun instead of the moon.

◆ step_hours_settled()

int step_hours_settled ( int cur_hour,
int cur_min )

How many whole hours have settled, meaning they can never gain another step.

The hour in progress never counts, nor does the hour just gone while the clock is on its first minute: the watch writes a minute's record at the top of the minute after it, below the watchface in priority, so on the rollover turn that last record is usually not there yet.

Parameters
cur_hourThe hour now, 0 to 23.
cur_minThe minute within that hour, 0 to 59.
Returns
How many hours have settled, so buckets below it are final.

◆ stock_wire_decode()

bool stock_wire_decode ( const uint8_t * buf,
uint16_t len,
StockStrip * out )

Unpacks a watchlist off the wire.

Layout is [count] then per slot [ok][price int32 LE cents][pct int16 LE hundredths][symLen] [sym bytes]. A count past what the strip holds is pinned to it rather than walking off the end.

Nothing is written to out until the whole run reads clean, so a message that stops halfway leaves the caller's last good watchlist alone rather than half replacing it.

Parameters
bufThe raw wire bytes.
lenHow many bytes there are.
[out]outReceives the watchlist. Untouched unless this returns true.
Returns
Whether the run read clean.

◆ text_to_upper()

void text_to_upper ( char * s)

Uppercase an ascii string in place.

Parameters
sThe string to uppercase.

◆ tide_level()

int tide_level ( int32_t minutes)

How far in the tide is, from 0 at dead low to 100 at high water.

Eased at both ends rather than run at a constant rate, because a real tide stands almost still around the turn and moves fastest halfway through. Water creeping up the sand and then pausing is the part that reads as tidal.

Parameters
minutesA running minute count, e.g. the epoch in minutes. Negative values are fine.
Returns
The level from 0 to 100.

◆ tide_rising()

bool tide_rising ( int32_t minutes)

Whether the tide is on its way in.

Parameters
minutesA running minute count, on the same base as tide_level.
Returns
True while it is flooding, false while it ebbs.

◆ timeband_clip()

bool timeband_clip ( TimeBand band,
time_t window_epoch,
time_t start,
time_t end,
TimeBandSpan * out )

Clip an absolute span onto the window.

Both ends are epochs, which is what an agenda carries, and the window's own first minute is given as one too, so an event from yesterday or from next week falls outside rather than being wrapped onto today.

A span with no length still comes back a minute wide, so a moment someone pinned to their calendar still draws instead of vanishing.

Parameters
bandThe window.
window_epochThe epoch of the window's first minute.
startThe span's start.
endThe span's end. An end before the start is read as having no length.
outReceives the clipped span. Untouched unless this returns true.
Returns
Whether any of the span landed inside the window.

◆ timeband_clip_daily()

int timeband_clip_daily ( TimeBand band,
int from_min,
int to_min,
TimeBandSpan * out,
int max_out )

Clip a daily span, one that repeats every day like the daylight, onto the window.

A window running past midnight can see the same daily span at both of its ends, which is why this hands back pieces rather than one span. A span whose end is at or before its start is read as wrapping midnight itself, so a polar winter's short daylight and a midsummer night's short darkness both work without the caller knowing which it has.

Parameters
bandThe window.
from_minThe daily span's start, minutes past midnight, or -1 for no reading.
to_minIts end, same. A -1 on either side gives no pieces.
outReceives the pieces, earliest first.
max_outHow many out holds. Two is enough for any window of a day or less.
Returns
How many pieces were written, 0 to 2.

◆ timeband_from_hour()

TimeBand timeband_from_hour ( int base_hour,
int span_min )

A window starting at the top of a given hour.

The hourly forecast carries its own base hour, so feeding it in here lands the strip's columns on even fractions of the axis instead of somewhere arbitrary along it.

Parameters
base_hourThe hour the window opens on, wrapped into 0..23.
span_minHow long it runs, pinned to 1..1440.
Returns
The window.

◆ timeband_offset()

int timeband_offset ( TimeBand band,
int minute_of_day )

How far into the window a minute of the day falls.

Parameters
bandThe window.
minute_of_dayThe moment, minutes past midnight.
Returns
Minutes past the window's start, 0 to span_min - 1, or -1 when it sits outside. The far end is outside: a window is half open so two of them laid end to end share no minute.

◆ timeband_pos()

int timeband_pos ( TimeBand band,
int length,
int minute_of_day )

Where a minute of the day lands along an axis of a given length.

Parameters
bandThe window.
lengthThe axis length in whatever the caller measures it in.
minute_of_dayThe moment, minutes past midnight.
Returns
0 to length - 1, or -1 when the moment sits outside the window or the length is not positive.

◆ timeband_pos_offset()

int timeband_pos_offset ( TimeBand band,
int length,
int offset_min )

The same mapping for a value already measured from the window's start.

This is the one the drawing uses, since everything clipped comes back as an offset. It takes the far end too, so a span ending exactly at the window's end reaches the end of the axis rather than stopping a unit short.

Parameters
bandThe window.
lengthThe axis length.
offset_minMinutes past the window's start, 0 to span_min.
Returns
0 to length, or -1 when the offset is outside 0..span_min or the length is not positive.

◆ timeband_rolling()

TimeBand timeband_rolling ( int now_min,
int span_min,
int lead_min )

A window that puts a given moment a fixed way into it.

Parameters
now_minThe moment to place, minutes past midnight.
span_minHow long the window runs, pinned to 1..1440.
lead_minHow far into it the moment sits, pinned to 0..span_min - 1. The window's last minute is the furthest in, since its far end belongs to whatever comes next.
Returns
The window, its start wrapped into 0..1439.

◆ timeband_window_epoch()

time_t timeband_window_epoch ( TimeBand band,
time_t now,
int now_min )

The epoch the window's first minute sits at.

It counts back from now rather than forward from midnight. A day the clocks change on is an hour short or long, so midnight plus the window's start would land an hour off, while the minutes between the window's start and now are the same real minutes on any day. A rolling window that crossed midnight opened yesterday, which counting back finds without being told.

A window that has not opened yet, such as a forecast strip starting at the next hour, is the one coming up later today.

The clock is handed over both ways rather than worked out here, so this needs no localtime and tests off the watch.

Parameters
bandThe window.
nowThe clock as an epoch.
now_minThe same moment on the wall clock, minutes past midnight.
Returns
The epoch of the window's first minute.

◆ weather_daily_decode()

bool weather_daily_decode ( const uint8_t * buf,
uint16_t len,
WeatherDaily * out )

Unpacks a 7-day forecast off the wire.

Layout is [count][base_weekday] then per column [code][max int16 LE][min int16 LE]. A count past what the strip holds is pinned to it rather than walking off the end.

Parameters
bufThe raw wire bytes.
lenHow many bytes there are.
[out]outReceives the strip. Untouched unless this returns true: everything that can turn a message away is settled before the first byte of it is written.
Returns
Whether the run read clean.

◆ weather_hourly_decode()

bool weather_hourly_decode ( const uint8_t * buf,
uint16_t len,
WeatherHourly * out )

Unpacks an hourly forecast off the wire.

Layout is [count][base_hour][step_hours] then per column [code][temp int16 LE]. A count past what the strip holds is pinned to it rather than walking off the end.

Parameters
bufThe raw wire bytes.
lenHow many bytes there are.
[out]outReceives the strip. Untouched unless this returns true: everything that can turn a message away is settled before the first byte of it is written.
Returns
Whether the run read clean.

◆ weather_reading_apply()

bool weather_reading_apply ( WeatherState * state,
const WeatherMessage * msg,
const WeatherClock * clock )

Put a message's readings into the kept weather.

A group that did not arrive leaves its readings alone. One that did replaces all of them, and a reading it left out becomes that reading's no-data value, -1 or WEATHER_NO_TEMP for the temperatures. The current temperature, the high and low, the feels-like, and the dew point are held to -99 to 199, and one sent as WEATHER_NO_TEMP stays no data. A failed fetch keeps the last good temperature and sky. A strip that does not read clean keeps the last good row, and a strip's temperatures are kept as they arrive until weather_reading_convert holds them to the same range.

Each strip that reads clean records where its first column starts, and the high, low, UV, and rain chance record the day they came, so the store can tell later what has gone by.

Parameters
stateThe kept weather, updated in place.
msgThe message.
clockThe watch's clock as the message landed. Its time is stamped as the last sync when anything was kept.
Returns
Whether anything was kept, which is when the face needs a redraw and a save.

◆ weather_reading_convert()

void weather_reading_convert ( WeatherState * state,
bool to_fahrenheit )

Convert every temperature in the kept weather to the other unit.

Runs when the wearer switches units, so the reading in hand matches the new letter until the phone's fetch in the new unit lands. Each value rounds to the nearest degree, and a reading with no data stays that way.

Parameters
stateThe kept weather, converted in place.
to_fahrenheitTrue to go from Celsius to Fahrenheit, false for the other way.

◆ weekday_short()

const char * weekday_short ( int wday)

The two-letter short name for a weekday, indexed like tm_wday and the phone's base_weekday (0 = Sunday through 6 = Saturday).

Parameters
wdayThe weekday, 0 (Sunday) through 6 (Saturday).
Returns
The short name ("SU".."SA"), or "" when the index is out of range.

◆ wind_bearing()

int wind_bearing ( const char * dir)

The bearing a 16-point compass abbreviation stands for.

Meteorological convention: the bearing is where the wind is coming from, so "N" is 0 and a northerly blows towards the south.

Parameters
dirThe abbreviation, e.g. "N", "ESE", "NW". Case insensitive.
Returns
Degrees from 0 to 337, or -1 when the string is not one of the sixteen points.

◆ wind_from_kmh()

int wind_from_kmh ( int kmh,
WindUnit unit )

Convert a km/h wind speed into the chosen unit.

Integer maths, truncated. An unknown unit falls back to km/h.

Parameters
kmhThe wind speed in km/h.
unitThe unit to convert into.
Returns
The wind speed in the chosen unit.

◆ wind_lean()

int wind_lean ( int bearing)

The east-west component of a wind, as a signed proportion.

Anything drawn from the side only shows the part of a wind that runs across the view: one blowing straight towards or away from the viewer has no sideways component at all, and one blowing along the view has the whole of it. Positive is eastward, which is the way a wind out of the west goes.

Parameters
bearingDegrees, or -1 for no data.
Returns
-100 (full westward) to 100 (full eastward), or 0 when there is no reading.

◆ wind_unit_label()

const char * wind_unit_label ( WindUnit unit)

The short label for a wind unit.

Parameters
unitThe unit to label.
Returns
"KM/H" / "MPH" / "KTS" / "M/S", km/h for an unknown unit.

◆ wire_cstring_terminated()

bool wire_cstring_terminated ( const char * s,
uint16_t length )
inlinestatic

Whether a string of length bytes carries its terminator inside itself.

A sender is supposed to include the terminator and nothing re-checks that it did. A string without one is not a string: reading it runs on past the end looking for a zero byte that belongs to something else, or to nothing at all.

Parameters
sThe first byte of the string.
lengthHow many bytes the string occupies, terminator included.
Returns
Whether it is safe to read as a C string.

◆ wire_int_or()

int32_t wire_int_or ( bool is_signed,
uint16_t length,
const uint8_t * value,
int32_t fallback )
inlinestatic

Reads a little-endian integer of exactly length bytes.

Parameters
is_signedWhether the value is signed. Decides only what the top bit means.
lengthHow many bytes the value occupies. 1, 2, and 4 are the widths a sender may use, and anything else is refused rather than guessed at.
valueThe first byte of the value.
fallbackReturned when the width is not one a sender may use, or there is no value.
Returns
The value, or fallback.

◆ wx_label_short()

void wx_label_short ( char * out,
size_t n,
const char * condition )

Writes the short word for a condition token, without its "_NIGHT" suffix.

Parameters
outWhere the word goes.
nThe room in out, terminator included.
conditionThe token, such as "PCLDY_NIGHT". NULL or empty writes "UNKNOWN".

◆ zone_setting_is_set()

bool zone_setting_is_set ( const char * value)

Whether the setting holds a zone at all.

An empty one is no zone rather than UTC with no name, so a panel shows dashes for it rather than a clock for a place nobody picked.

Parameters
valueThe setting, or NULL.
Returns
True when a zone is set.

◆ zone_setting_label()

const char * zone_setting_label ( const char * value)

The place's name, everything after the first comma.

A name can carry commas of its own, as in "Paris, Ile-de-France, France", so a caller that wants only the city cuts it at the next comma itself.

Parameters
valueThe setting, or NULL.
Returns
The name, or an empty string when the setting has none.

◆ zone_setting_offset()

int16_t zone_setting_offset ( const char * value)

How many minutes the zone is ahead of UTC, negative for one behind.

Parameters
valueThe setting, or NULL.
Returns
The offset in minutes, or 0 when no zone is set.