Three Times per Turn
A good co-driver does not narrate. She tells you about a turn early enough to change lanes, again when it is close, and once more right before it. Pathfinder encodes exactly that as AnnouncementStage: early, prepare and imminent, ordered so the engine can compare them.
The trigger distances depend on speed. In town the plan is 1,000 m, 300 m and 90 m. Once the smoothed speed reaches 19.4 m/s (70 km/h) the engine switches to the highway plan, 2,000 m, 1,000 m and 300 m, because at 100 km/h the town plan's 300 m warning would leave about eleven seconds. The speed it uses is smoothed (each new reading counts 40 %, the previous estimate 60 %), so one noisy sample pulls the estimate only part of the way toward itself. That damps jitter; it does not forbid a switch. Near the threshold a single reading can still tip the plan: an estimate of 19 m/s and one reading of 21 m/s make 19.8 m/s, and there is no hysteresis band holding it back. What makes such a flip cheap is the rule in the next section: a stage once spoken is never spoken again, so a flip can bring the next line forward but can never make Pippa repeat one.
The early and prepare lines carry a distance; imminent carries none. It is said as "잠시 후" (soon) and goes out at urgent priority, which lets it interrupt a less urgent line already playing. A real line from the voice tests: 300미터 앞에서 + 백마로로 + 우회전해, three cached chunks of Pippa's voice.
Once Each, and Only the Latest
The engine keeps a small map, announced, from step index to the highest stage already spoken. On each matched fix it finds the next spoken step, works out which band the car is in, and speaks only if that band is higher than what was already said. Three behaviours fall out of that one rule, and each has a test:
- Each stage once. A town drive past the turn produces early, prepare, imminent for it, and nothing twice.
- A gap speaks only the latest. If a tunnel swallows the fixes and the next one arrives 80 m before the turn, the car has crossed two triggers at once. Only
imminentis spoken. Saying "1 km ahead" at 80 m would be worse than silence. - Starting inside a band does not repeat. The start line ("안내 시작할게 …") already names the next maneuver with its real distance, so when the drive begins 600 m before a turn, that band is marked as spoken; prepare and imminent still follow.
Right after a maneuver, the next one may already be inside its early band. It is spoken at once with its real distance, the doc comment's "then, in 600 m ...".
Chaining Close Turns
Two maneuvers 100 m apart cannot each get their own warning: by the time the first is done, the second is already happening. So at prepare or later, if the following maneuver lies within chainDistance (180 m) of this one, it rides along with "이어서" (then): 잠시 후 우회전해 이어서 좌회전해. The early stage never chains, because 1 km out the second turn is not yet useful information.
What Is Said Is Not What Is Measured
At the moment the prepare trigger fires the car might be 287 m from the turn, because fixes arrive once a second and the car moves 12 m between them. Nobody wants to hear "287 m". SpokenDistance.from(metres:) rounds what is measured into what is said:
| Measured | Said in | Examples |
|---|---|---|
| under 75 m | 10 m steps, at least 10 | 43 → 40미터 |
| 75 to under 950 m | 100 m steps, 100 to 900 | 149 → 100미터, 287 → 300미터 |
| 950 to under 2,900 m | half-kilometres, at least 1 | 1,260 → 1.5킬로미터 |
| 2,900 m and up | whole kilometres | 7,420 → 7킬로미터 |
| NaN, infinite or a sentinel | "soon" | never converted |
That last row is a crash avoided, not a style choice: converting a provider's "no route" sentinel or a NaN to Int would trap. And the rounding has a second payoff. Because the plan can only ever speak a small, closed set of distances, the composer lists "the fixed lines and every distance the plan can speak" and Pippa's voice for all of them can be fetched before the first drive.
Arrival, and the Eight Seconds After
The destination is a step too, so it gets its own early, prepare and imminent lines. The engine declares arrived when what remains of the line is within 35 m plus the fix's accuracy (counted up to 25 m), or when the car is past 90 % of the line and within 35 m of the destination pin itself, because routes end at the curb and the pin may sit inside a car park. It says arrived once; after that update returns nothing.
The app then ends the drive eight seconds later, once, and only for the trip that arrived. It used to schedule an end on every fix after arrival, and a late one could end the next drive or clear a new preview, a bug a code review caught. The engine was right all along; the shell had to learn to act on an event exactly once.