양방향 거리 측정: SS-TWR와 DS-TWR
휴대폰과 태그의 시계는 서로 맞춰져 있지 않다. 한쪽이 “지금 12시 0분 0.000000001초에 보냈어”라고 해도, 받는 쪽 시계로는 그게 언제인지 알 수 없다. 그래서 UWB는 한 번 보내고 받는 것으로는 거리를 잴 수 없고, 보내고, 답을 받고, 걸린 시간을 비교한다. 이것이 TWR(Two-Way Ranging)이다. 이 장의 공식은 공개 라이브러리 Decawave uwb-core의 rng_math.c를 그대로 옮겼고, 그 라이브러리의 자체 시험 값으로 검증했다.
- 단방향 TWR(SS-TWR)의 공식 ToF = (Tround − Treply) ÷ 2를 타임라인에서 유도한다.
- 응답 시간과 두 시계의 ppm 차이가 SS-TWR 오차를 만드는 방식, 반송파 보정의 효과를 계산한다.
- 비대칭 양방향 TWR(DS-TWR) 공식이 시계 오차를 거의 지워 버리는 이유를 확인한다.
- 공개 코드의 시험 타임스탬프로 거리를 직접 계산해 본다.
단방향 TWR: 핑퐁 한 번
A(시작자, initiator)가 Poll을 보내고 송신 시각을 자기 시계로 찍는다. B(응답자, responder)는 받은 시각을 자기 시계로 찍고, 정해진 시간 뒤에 Response를 보낸다. A는 그 Response를 받은 시각을 찍는다. 이제 두 시간이 있다.
- A가 잰 왕복 시간 Tround = A의 수신 시각 − A의 송신 시각
- B가 잰 응답 시간 Treply = B의 송신 시각 − B의 수신 시각 (Response 안에 담아 알려 준다)
왕복에서 B가 기다린 시간을 빼면 신호가 두 번 날아간 시간이 남는다. 그래서 ToF = (Tround − Treply) ÷ 2. 각자 자기 시계 안에서의 차이만 쓰므로, 두 시계가 맞을 필요가 없다. 대신 5장에서 본 대로 시계의 빠르기가 다르면 Treply가 A의 단위로 틀리게 전달된다.
산에서 “야호”를 외치고 메아리가 2초 뒤에 오면, 소리가 왕복한 시간이 2초라서 산까지 1초 거리다. 사람이 답한다면 답하기 전에 망설인 시간을 빼야 한다. TWR은 상대가 “나는 받고 나서 정확히 이만큼 있다가 답했다”고 알려 줘서 그 시간을 뺀다. 문제는 상대의 시계도 조금 틀리다는 것이다.
두 기기의 시계가 서로 20 ppm 어긋나 있다. DS-TWR(메시지 셋)를 쓰면 이 오차는?
DS-TWR은 양쪽이 한 번씩 응답해 왕복을 두 번 잰다.
타임라인으로 보는 SS-TWR와 DS-TWR
uwb-core의 calc_tof_ss()·calc_tof_ds()와 같다.uwb-core의 calc_tof_ss()는 마지막 인자 skew로 이 보정을 한다: ToF = (Tround − Treply × (1 − skew)) ÷ 2. A는 B가 보낸 Response의 반송파 주파수가 자기 반송파와 얼마나 다른지 재서(5장, 9번 채널에서 1 ppm ≈ 8 kHz) skew를 얻는다.
양방향 TWR: 핑퐁 두 번
DS-TWR(Double-Sided TWR)는 핑퐁을 한 번 더 한다. A Poll → B Response → A Final. 이제 A와 B 모두 왕복 하나와 응답 하나씩을 갖는다(Tround1, Treply1은 첫 번째 핑퐁, Treply2, Tround2는 두 번째). 두 응답 시간이 같지 않아도 되는 비대칭 공식은 다음과 같다.
SS: calc_tof_ss(response, request, transmission, reception, skew) = ((응답 − 요청) − (송신 − 수신) & 0xFFFFFFFF × (1 − skew)) ÷ 2 · DS 비대칭: calc_tof_ds()(분모가 0이면 NaN) · DS 대칭 근사: calc_tof_sym_ds() = (T1R − T1r + T2R − T2r) ÷ 4 · 거리: uwb_rng_tof_to_meters() = ToF × (299,792,458 ÷ 1.000293) × (1 ÷ 499.2 MHz ÷ 128) — 모두 Decawave uwb-core lib/rng_math/src/rng_math.c · Android FiRa의 측정 방식 이름: RANGING_ROUND_USAGE_SS_TWR_*, DS_TWR_*(FiraParams.java)
공개 코드의 시험 값으로 계산하기
uwb-core에는 실제 측정에서 가져온 타임스탬프로 공식이 정확한 거리(정수 m)를 내는지 확인하는 자체 시험이 있다. 32비트 시계 넘침(0xFFFFFFFF 근처의 값)을 일부러 섞은 경우도 있다. 같은 값을 이 책의 계산 함수에 넣어 보자.
uwb-core 시험 타임스탬프
uwb-core의 lib/rng_math/selftest/src/testcases/calc_tof_test.c에서 그대로 가져왔다. 시험은 결과를 정수로 자른 값(F64_TO_U64)이 기대값과 같은지 본다. “넘침”은 응답 − 요청이 32비트를 넘어가 0으로 돌아가는 값을 섞은 경우로, 빼기를 32비트에서 하고 마스크를 씌워야 맞는다(5장).핵심 정리
- 시계가 맞지 않는 두 기기는 왕복으로 잰다. SS-TWR: ToF = (Tround − Treply) ÷ 2.
- SS-TWR 오차 ≈ Treply × (eA − eB) ÷ 2. 20 ppm 차이, 500 µs면 1.5 m. 반송파로 잰 시계 차이(skew)로 보정한다.
- DS-TWR(비대칭): ToF = (R1·R2 − P1·P2) ÷ (R1 + R2 + P1 + P2). 오차 ≈ ToF × ppm, 사실상 0. 대신 프레임이 하나 더.
- 타임스탬프 빼기는 넘침을 고려해 마스크를 씌운다. 공식은
uwb-core의 시험 값으로 검증된다.
확인 퀴즈
Tround = 1,000.0200 µs, Treply = 1,000.0000 µs(같은 시계 빠르기)면 거리는?
SS-TWR의 오차를 키우는 두 가지는?
DS-TWR가 시계 오차에 강한 이유는?
calc_tof_ss()의 skew 인자는 무엇을 보정하나?