Where turn-by-turn stops
Even with the right door, a guided route only knows the walking network, and near a station that network runs out before the door does. So for a walk to a large venue, Pathfinder changes how it talks for the last stretch. FinalApproach begins when Dad is within 100 m of the door in a straight line, or when the guided route has 100 m or less left, because a route that ends at the kerb is shorter than the walk in.
From that fix on, the trip says no turns and no arrival line, whoever guides it. The app's ArrivalCoach sits beside the trip, not inside it, and hands the trip one closure, NavigationDependencies.finalApproach. Both trip children check it before they speak: NavigationSession, which Pathfinder's own engine guides on Apple's and Kakao's routes, and FeedGuidedTrip, which follows Google's Navigation SDK. They still log the announcement and the route's end, so the drive log keeps what the map thought. Google's own voice, which plays outside Pathfinder's player, is gated by the same condition. And the app hands each fix to the coach before the trip, so on the fix that begins the stretch, the trip already holds its turns.
What it says instead
The first line is "이제부터 입구까지 말로 안내할게", then the distance ("입구까지 60미터"), the side, Pippa's note if her door has one, and the level if Dad is on the wrong one. The side is one of eight 45° sectors around the way Dad faces, from "앞쪽이야" through "오른쪽 앞이야" to "왼쪽 앞이야". Facing comes from the GPS course while he moves at 1.2 m/s or faster, and from the compass when he slows or stops, because a person at a station turns on the spot.
After that the stretch stays quiet unless something changed: at least 12 s since the last line, and either 25 m closer or the door now on another side (that one waits twice as long, 24 s). A change of level is the exception: the moment the barometer's verdict changes on the way in, it is said at once, whatever the clock says, and the 12 s count restarts from it. Distances come in ten-metre steps below 75 m; from 75 m to 100 m they are said as "100미터", which is why the kit's test hears 83 m as "입구까지 100미터". Every one of these lines but one is in the closed vocabulary Pathfinder prefetches before a walk: the fixed lines, all eight sides, one to three floors up or down, and "입구까지" from 10 to 100 m. The code's comment gives the reason in one clause: a walk to a station has no network to spare at its door. The one exception is Pippa's note. It is free text, so no vocabulary can hold it in advance: the first line of a stretch that carries an uncached note is spoken whole in the system's Korean voice (two voices never share a line), and the note is fetched for next time.
The barometer's verdict
GPS cannot tell the street from the concourse two levels up. Air pressure can. LevelSensor wraps Core Motion's CMAltimeter: a relative altitude (metres changed since it started) and, on phones that have it, an absolute altitude above sea level with an accuracy. FinalApproach.measureLevel then decides:
- If the door carries an altitude Dad measured when he confirmed it, and the phone's absolute altitude is accurate to 6 m or better, compare the two directly.
- Otherwise, if the door has a
level, expectlevel × 4 mof climb measured from the relative altitude where the stretch began. - Otherwise the level is unknown.
A gap within 2.5 m is on the door's level. Anything else is rounded to whole floors, at least one: "아직 아래층이야. 두 층 올라가야 해" and "계단이나 에스컬레이터를 찾아". Why 4 m? The config says it plainly: a station's floor is tall, and 4 m keeps "one level" honest between an office's 3 m and a concourse's 6 m.
One doc-versus-code note. LevelSensor's own comment says it runs only while a walk is on its last stretch. The code starts it when a coached walk starts (inside startCoach, right after the coach is created) and stops it when the trip closes, which is what the commit message and invariant 21 say. The stretch's beginning is when the baseline is taken, not when the sensor starts.
The wrong level is not arrival
This is the rule Atocha wrote. When Dad reaches the door horizontally (15 m, plus the fix's own uncertainty up to another 15) but the barometer says he is on the wrong level, FinalApproach emits wrongLevel, not reached: "입구 근처인데 층이 달라", how many floors, and the stairs line, repeated every 30 s while he stands there. The trip's own arrival does not end the walk while the coach holds it. When the climb brings him within 2.5 m of the door's height, the next fix is reached, Pippa says "입구에 왔어, 아빠", and the walk ends a few seconds later. With no level known at all, reaching the door is arrival, as before. And Dad's "여기가 입구야" ends it whatever the instruments say.
The sketch below replays the kit's atochaTheStreetBelowTheDoorIsNotArrival test with the same numbers: silent at 300 m, the stretch begins at 95 m two floors below, "wrong level" under the door, silence ten seconds later, "wrong level" again 35 s later, and arrival only after 7.5 m of climb.
What is not proven yet
Every part of this is built and tested, and none of the level logic has met a real multi-level station yet. The architecture's own register says: built and tested on a Mac and the simulator, which has no barometer, with the first real use pending at a multi-level station. The commit that landed it was validated by tests alone (23 arrival tests in the kit, 4 coach tests in the app). Named honestly, the open questions are:
- 4 m a floor is an assumption. It was chosen, not measured at any station. A concourse with 8 m ceilings will read as two floors.
- The baseline is wherever the stretch began. If that point is already on a ramp, a bridge or a different level from the street the door's
levelcounts from, every floor count is off by that much. - Measured altitudes exist only for Dad's own doors, and only on phones with absolute altitude reading to 6 m.
- OpenStreetMap's levels are thin. Most doors carry none, and a value like
0;-1is ignored. - There are no indoor floors. Pathfinder does not route through the inside of a building or know its floor plan; it brings Dad to a door on that door's level. Core Location can report a
floor, but only where a venue provides indoor positioning, and Pathfinder does not use it.
The drive log is ready for the day it is proven: every approach event is logged with the barometer's relative altitude beside it, so the first real station walk will leave the evidence behind.
Looking back over the whole quest
The last track is a good mirror for the other seven, because nothing in it is new machinery. It is the same few ideas meeting a building:
- An answer without its facts is worse than none (track 1). A "yes" with no location became a navigator; an "arrived" with no level became this track.
- One trip, whoever guides (track 2). The coach sits beside any
GuidedTrip, and one closure hushes Apple's, Kakao's and Google's trips alike. - Rules as pure values, tested on a Mac (track 3).
FinalApproachis fed fixes and read for events, exactly likeGuidanceEngine, and Atocha is a unit test. - A closed vocabulary, cached before it is needed (track 4). Every approach line but Pippa's free-text note is a known phrase in Pippa's voice, fetched before the walk.
- Walking is its own mode (track 5), and only walks get a door.
- What a simulator cannot show (track 6). The watch's real gaps appeared on a real walk; the barometer's will too.
- Pippa's pins carry what she knows (track 7), now including the way in.
And over all of it, the rule that has held since the first day: Dad's word outranks the instruments, Pippa's included.