나침반 방위 30도를 ROS yaw에 그대로 넣으면 로봇은 어디를 볼까: 북쪽 기준 시계방향을 동쪽 기준 반시계방향으로 바꾸기
야외 로봇의 나침반 모듈이 방위 30도를 알려 줬다. 북쪽에서 시계방향으로 30도 돌아간 방향이다. 이 값을 math.radians(30)으로 단위만 바꿔 ROS 메시지의 yaw에 넣으면, ROS 쪽 코드는 로봇이 동쪽에서 반시계방향으로 30도 돌아간 곳을 본다고 해석한다. 나침반으로 치면 방위 60도다. 나침반 방위 30도를 ROS yaw에 그대로 넣으면 로봇이 실제로 향한 방향과 ROS가 믿는 방향이 30도 벌어진다.
고치는 식은 yaw = 90° - 방위 한 줄이다. 까다로운 쪽은 이 한 줄이 맞는지 확인하는 테스트다. 방위 45도는 두 표기에서 같은 방향이라 변환을 아예 하지 않은 코드도 45도 테스트를 통과한다. 동쪽 90도 하나만 넣으면 이번에는 부호를 거꾸로 쓴 코드가 통과한다. 아래에서는 ROS 공식 관례 문서로 규칙을 확인하고 식을 유도한 뒤, 흔한 오답 다섯 가지를 Python으로 계산해 어느 입력에서 드러나고 어느 입력에서 숨는지 비교한다. 그 비교에서 역할이 겹치지 않는 네 입력짜리 테스트가 나온다.
나침반 방위와 ROS yaw는 0도의 위치와 도는 방향이 모두 다르다
나침반 방위(compass bearing, heading)는 북쪽을 0도로 두고 시계방향으로 잰 방향이다. 동쪽이 90도, 남쪽이 180도, 서쪽이 270도다. 요 각(yaw)은 수직축(z축) 둘레의 회전 각이다. 로봇을 위에서 내려다봤을 때 머리가 향한 방향이라고 생각하면 된다.
ROS에서 yaw를 어떻게 읽는지는 REP(ROS Enhancement Proposal)라는 커뮤니티 관례 문서가 정한다. 그중 단위와 좌표 관례를 다루는 REP-103(Status: Active)은 짧은 거리의 지리 위치를 ENU(East-North-Up, 동-북-위) 좌표로 나타내라고 한다. X축이 동쪽, Y축이 북쪽, Z축이 위다(REP-103 L111–L116). 축 순서가 북-동-아래인 NED(North-East-Down)는 REP-103에서 _ned 접미사를 붙이는 보조 프레임으로 따로 둔다(L134–L140).
회전 방향은 오른손 법칙(right-hand rule)을 따른다. 오른손 엄지를 위(Z축)로 세우면 나머지 손가락이 감기는 방향이 양의 회전이고 위에서 내려다보면 반시계방향이다. REP-103은 이 점과 나침반과의 차이를 직접 적어 두었다.
By the right hand rule, the yaw component of orientation increases as the child frame rotates counter-clockwise, and for geographic poses, yaw is zero when pointing east. This requires special mention only because it differs from a traditional compass bearing, which is zero when pointing north and increments clockwise. Hardware drivers should make the appropriate transformations before publishing standard ROS messages.
지리 좌표의 yaw는 동쪽에서 0이고 반시계방향으로 커진다. 전통적인 나침반 방위는 북쪽에서 0이고 시계방향으로 커진다. 그리고 두 표기를 맞추는 일은 표준 ROS 메시지를 내보내기 전에 하드웨어 드라이버가 맡는다. 각도 단위도 라디안이다(L73–L76). 그러니 방위 숫자를 yaw 자리에 옮기려면 기준축 이동, 회전 방향 반전, 도→라디안 세 가지를 모두 처리해야 한다. 오른손 좌표계와 로봇 몸체 축의 기본은 로봇 좌표계 기초 글에서 먼저 볼 수 있다.

