# T2 천문 구현과 검증

## 착수 판단

- 코어 질문: 정적 브라우저 Worker에서 실제 Swiss Ephemeris로 서양·라히리 베딕 핵심 명식을 계산하고, 미상·고위도·파일 실패를 숨기지 않는가.
- 유효성 문턱: 로컬 WASM/행성·달 데이터, UTC→TT/UT1 변환, 실제 엔진 플래그/하우스 상태 확인, 독립 수치 또는 규칙 기대값, 배포 번들 Worker 실행.
- 가용 자원: 고정 npm 패키지·C API·공식 문서·JPL 독립 서비스, 로컬 Node/Vitest 및 설치된 Edge. 전 범위 역법/천체 정확도 벤치마크 자원은 없음.
- 결정: 이 핵심 동작과 제한된 대표 수치 검증에 착수. 모든 시대·모든 행성의 정확도 보증 주장은 하지 않음.
- 단계 근거: API 검사→시간척도·실패 코드 보존; 실제 WASM 단위검사→호출·데이터·경계; JPL 고정 epoch→독립 황경 기준; production Worker→배포 자산·네트워크 호환성.

## 구현 계약

`src/core/astronomy.ts`: `calculateAstronomy(input,normalized)`와 `solarLongitudeAtUtc(string|Date):Promise<number>`.
`astro-engine.ts`는 실제 C 함수 `swe_utc_to_jd`, `swe_calc_ut`, `swe_houses_ex`를 직접 호출한다. C 출력 버퍼를 복사한 뒤 finally에서 모두 해제한다. 고정된 로컬 파일만 사용하며 런타임 외부 계산 API를 호출하지 않는다. UI/복사에는 `types.ts`의 명시적 자료형을 공유했다.

서양: 지구중심 tropical Sun~Pluto 10개+true node, 황경/속도/역행, Placidus 또는 whole-sign, ASC/MC/IC/DC, 합·육각·사각·삼각·충 및 명세 오브. 베딕: Swiss sidereal ID1 Lahiri, Sun~Saturn+mean Rahu/대칭 Ketu, whole-sign D1, lagna, 27 nakshatra/4 pada와 D9 sign/house. 나크샤트라 이름은 항성 체계에만 의미가 있으며 서양 출력에 함께 존재하는 분할 보조 필드는 tropical 분할값이다.

UTC를 경도 보정 시각으로 바꾸지 않는다. Swiss UTC 변환의 두 double을 TT와 UT1로 각각 보존한다. 1972년 이전은 Swiss 정책상 UT1 입력/ΔT 모형이며 역사적 UT1 관측값 보증이 아니다. 1972년 이후 내장 leap-second 표와 ΔT를 사용한다. 현재 이후 leap seconds/EOP 실측 정확도를 보증하지 않는다. 미상에는 순간을 만들지 않고 두 체계를 null/unavailable로 반환한다.

`(returnedFlags & 7) !== 2`이면 Moshier/JPL 대체를 거부한다. 플라시두스 C status<0인 경우 Swiss가 제공한 대체 cusp를 버리고 western.houses=null/errors로 반환한다. 베딕 whole-sign은 별도로 계산 가능하다. engine 초기화/데이터 실패는 두 체계를 null로 반환한다.

## 선택과 재현성·라이선스

검토한 `@swisseph/browser@1.4.0`은 calc flags는 노출하나 WASM에 UTC 변환 export가 없고 houses 상태를 버린다. 대신 **swisseph-wasm@0.1.0** npm tarball의 생성된 `wasm/swisseph.js`, `.wasm`, `.data`만 `public/ephemeris`에 그대로 복사했다. 패키지의 고수준 wrapper는 사용하지 않는다(UTC C 서명/상태 처리를 직접 보존). 추가 npm dependency는 없다.

