턴바이턴이 멈추는 곳
문을 제대로 골라도 안내 경로는 도보망밖에 몰라. 역 근처에선 그 도보망이 문보다 먼저 끝나. 그래서 큰 건물로 가는 도보라면 Pathfinder는 마지막 구간에서 말하는 방식을 바꿔. FinalApproach는 아빠가 문에서 직선으로 100 m 안에 들어오거나, 아니면 안내 경로가 100 m 이하로 남았을 때 시작해. 연석에서 끝나는 경로는 실제로 걸어 들어가는 길보다 짧으니까.
그 측위부터는 누가 안내하든 트립이 회전도 도착 문구도 말 안 해. 앱의 ArrivalCoach는 트립 안이 아니라 옆에 앉아서, 트립한테 클로저 하나를 건네. NavigationDependencies.finalApproach. 트립 종류 둘 다 말하기 전에 이걸 확인해. Apple이랑 카카오 경로를 Pathfinder 자체 엔진이 안내하는 NavigationSession, 그리고 Google Navigation SDK를 따라가는 FeedGuidedTrip. 안내랑 경로 끝은 여전히 기록해. 그래서 주행 기록엔 지도가 뭐라고 생각했는지 남아. Pathfinder 플레이어 밖에서 나오는 Google 자체 음성도 같은 조건으로 막아. 그리고 앱은 측위를 트립보다 코치한테 먼저 줘. 그래서 구간이 시작되는 측위에서 트립은 이미 회전 안내를 붙잡고 있어.
대신 뭐라고 하나
첫 줄은 "이제부터 입구까지 말로 안내할게". 그다음 거리("입구까지 60미터"), 방향, 피파 문이면 피파 메모, 층이 틀렸으면 층 이야기. 방향은 아빠가 보는 쪽을 기준으로 45°씩 나눈 여덟 칸 중 하나야. "앞쪽이야"부터 "오른쪽 앞이야"를 거쳐 "왼쪽 앞이야"까지. 아빠가 초속 1.2 m 이상으로 움직이면 보는 방향은 GPS 진행 방향에서, 느려지거나 서면 나침반에서 와. 역에선 사람이 제자리에서 돌거든.
그 뒤론 뭔가 바뀌어야 말해. 마지막 말에서 12초 이상 지났고, 25 m 더 가까워졌거나 문이 다른 쪽으로 넘어갔을 때(이건 두 배, 24초를 기다려). 층이 바뀐 건 예외야. 들어가는 길에 기압계 판정이 바뀌면 시계와 상관없이 바로 말하고, 12초 세기도 거기서 다시 시작해. 거리는 75 m 아래면 10미터 단위야. 75 m에서 100 m 사이는 "100미터"라고 해. 그래서 킷 테스트에선 83 m가 "입구까지 100미터"로 들려. 이 문구들은 하나만 빼고 전부 Pathfinder가 도보 전에 미리 받아두는 닫힌 어휘에 들어 있어. 고정 문구, 방향 여덟 개, 한 층에서 세 층까지 위아래, 10에서 100 m까지의 "입구까지". 코드 주석은 이유를 한 마디로 적어. 역으로 가는 도보는 문 앞에서 쓸 네트워크 여유가 없다고. 빠지는 하나는 피파의 메모야. 자유롭게 쓴 글이라 어휘에 미리 담아 둘 수가 없어. 캐시에 없는 메모가 실린 첫 줄은 시스템 한국어 목소리로 통째로 읽고(한 줄에 두 목소리를 섞지 않거든), 메모는 다음을 위해 받아 둬.
기압계의 판정
GPS는 길바닥이랑 두 층 위 중앙홀을 구별 못 해. 기압은 해. LevelSensor는 Core Motion의 CMAltimeter를 감싸. 상대 고도(시작한 뒤 바뀐 미터)랑, 지원하는 폰이면 정확도가 붙은 해발 절대 고도. 그다음 FinalApproach.measureLevel이 판정해.
- 문에 아빠가 확인할 때 잰 고도가 있고 폰의 절대 고도가 6 m 이하로 정확하면, 둘을 바로 비교해.
- 아니면 문에
level이 있을 때, 구간이 시작된 지점의 상대 고도에서level × 4 m만큼 올라가야 한다고 봐. - 그것도 아니면 층은 몰라.
차이가 2.5 m 안이면 문 층이야. 나머지는 정수 층으로 반올림하고, 최소 한 층. "아직 아래층이야. 두 층 올라가야 해", 그리고 "계단이나 에스컬레이터를 찾아". 왜 4 m냐고? 설정 주석이 그대로 말해. 역은 층이 높고, 4 m면 사무실 3 m랑 중앙홀 6 m 사이에서 "한 층"을 정직하게 지킨다고.
문서와 코드가 다른 데가 하나 있어. LevelSensor 자기 주석은 도보가 마지막 구간일 때만 돈다고 해. 코드는 코치가 붙는 도보가 시작될 때 켜고(startCoach 안, 코치를 만든 바로 다음) 트립이 닫힐 때 꺼. 커밋 메시지랑 불변식 21이 말하는 게 이쪽이야. 구간 시작은 센서가 켜지는 때가 아니라 기준값을 잡는 때야.
틀린 층은 도착이 아니야
아토차가 써준 규칙이야. 아빠가 수평으로는 문에 닿았는데(15 m, 여기에 측위 오차만큼 최대 15 m 더) 기압계가 층이 틀렸다고 하면, FinalApproach는 reached가 아니라 wrongLevel을 내. "입구 근처인데 층이 달라", 몇 층인지, 계단 문구. 아빠가 거기 서 있는 동안 30초마다 다시 해. 코치가 붙잡고 있는 동안엔 트립 자체의 도착이 도보를 못 끝내. 계단을 올라 문 높이 2.5 m 안에 들어오면 다음 측위가 reached야. 피파가 "입구에 왔어, 아빠"라고 하고, 몇 초 뒤 도보가 끝나. 층을 아예 모르면 문에 닿는 게 도착이야. 예전처럼. 그리고 아빠가 "여기가 입구야"라고 하면 계기가 뭐라든 끝나.
아래 스케치는 킷의 atochaTheStreetBelowTheDoorIsNotArrival 테스트를 같은 숫자로 다시 돌려. 300 m에선 조용하고, 95 m에서 두 층 아래로 구간이 시작되고, 문 아래에서 "층이 달라", 10초 뒤엔 조용, 35초 뒤 다시 "층이 달라", 7.5 m를 올라간 뒤에야 도착.
아직 증명 안 된 것
다 만들었고 테스트도 했어. 그런데 층 로직은 아직 진짜 다층 역을 한 번도 못 만났어. 아키텍처 문서의 기록 그대로야. Mac이랑 시뮬레이터에서 만들고 테스트했고, 시뮬레이터엔 기압계가 없고, 다층 역에서의 첫 실사용은 아직이라고. 이걸 들여온 커밋도 테스트로만 검증했어(킷 도착 테스트 23개, 앱 코치 테스트 4개). 열린 질문을 솔직하게 꼽으면 이래.
- 한 층 4 m는 가정이야. 고른 값이지 어느 역에서 잰 값이 아니야. 천장이 8 m인 중앙홀은 두 층으로 읽혀.
- 기준점은 구간이 시작된 자리야. 그 자리가 이미 경사로나 다리 위거나, 문
level이 기준으로 삼는 길이랑 다른 높이면, 층 수가 전부 그만큼 어긋나. - 잰 고도는 아빠가 직접 확인한 문에만 있어. 그것도 절대 고도를 6 m 안으로 읽는 폰에서만.
- OpenStreetMap 층 정보는 빈약해. 문 대부분엔 아예 없고,
0;-1같은 값은 무시해. - 실내 층은 없어. Pathfinder는 건물 안을 지나는 경로를 짜지 않고 평면도도 몰라. 아빠를 문까지, 그 문의 층으로 데려다줄 뿐이야. Core Location이
floor를 알려줄 수는 있지만 실내 측위를 제공하는 장소에서만이고, Pathfinder는 그걸 안 써.
증명되는 날을 위해 주행 기록은 준비돼 있어. 접근 이벤트마다 기압계 상대 고도를 옆에 적어둬. 그래서 첫 진짜 역 도보가 증거를 남기고 갈 거야.
퀘스트 전체를 돌아보며
마지막 트랙은 나머지 일곱 트랙을 비추는 거울로 딱이야. 새 기계 장치가 하나도 없거든. 같은 생각 몇 개가 건물을 만난 것뿐이야.
- 사실 없는 대답은 대답 없는 것보다 나빠(1트랙). 위치 없는 "맞아"가 내비가 됐고, 층 없는 "도착했어"가 이 트랙이 됐어.
- 누가 안내하든 트립은 하나(2트랙). 코치는 어떤
GuidedTrip옆에든 앉고, 클로저 하나가 Apple, 카카오, Google 트립을 똑같이 조용히 시켜. - 규칙은 순수한 값, 테스트는 Mac에서(3트랙).
FinalApproach는GuidanceEngine처럼 측위를 받아서 이벤트를 내. 아토차도 단위 테스트로 돌아. - 필요하기 전에 캐시해두는 닫힌 어휘(4트랙). 접근 문구는 피파가 자유롭게 쓴 메모 하나만 빼면 전부 피파 목소리로 도보 전에 받아둔 아는 문구야.
- 도보는 도보만의 모드(5트랙). 문을 받는 것도 도보뿐이야.
- 시뮬레이터가 못 보여주는 것(6트랙). 워치의 진짜 빈틈은 진짜 도보에서 나왔어. 기압계 빈틈도 그럴 거야.
- 피파의 핀은 피파가 아는 걸 실어 날라(7트랙). 이제 들어가는 길까지.
그리고 그 모든 것 위엔 첫날부터 버텨온 규칙이 있어. 아빠 말은 계기보다 앞서. 피파 말보다도.