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 stages with current and duration over a cycle of length :
and the energy drawn per cycle at bus voltage is:
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.
Sizing the battery and panel from the mean current
Once is known for a representative mix of hot and cold starts, battery sizing for a target autonomy of days with no solar input at all — the worst-case reserve — follows directly, with the depth of discharge the chemistry tolerates (commonly 0.8 for LiFePO4, lower for generic Li-ion if cycle life matters):
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, , rather than an annual average:
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 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 , 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 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 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 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.
Related
- Power & Duty-Cycling for Field Nodes — the wake-process-sleep loop and constraint mapping this GNSS budget plugs into.
- Wake-Process-Sleep Scheduling for Solar Nodes — the scheduler that determines the actual stage durations this budget depends on.