- 원본: https://registry.npmjs.org/swisseph-wasm/-/swisseph-wasm-0.1.0.tgz
- tarball SHA256: `187C81AA5F760C496F20F4C16ADD86DA87B89138125E1E82A96BF9422EB7CA9F`
- 생성 loader: `F645827DC8620C058101AA2373BD1254C679C2EEB5A6A80E97A1528BFAC5DF36`
- WASM: `A6E5F53CC39BAC07F44B4CE54E5F90F6BD1E25B6D20DF2EA0BC4C1374D09842C`
- data: `1AAC44B2EA3DFE668A063ED29DADA99A7A96D8FA22FDCE626CDF9E270B084ED0`
- 실행 core version: `2.10.03`. data에는 sepl_18, semo_18, seas_18 포함(1800~2399 구간). 제품 입력 1900~현재 범위에 사용한다.
- 생성 loader/배포 패키지 GPL-3.0-or-later 고지: `public/ephemeris/WRAPPER-LICENSE.txt`. Swiss C/ephemeris는 Astrodienst 저작권 및 AGPL/상업 이중 라이선스; `SWISS-LICENSE.txt`는 https://raw.githubusercontent.com/aloistr/swisseph/v2.10.03/LICENSE 에서 보존했다. 앱은 AGPL-3.0-only 공개 소스다.
- 대응 소스: https://github.com/prolaxu/swisseph-wasm (패키지 0.1.0, vendored C 2.10.03); https://github.com/aloistr/swisseph/tree/v2.10.03 . 원본 tarball을 추출해 위 3개 파일을 복사하고 SHA256을 비교하여 재현 가능하다. 공개 배포 시 앱과 이 고지 및 대응 소스 링크를 함께 제공해야 한다.

## 검증 결과

`npx vitest run tests/astronomy.test.ts`: **6/6 통과**. 실제 로컬 WASM을 실행한다.

- UTC 2000-01-01T12:00Z→TT JD `2451545.0007428704` (UTC+64.184s 독립 시간척도 기대), UT1 `2451545.00000411`; 서로 뒤바뀌지 않음.
- Sun 반환 flags SWIEPH=2 확인. 파일 범위 밖 JD2600000에서 fallback 거부 확인.
- 위도80° Placidus C 실패 감지/대체 거부; 같은 위도 whole-sign 성공. 통합 결과 western.houses만 null, Vedic lagna 유지 확인.
- 미상 두 체계 null. 독립 분할 규칙 fixture: 0°,30°,120°,359.999°의 sign/nakshatra/pada/D9; 원주 359°↔1° 합 2°; 충8° 포함/사각6.01° 제외.
- 독립 JPL Horizons **DE441** geocentric observer quantity31 apparent ecliptic-of-date: 2000-Jan-01 12:00UT Sun longitude **280.3689092°**, latitude 0.0002381°. Swiss 황경과 차이 <0.001° 확인. 이는 model/좌표 구현 차이를 허용하는 대표 값 검증이며 모든 행성 정밀도 인증이 아니다.
  - 재현: https://ssd.jpl.nasa.gov/api/horizons.api?format=text&COMMAND=%2710%27&CENTER=%27500%40399%27&EPHEM_TYPE=%27OBSERVER%27&START_TIME=%272000-01-01%2012%3A00%27&STOP_TIME=%272000-01-01%2012%3A01%27&STEP_SIZE=%271%20m%27&QUANTITIES=%2731%27
  - 정의/좌표: https://ssd.jpl.nasa.gov/horizons/manual.html
  - C API/시간척도/고위도 fallback 정의: https://www.astro.com/ftp/swisseph/doc/swephprg.2.10.htm

`npm run build`: 성공, astronomy 관련 경고 없음. `npx tsc --noEmit`: 성공.

실제 production `vite preview` + Playwright의 설치된 **Edge(msedge), headless**에서 module Worker에 합성 Greenwich J2000 입력을 전달했다. 네 체계 실제 결과와 errors=[] 확인. 네트워크는 같은 origin index/css/js/worker 및 `/ephemeris/swisseph.js`, `.data`, `.wasm` 3개만 관측; pageerror=[]였다. 이는 wrapper 재실행 수치 비교가 아닌 배포 자산/Worker 통합 증거다. 테스트 중 브라우저 설치는 추가하지 않았다.

확인하지 않은 것: 전 행성/ASC의 독립 정밀 수치 대조, 장기간 leap/EOP/ΔT 모형 정확도, 수동 실제기기 사용성. 출생시간 미상의 날짜 전체 불변 황경을 계산하지 않고 명시적으로 unavailable 처리한다. 천문 결과가 점성 해석의 과학적 타당성을 입증하지 않는다.

## 최종 보정
Western 출력에서 Vedic 전용 nakshatra/pada/D9 필드를 제거했다. Lahiri 표시값은 mean ayanamsa로 명시했고, 계산 자체는 Swiss sidereal flag를 사용한다. 초기 자산 로딩 거부 시 promise cache를 비워 재시도 가능하게 했다. vendor-source/README.md와 두 source archive를 추가했으며 wrapper source archive의 generated js/wasm/data가 실제 배포 파일과 SHA256 전부 일치함을 확인했다. 소스 재컴파일은 하지 않았다. public/ephemeris/NOTICE.txt는 upstream의 부정확한 상업용 문구와 AGPL의 상업 이용 가능성을 구별한다.
