Power Budgeting for GPS Duty-Cycled Nodes

This page solves one concrete problem: building a defensible energy budget for a field gateway that wakes on a schedule to take a GNSS fix, process it, and transmit it, so the battery and solar panel can be sized before the node ships rather than discovered to be undersized after the first cloudy week. Within Edge Operations & Observability, and specifically as an application of power and duty-cycling for field nodes to the single most power-hungry peripheral most gateways carry, this is the arithmetic that turns a receiver datasheet and a fix-rate requirement into an amp-hour number. The deployment target is a u-blox- or similar-class GNSS module on an ARM Linux gateway, powered from a 3.7 V Li-ion or LiFePO4 pack topped up by a solar panel.

GNSS acquisition strategy selection rationale

A GNSS receiver’s current draw during acquisition is roughly constant regardless of how it acquires — what varies enormously is how long it draws that current, and that duration is entirely a function of what state its ephemeris and almanac data are in when it powers up. A cold start has no valid almanac, ephemeris, or last-known position, and must download a full navigation message from each satellite before it can compute a fix — commonly 30–60 s on a decent sky view, longer under foliage or urban multipath. A warm start has a recent position and almanac but stale ephemeris, cutting acquisition to roughly 15–35 s. A hot start, with ephemeris still valid (GPS ephemeris is valid for about 2–4 hours), can produce a fix in 1–5 s because the receiver already knows exactly where to look.

This is why duty-cycle interval and GNSS strategy are coupled decisions, not independent ones: a node that sleeps for 20 minutes wakes to a still-valid ephemeris and gets a hot start almost every cycle, while a node that sleeps for 6 hours forces a cold start on every wake regardless of receiver quality. Keeping the receiver’s own low-power backup rail alive across sleep (a few microamps, distinct from gating its main supply off entirely) is what preserves ephemeris validity across the sleep window and is very often the single highest-leverage power decision on the whole node — it can turn a 45-second, 45 mA acquisition into a 2-second one every single cycle.

Averaging stage currents into a per-cycle energy budget

The budgeting method is the same one used for any duty-cycled load: multiply each stage’s current by the time spent in that stage, sum across the cycle, and divide by the cycle length to get a mean current the battery actually has to sustain. Given nn stages with current IiI_i and duration tit_i over a cycle of length TT:

Iˉ=1Ti=1nIiti\bar{I} = \frac{1}{T}\sum_{i=1}^{n} I_i \, t_i

and the energy drawn per cycle at bus voltage VV is:

Ecycle=Vi=1nIitiE_{\text{cycle}} = V \sum_{i=1}^{n} I_i \, t_i

A representative five-minute cycle with a hot-start GNSS fix, a short compute burst, and a modem transmission looks like this in code — the same shape as the ledger in the parent guide, specialized to the GNSS stages:

from dataclasses import dataclass

BUS_VOLTS = 3.7

@dataclass
class Stage:
    name: str
    current_ma: float
    duration_s: float

def cycle_energy_and_mean_current(stages: list[Stage], cycle_s: float):
    """Return (mean_current_ma, energy_mwh) for one duty cycle.

    cycle_s must include the full sleep duration, not just the active
    stages -- sleep current dominates the mean on any well-tuned node.
    """
    mah_sum = 0.0
    for s in stages:
        mah_sum += s.current_ma * (s.duration_s / 3600.0)
    mean_current_ma = (mah_sum * 3600.0) / cycle_s
    energy_mwh = mah_sum * BUS_VOLTS
    return mean_current_ma, energy_mwh

# Hot-start GNSS fix (ephemeris valid), short compute burst, one TX
HOT_START_CYCLE = [
    Stage("gnss_hot_fix", 28.0, 2.5),
    Stage("compute", 210.0, 0.8),
    Stage("modem_tx", 420.0, 3.0),
    Stage("sleep", 20.0, 294.0 - (2.5 + 0.8 + 3.0)),
]

mean_ma, mwh = cycle_energy_and_mean_current(HOT_START_CYCLE, cycle_s=300.0)
# mean_ma ~= 21.8 mA, mwh ~= 0.045 mWh energy accounted per active stage set

Run the same function with a gnss_cold_fix stage at 45 mA for 50 s substituted in, and the mean current for that cycle jumps by roughly 7 mA — small in isolation, but multiplied across a day of cycles that fall back to cold starts (because the backup rail was gated off, or the node sat off for longer than the ephemeris validity window) it is often the difference between a battery that lasts the week and one that does not.

Hot start versus cold start GNSS energy per cycle Two cards compare a hot-start cycle, where ephemeris was preserved by keeping the receiver backup rail alive and acquisition takes a few seconds at moderate current, against a cold-start cycle, where the backup rail was gated off, ephemeris is stale, and acquisition takes tens of seconds at similar current, multiplying the energy spent on the fix alone. Hot start • ephemeris preserved across sleep • ~28 mA for ~2-5 s • backup rail: low microamps • fix energy: well under 1 mWh • works if cycle < ephemeris validity Best when: cycle under ~2-4 h Cold start • backup rail gated off, or long sleep • ~45 mA for 30-60 s • full almanac + ephemeris download • fix energy: several mWh • dominates the cycle's energy total Best when: unavoidable — size for it
Preserving ephemeris across sleep is usually cheaper than any compute-side optimization on the same node.