개념도: 방위 30°를 yaw 자리에 그대로 넣으면 ROS는 방위 60° 쪽을 로봇 방향으로 읽고, 실제 방향과 30° 벌어진다.
30도가 60도로 읽히는 이유를 단위벡터로 따져 본다
그림으로 떠올리면 시계 문자판을 90도 돌린 다음 거울에 비춘 것과 같다. 돌리기는 0도의 위치를 북쪽에서 동쪽으로 옮기는 일이고, 거울 비추기는 시계방향을 반시계방향으로 뒤집는 일이다. 이 비유는 평면에서 각도 표기를 바꾸는 데까지만 맞는다. 로봇이 기울어져 roll·pitch가 섞이는 3차원 자세나, NED와 ENU 사이에서 축 전체를 바꾸는 변환은 이 그림으로 설명되지 않는다.
여기부터는 도와 라디안(180° = π rad), x·y 좌표 평면, 그리고 방향을 sin·cos로 적는 방법이 필요하다. 정확하게는 같은 방향을 가리키는 단위벡터(길이가 1인 방향 화살표)를 두 방식으로 쓰고 맞춰 보면 된다. ENU에서 X가 동쪽, Y가 북쪽이므로 방위 b인 방향의 단위벡터는 (sin b, cos b)다. 방위 0(북)이면 (0, 1), 방위 90(동)이면 (1, 0)이 된다. 한편 yaw ψ는 +X축(동)에서 반시계방향으로 잰 각이라 단위벡터가 (cos ψ, sin ψ)다. 두 벡터가 같으려면 cos ψ = sin b, sin ψ = cos b여야 하고, 이를 만족하는 값이 ψ = 90° − b다.
세 방향으로 확인해 본다.
- 방위 0(북): 벡터
(0, 1). yaw 90이면(cos 90°, sin 90°) = (0, 1)로 같다. - 방위 90(동): 벡터
(1, 0). yaw 0이면(1, 0)으로 같다. - 방위 30: 벡터
(0.5, 0.866). yaw 60이면(0.5, 0.866)으로 같다.
REP-103 원문에 이 식이 있는 것은 아니다. 두 정의에서 유도한 식이다. 식을 거꾸로 풀면 b = 90° − ψ로 모양이 같다. 방위→yaw와 yaw→방위가 같은 식이라는 뜻이고, 제목의 질문도 여기서 풀린다. yaw 자리에 30이 들어가면 ROS는 방위 90 − 30 = 60도를 본다고 해석한다. 로봇은 방위 30도를 보고 있으니 30도 차이다.
변환 함수는 빼기, 범위 맞추기, 라디안 순서로 쓴다
90 - b를 계산하면 방위 0~360도에 대해 yaw가 −270도 초과 90도 이하로 나온다. 방위가 270도 이상이면 결과가 −180도 이하로 내려간다. 방위 300도(서쪽에서 북쪽으로 30도 올라간 방향)는 90 - 300 = -210이다. −210도와 150도는 같은 방향이므로 약속한 범위 안의 숫자로 옮겨 적는데, 이를 각도 정규화(angle normalization, angle wrapping)라고 한다.
이 글의 코드는 (x + 180) % 360 - 180으로 [−180, 180) 범위를 쓴다. Python의 %는 나누는 수의 부호를 따르므로 음수 입력도 이 범위로 들어온다(Python math 문서에 %와 math.fmod의 부호 차이가 설명돼 있다). 이 범위에서 방위 300은 150도, 방위 270은 −180도가 된다. (−180, 180]을 쓰는 코드라면 270을 180도로 적는다. 표기만 다르고 방향은 같다. 사용하는 라이브러리의 약속을 따르면 된다.)
마지막으로 math.radians로 라디안으로 바꾼다. 방위 300의 결과는 2.6179938779914944 rad(= 5π/6)이다. 3.14159 같은 근사값을 직접 곱하면 작은 오차가 생기므로 math.radians나 math.pi를 쓴다.
정규화는 방향을 바꾸지 않는다. REP-103은 회전을 나타내는 방법 가운데 쿼터니언(quaternion, 사원수)을 가장 선호한다(L148–L153). 쿼터니언은 3차원에서 물체가 향한 방향을 네 숫자 (x, y, z, w)로 적는 회전 표현이고, ROS 2에서는 w를 마지막에 둔다(ROS 2 Quaternion fundamentals). 평면에서 yaw ψ만큼만 돈 회전이라면 (0, 0, sin(ψ/2), cos(ψ/2))가 된다. yaw −210도와 150도를 여기에 넣으면 (z, w)가 (-0.9659, -0.2588)과 (0.9659, 0.2588)로 부호만 반대인 같은 회전이 된다. 그래서 정규화를 빠뜨린 코드는 yaw 숫자의 범위를 직접 따지는 소비자 앞에서만 문제가 된다. 이 차이는 뒤의 테스트 설계에서 다시 쓴다.
틀린 구현은 저마다 통과하는 방위가 따로 있다
변환 코드를 잘못 쓰는 방식은 몇 가지로 모인다. 변환을 빼먹거나(yaw = b), 부호를 거꾸로 쓰거나(yaw = b - 90), 방향만 뒤집거나(yaw = -b), 축만 옮기거나(yaw = b + 90), 식은 맞게 계산하고 도 숫자를 라디안 자리에 그대로 넣는 경우다. 다섯 구현에 방위 여덟 개를 넣고 정답과의 방향 차이를 계산했다. 차이는 두 각도를 감싼 뒤의 크기(도)이고, 0이면 그 입력에서는 틀린 구현이 정답과 구분되지 않는다는 뜻이다.
| 방위 입력(도) | 0 | 30 | 45 | 90 | 180 | 225 | 270 | 300 | 차이가 0인 방위 |
|---|---|---|---|---|---|---|---|---|---|
| 정답 yaw(도, [−180, 180)) | 90 | 60 | 45 | 0 | −90 | −135 | −180 | 150 | — |
변환 없음 b |
90 | 30 | 0 | 90 | 90 | 0 | 90 | 150 | 45, 225 |
부호 반대 b - 90 |
180 | 120 | 90 | 0 | 180 | 90 | 0 | 60 | 90, 270 |
방향만 반전 -b |
90 | 90 | 90 | 90 | 90 | 90 | 90 | 90 | 없음 |
축만 이동 b + 90 |
0 | 60 | 90 | 180 | 0 | 90 | 180 | 120 | 0, 180 |
| 도 숫자를 라디안 자리에 | 26.62 | 137.75 | 13.31 | 0 | 26.62 | 39.93 | 53.24 | 164.37 | 90 |
표는 2026년 9월 24일 Linux의 Python 3.12.3 표준 라이브러리로 계산한 산술 검산이며, ROS 노드나 시뮬레이터, 로봇을 돌려 얻은 값이 아니다. 마지막 행은 정규화까지 마친 도 숫자(예: 60)를 ROS가 라디안으로 읽었을 때의 방향을 도로 환산해 비교한 값이다. 60 rad는 감으면 약 −162.25도가 된다. 같은 주제의 산술 예제 example.py는 북 0→90, 동 90→0, 30→60, 300→150 등 여덟 사례를 확인하며, 실행 기록 verification.json에 python3 -I example.py의 종료 코드 0이 남아 있다.
통과하는 방위가 구현마다 다른 이유는 식을 나란히 놓으면 보인다. 변환 없음은 b ≡ 90 - b(mod 360), 즉 2b ≡ 90일 때 정답과 같아지므로 45도와 225도에서 숨는다. 부호 반대는 2b ≡ 180이라 90도와 270도, 축만 이동은 2b ≡ 0이라 0도와 180도에서 숨는다. 방향만 반전은 어디서나 90도 어긋나고, 도-라디안 혼동은 yaw 숫자가 0이 되는 방위 90에서만 우연히 맞는다. 0~359의 정수 방위를 모두 넣어 봐도 결과가 같았다.
여기서 테스트 입력에 대한 판단이 나온다. 방위 45만 넣은 테스트는 가장 흔할 법한 실수인 “변환 없음”을 통과시킨다. 동쪽 90만 넣으면 부호 반대와 도-라디안 혼동을 놓치고, 북쪽 0만 넣으면 축만 옮긴 코드를 놓친다. 반면 30과 300은 각각 혼자서도 다섯 오답을 모두 드러낸다. 그래도 0과 90을 함께 넣어 실패 메시지를 읽기 쉽게 만든다. “북쪽이 yaw 90으로 나오는가”, “동쪽이 yaw 0으로 나오는가”는 기준축이 제자리에 있는지 바로 알려 준다. 300은 정규화 경계를 지나는 입력이다. 정리하면 0·90은 기준점, 30은 제목의 사례, 300은 경계로 역할이 겹치지 않는 네 입력이다. 입력 개수를 늘리는 것보다 각 입력이 어떤 오답을 떨어뜨리는지가 중요하다.
정규화를 빠뜨린 90 - b도 짚어 둔다. 방위 270·300에서 원시 숫자는 −180, −210으로 기대값 180, 150과 다르지만 방향은 같다. 원시 숫자를 그대로 비교하는 테스트만 이 구현을 실패로 판정한다. 범위를 요구하는 소비자가 있을 때만 결함이다.
0·90·30·300을 넣은 테스트가 실제로 내는 출력
아래 코드는 변환 함수, 두 각도의 감싼 차이, 네 입력 테스트와 틀린 구현 비교를 한 번에 담았다. 검산은 2026년 9월 24일 Linux의 Python 3.12.3에서 이 코드를 python3 -I -에 표준 입력으로 넘겨 실행했고 종료 코드는 0이었다. 표준 라이브러리 math만 쓰므로 파일로 저장했다면 python3 -I - < 파일이름.py처럼 같은 방식으로 실행할 수 있다.
import math
def bearing_to_enu_yaw(bearing_deg):
"""나침반 방위(북 0°, 시계방향)를 ENU yaw(동 0°, 반시계방향, 라디안)로 바꾼다."""
yaw_deg = 90.0 - bearing_deg
yaw_deg = (yaw_deg + 180.0) % 360.0 - 180.0 # [-180, 180)
return math.radians(yaw_deg)
def angle_diff_deg(a_rad, b_rad):
d = math.degrees(a_rad - b_rad)
return (d + 180.0) % 360.0 - 180.0
CASES = {0: 90, 90: 0, 30: 60, 300: 150} # 방위 -> 기대 yaw(도)
def check(convert, cases):
bad = []
for bearing, expected_deg in cases.items():
diff = angle_diff_deg(convert(bearing), math.radians(expected_deg))
if abs(diff) > 1e-9:
bad.append((bearing, round(diff, 6)))
return bad
print("correct, 4 cases :", check(bearing_to_enu_yaw, CASES))
print("identity, 45 only:", check(lambda b: math.radians(b), {45: 45}))
print("identity, 4 cases:", check(lambda b: math.radians(b), CASES))
print("sign flip, 90 only:", check(lambda b: math.radians(b - 90), {90: 0}))
print("sign flip, 4 cases:", check(lambda b: math.radians(b - 90), CASES))
print("degrees kept, 4 cases:", check(lambda b: 90.0 - b, CASES))
print("yaw(300) rad =", bearing_to_enu_yaw(300), "deg =", math.degrees(bearing_to_enu_yaw(300)))
print("yaw(270) deg =", math.degrees(bearing_to_enu_yaw(270)))
실제 출력은 다음과 같다.
correct, 4 cases : []
identity, 45 only: []
identity, 4 cases: [(0, -90.0), (90, 90.0), (30, -30.0), (300, 150.0)]
sign flip, 90 only: []
sign flip, 4 cases: [(0, -180.0), (30, -120.0), (300, 60.0)]
degrees kept, 4 cases: [(0, 26.620156), (30, 137.746771), (300, 57.886302)]
yaw(300) rad = 2.6179938779914944 deg = 150.0
yaw(270) deg = -180.0
빈 목록 []이 통과, 튜플이 들어 있으면 실패다. 튜플의 두 번째 값은 “구현 − 기대”의 감싼 차이(도)이고, 앞 표는 이 값의 크기만 적었다. 읽을 곳은 두 쌍이다. identity, 45 only는 비어 있지만 같은 구현이 네 입력에서는 네 개 모두 실패한다. sign flip, 90 only도 비어 있지만 네 입력에서는 0, 30, 300에서 실패하고 90만 통과한다. 한 입력짜리 테스트가 초록불을 켜도 믿을 수 없다는 것이 출력으로 드러난다.
degrees kept의 300 값 57.886302가 앞 표의 164.37과 다른 것은 입력이 달라서다. 이 람다는 정규화를 하지 않으므로 −210을 라디안으로 읽은 방향과 비교했고, 표는 정규화된 150을 라디안으로 읽은 경우다.
이 테스트는 감싼 차이를 비교하므로 방향이 맞는지만 확인한다. 반환값이 [−π, π) 안에 있어야 하는 소비자가 있다면, 범위 자체를 확인하는 조건을 따로 둬야 정규화 누락을 잡을 수 있다.
직접 풀어 볼 세 문제
1. 방위 135도는 ENU yaw로 몇 도, 몇 라디안인가?
90 − 135 = −45도다. [−180, 180) 안이므로 정규화할 필요가 없고, math.radians(-45)는 약 −0.785 rad다. 위에서 보면 동쪽에서 시계방향으로 45도, 곧 남동쪽이다.
2. 테스트 입력을 45와 225 두 개로 정하면 무엇을 놓치는가?
변환 없음 구현을 놓친다. 표에서 이 구현의 차이는 두 입력 모두 0이다. 부호 반대, 방향만 반전, 축만 이동, 도-라디안 혼동은 45에서 각각 90, 90, 90, 13.31도 어긋나 드러난다. 입력이 두 개여도 둘 다 같은 오답의 사각지대에 있으면 한 개와 다를 바 없다.
3. 드라이버가 이미 ENU yaw로 바꿔 보낸 값에 변환을 한 번 더 걸면?
식이 자기 자신의 역함수이므로 90 − (90 − b) = b, 원래 방위 숫자가 돌아온다. 결과는 표의 “변환 없음”과 같아서 방위 30도 로봇이 다시 방위 60도를 보는 것으로 읽힌다. 식을 붙이기 전에 입력이 나침반 방위인지부터 확인해야 하는 이유다.
robot_localization의 yaw_offset은 축만 옮긴다
GPS와 IMU 같은 센서 값을 융합하는 ROS 패키지 robot_localization의 navsat_transform_node 문서에는 비슷해 보이는 설정이 있다.
~yaw_offsetYour IMU should read 0 for yaw when facing east. If it doesn’t, enter the offset here (desired_value = offset + sensor_raw_value). For example, if your IMU reports 0 when facing north, as most of them do, this parameter would be pi/2 (~1.5707963).
이 문서도 동쪽 0을 기준으로 삼아 REP-103과 같은 말을 한다. 다만 식이 desired = offset + raw로 더하기뿐이라 하는 일은 축 이동이다. 같은 패키지의 센서 데이터 준비 문서는 모든 IMU 데이터를 ENU로 가정하고 NED 데이터와는 동작하지 않으며, 지상 로봇을 반시계방향으로 돌리면 yaw가 커져야 한다고 적는다. 즉 원시 yaw가 이미 반시계방향으로 커지는데 0의 위치만 북쪽인 IMU에 π/2를 넣으라는 안내다.
그렇다면 시계방향으로 커지는 나침반 방위를 그대로 넣고 yaw_offset에 π/2만 주면 어떻게 될까. 문서의 식에 앞의 표를 대입해 보면 결과는 “축만 이동” 행과 같다. 방위 30에서 60도 어긋나고, 0과 180에서만 정답과 같다. 로봇을 북쪽으로 세워 두고 설정을 확인하면 이 오류가 보이지 않는다는 뜻이다. π/2만 더하는 설정은 원시값이 반시계방향으로 커질 때만 맞으므로, 나침반 방위는 입력 단계에서 먼저 90 - b로 바꾼다.
식을 붙이기 전에 입력이 정말 나침반 방위인지 확인한다
yaw = 90 - b는 입력이 “북쪽 0, 시계방향 증가”일 때만 맞는다. 장치나 드라이버가 이미 ENU로 바꿔 내보낸다면 확인 문제 3처럼 원래 방위 숫자가 돌아오므로 식을 붙이지 않는다. NED 기준으로 출력하는 장치는 조금 다르다. NED는 Z축이 아래를 향하므로(REP-103 L134–L140) 같은 오른손 법칙의 양의 회전이 위에서 내려다보면 시계방향이 되고, yaw 0은 X축인 북쪽이다. 그래서 NED 출력의 yaw는 나침반 방위와 같은 북 0·시계방향 규약이고, heading에는 같은 90 - ψ가 그대로 적용된다. roll·pitch까지 포함한 전체 자세는 사정이 다르다. 축 교환과 z 반전이 필요한 별도 변환이라 yaw만 바꾸고 roll·pitch를 그대로 복사하면 틀릴 수 있고, robot_localization도 NED 데이터는 사용 전에 ENU로 바꾸라고 한다. 장치가 어느 좌표 규약과 몸체 축 기준으로 값을 내보내는지는 제조사 드라이버 문서에서 먼저 확인한다.
값의 출처도 따져 봐야 한다. GPS 데몬 gpsd의 JSON 문서는 track을 “Course over ground, degrees from true north”로 정의한다. 진북 기준 이동 방향이지 차체가 향한 방향이 아니다. 정의대로라면 정지, 후진, 옆미끄럼 상황에서는 차체 방향과 다를 수 있으니, 장치가 주는 값이 이동 방향(track)인지 차체 방향(heading)인지 문서로 가려 둔다.
나침반은 자북을 가리키고, 자북과 진북 사이의 각인 자기 편차(magnetic declination)는 장소와 시간에 따라 변한다(NOAA NCEI). 편차의 부호 약속은 자료마다 다르다. NOAA는 진북보다 동쪽이면 양수라고 하고, gpsd의 magvar는 서쪽 편차를 양수로 둔다. ROS의 ENU에서 북쪽이 진북인지 자북인지도 REP-103은 구분하지 않는다. 초안(Draft) 상태인 REP-145는 IMU world frame을 “relative to magnetic north”라고 적지만 확정 표준은 아니고, robot_localization은 자북 기준 IMU를 위해 ~magnetic_declination_radians 설정을 따로 둔다. 이 글의 90 - b는 편차를 보정하지 않는다. 진북 기준이 필요하면 보정 단계를 따로 두고, 부호는 한쪽 자료를 일반 규칙으로 삼지 말고 쓰는 장치와 라이브러리의 약속에 맞춘다.
어댑터에는 식보다 네 입력 테스트를 먼저 붙인다
방위 센서 값을 ROS 메시지로 넘기는 어댑터를 쓰거나 검토하고 있다면, 변환 함수보다 먼저 0·90·30·300 네 입력 테스트를 붙여 두는 편이 안전하다. 식은 한 줄이라 눈으로 검토해도 틀리기 쉽고 틀린 식마다 통과하는 입력이 다르기 때문이다.
실물 로봇에 적용하기 전에는 드라이버 문서로 출력 규약을 확인하고, 바퀴를 구동하지 않은 정지 상태에서 로봇을 알려진 방향에 두고 발행되는 yaw를 읽어 본다. 이때도 표의 교훈이 그대로 적용된다. 동쪽 한 방향만 보면 부호 반대 오류가, 북쪽 한 방향만 보면 축만 옮긴 오류가 드러나지 않으므로 최소 두 방향 이상을 본다.
이 판단들이 기대는 근거는 REP-103과 robot_localization 문서, 그리고 앞의 산술이다. robot_localization이나 실제 센서 드라이버에 적용해 보지 않았으므로 yaw_offset 판단과 정지 상태 확인 절차는 문서의 식에서 끌어낸 권고이고, gpsd track과 차체 방향의 차이도 정의에서 끌어낸 해석이다. 인용문의 “as most of them do”(대부분의 IMU가 북쪽 0이라는 말)는 robot_localization 문서의 서술이지 이 글에서 조사한 통계가 아니다. 센서 장착 각도 오프셋, 주변 금속에 의한 자기 간섭, 기울어진 자세의 roll·pitch 결합, 쿼터니언을 만들어 메시지에 넣는 코드도 다루지 않았다. 표의 30도, 120도는 방향 표기의 차이일 뿐 주행 오차나 충돌 위험을 나타내는 수치가 아니다.
같은 REP-103이 정한 단위 약속을 경계 입력으로 확인하는 방법은 ROS 속도 단위 경계 테스트 글에서 이어서 볼 수 있다.
참고 링크
확인일은 모두 2026년 9월 24일이다.
- REP 103: Standard Units of Measure and Coordinate Conventions (고정 커밋 원문) — yaw 동쪽 0·반시계 증가, 나침반 방위와의 차이, 드라이버 변환 책임. ros.org 렌더링 페이지
- robot_localization — navsat_transform_node —
~yaw_offset,~magnetic_declination_radians - robot_localization — Preparing Your Data for Use with robot_localization — ENU 가정, 반시계 회전 시 yaw 증가
- REP 145: Conventions for IMU Sensor Drivers (Draft)
- ROS 2 Documentation (Jazzy) — Quaternion fundamentals — 쿼터니언의 네 성분
(x, y, z, w) - Python math 모듈 문서 —
math.radians,math.pi,%와math.fmod의 부호 차이 - gpsd_json(5) —
track,magvar정의 - NOAA NCEI — Magnetic Declination
- HuntLab 산술 예제 example.py와 검산 기록 verification.json — Python 3.12.3 산술 검산 기록