Sizing the battery and panel from the mean current

Once Iˉ\bar{I} is known for a representative mix of hot and cold starts, battery sizing for a target autonomy of dd days with no solar input at all — the worst-case reserve — follows directly, with DoD\text{DoD} the depth of discharge the chemistry tolerates (commonly 0.8 for LiFePO4, lower for generic Li-ion if cycle life matters):

Cbattery (Ah)=IˉA×24×dDoDC_{\text{battery (Ah)}} = \frac{\bar{I}_{\text{A}} \times 24 \times d}{\text{DoD}}

Solar panel sizing works from the same mean current, scaled up for charge-controller and battery round-trip losses (commonly 15–25% combined) and divided by the site’s worst-month peak-sun-hours figure, HH, rather than an annual average:

Ppanel (W)=IˉA×Vbus×24H×(1ηloss)P_{\text{panel (W)}} = \frac{\bar{I}_{\text{A}} \times V_{\text{bus}} \times 24}{H \times (1 - \eta_{\text{loss}})}

A node averaging 22 mA at 3.7 V (about 81 mW continuous) in a location with 3 peak-sun-hours in its worst month and 20% combined losses needs roughly a 1.9 W-rated panel — trivially small, which is the point: most of this exercise is not about the panel, it is about not fooling yourself with an optimistic Iˉ\bar{I} that assumes every cycle gets a hot start.

Constraint validation table

Constraint Expected impact Mitigation built into the budget
GNSS backup-rail current A few µA to keep RAM/ephemeris alive across sleep is easy to overlook against a mA-scale main budget, but omitting it forces cold starts Model the backup rail as its own always-on stage in cycle_energy_and_mean_current, not folded into “sleep”
Ephemeris validity window (~2-4 h) A duty cycle longer than this guarantees cold starts regardless of backup-rail care Choose the duty cycle with ephemeris validity as an upper bound, or accept and budget for cold-start energy explicitly
Cold-start frequency assumption Sizing purely on hot-start numbers understates real-world energy by several times on any day with poor sky view or a missed cycle Budget a realistic cold-start fraction (field data, not zero) into Iˉ\bar{I}, not just the nominal case
Worst-month solar harvest Sizing the panel on an annual average leaves the node undersized for exactly the season it needs the most margin Use the site’s worst-month peak-sun-hours figure in the panel sizing formula, never the annual mean
Battery depth of discharge Discharging past the chemistry’s rated DoD accelerates capacity fade, shortening node lifetime even if daily energy balances Size CbatteryC_{\text{battery}} against the manufacturer’s DoD figure, and treat autonomy days as a floor, not a target to shave

Gotchas and edge cases

  • Ephemeris validity is a range, not a constant. Different GNSS constellations and receiver firmware quote different ephemeris lifetimes; treat the commonly cited 2–4 hour window as a starting assumption to verify against the specific module’s datasheet, not a universal constant to hardcode.
  • Fix rate versus runtime is a real trade, not a free lunch. Halving the duty cycle to double fix rate roughly halves runtime for the same battery, because sleep current — not the fix itself — dominates the mean at low duty ratios; check the constraint table in the parent guide before assuming a faster fix rate is “free” because each individual fix is cheap.
  • Cold weather derates both sides of the equation. Li-ion capacity drops significantly below freezing just as GNSS acquisition can take longer under attenuated signal through ice or snow cover on the antenna, compounding two effects in the same direction during exactly the season margin matters most.
  • A weak antenna extends every acquisition, not just cold starts. A marginal antenna placement inflates the acquisition duration tit_i for hot, warm, and cold starts alike; re-measure actual acquisition time on the deployed antenna and enclosure, not the receiver’s open-sky datasheet figure.
  • Modem and GNSS current can overlap and add, not average. If the schedule ever lets GNSS acquisition and modem transmission run concurrently to save wall-clock time, the combined instantaneous current can exceed what the power supply’s peak rating tolerates even though the energy budget looks fine; check peak current headroom separately from the mean-current budget above.

Integrating with the duty-cycle scheduler

The stage list feeding cycle_energy_and_mean_current should be produced by the same scheduler that decides the duty cycle itself, so that a change to next_duty_seconds or an adaptive stretch during a low-battery period is reflected in the energy model rather than drifting out of sync with it — the scheduling mechanics for that loop are covered in wake-process-sleep scheduling for solar nodes. In practice, log each cycle’s actual stage durations (measured, not assumed) alongside the fuel gauge’s reported state of charge, and periodically recompute Iˉ\bar{I} from the trailing week of real cycles rather than trusting the bring-up estimate indefinitely — a receiver’s real-world cold-start rate on a specific antenna and sky view is usually the single biggest unknown in the whole budget, and it only becomes known after the node has been in the field for a while.