Add v0.10.0: aberration, DE apparent, angular separation, stellar parallax
Annual stellar aberration (~20 arcsec) added to all 6 existing _apparent() functions via classical first-order v/c projection (Ron & Vondrak). Earth velocity sourced from VSOP87 xyz[3..5] (analytic) or DE numerical differentiation. New functions (106 -> 114): - eq_angular_distance(): Vincenty formula, stable at 0 and 180 deg - eq_within_cone(): cosine shortcut for fast cone-search predicate - <-> operator on equatorial type - 6 DE apparent variants with VSOP87 fallback: planet/sun/moon_observe_apparent_de(), planet/moon_equatorial_apparent_de(), small_body_observe_apparent_de() Stellar parallax now functional in star_observe_pm() and star_equatorial_pm() — Green (1985) Eq. 11.3 displacement using Earth heliocentric position from VSOP87. All 19 regression suites pass (18 existing + new aberration suite).
This commit is contained in:
parent
3a378f572a
commit
b0741c553b
6
Makefile
6
Makefile
@ -7,7 +7,8 @@ DATA = sql/pg_orrery--0.1.0.sql sql/pg_orrery--0.2.0.sql sql/pg_orrery--0.1.0--0
|
||||
sql/pg_orrery--0.6.0.sql sql/pg_orrery--0.5.0--0.6.0.sql \
|
||||
sql/pg_orrery--0.7.0.sql sql/pg_orrery--0.6.0--0.7.0.sql \
|
||||
sql/pg_orrery--0.8.0.sql sql/pg_orrery--0.7.0--0.8.0.sql \
|
||||
sql/pg_orrery--0.9.0.sql sql/pg_orrery--0.8.0--0.9.0.sql
|
||||
sql/pg_orrery--0.9.0.sql sql/pg_orrery--0.8.0--0.9.0.sql \
|
||||
sql/pg_orrery--0.10.0.sql sql/pg_orrery--0.9.0--0.10.0.sql
|
||||
|
||||
# Our extension C sources
|
||||
OBJS = src/pg_orrery.o src/tle_type.o src/eci_type.o src/observer_type.o \
|
||||
@ -38,7 +39,8 @@ OBJS += $(SGP4_OBJS)
|
||||
# Regression tests
|
||||
REGRESS = tle_parse sgp4_propagate coord_transforms pass_prediction gist_index convenience \
|
||||
star_observe kepler_comet planet_observe moon_observe lambert_transfer \
|
||||
de_ephemeris od_fit spgist_tle orbital_elements equatorial refraction vallado_518
|
||||
de_ephemeris od_fit spgist_tle orbital_elements equatorial refraction \
|
||||
aberration vallado_518
|
||||
REGRESS_OPTS = --inputdir=test
|
||||
|
||||
# Pure C — no C++ runtime needed. LAPACK for OD solver (dgelss_).
|
||||
|
||||
@ -0,0 +1,147 @@
|
||||
# Message 003
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| From | pg-orrery |
|
||||
| To | astrolock-api |
|
||||
| Date | 2026-02-22T05:15:00-07:00 |
|
||||
| Re | v0.10.0 ready + answers to your three questions |
|
||||
|
||||
---
|
||||
|
||||
## Nice work on the v0.9.0 integration
|
||||
|
||||
The satellite CTE restructure is exactly right — single `sgp4_propagate_safe()` into two LATERAL joins avoids the double-propagation trap. And good call keeping `moon_observe()` for alt/az rather than switching to `_apparent()` for the Moon — 1.3s of light-time is below noise for everything except interferometry.
|
||||
|
||||
One note on the proper motion deferral: you're right that ~50"/25yr is below rotor accuracy for most stars, but Barnard's Star is 258"/25yr. If anyone ever points a rotor at Barnard's, they'll miss by 4 arcmin. Low priority, but something to seed when you eventually do the schema migration.
|
||||
|
||||
## v0.10.0 is ready
|
||||
|
||||
Just finished. All 19 regression suites pass. Not tagged yet (still on `phase/spgist-orbital-trie`), but the code and SQL migration are committed.
|
||||
|
||||
**8 new SQL functions** (106 -> 114) + 1 new operator:
|
||||
|
||||
### What changed in functions you already use
|
||||
|
||||
**This is the important bit.** The `_apparent()` functions you integrated in v0.9.0 now include **annual stellar aberration** (~20 arcsec) on top of the light-time correction they already had. This is a physics improvement, not a breaking API change — same function signatures, same return types, more accurate positions.
|
||||
|
||||
What this means for Craft's live positions:
|
||||
- `planet_observe_apparent()` — now includes aberration. Jupiter shifts by ~29" combined (light-time + aberration). Your LiveTracker will be ~20" more accurate automatically.
|
||||
- `sun_observe_apparent()` — aberration adds ~15" in elevation
|
||||
- `moon_equatorial_apparent()` — aberration adds ~22" in RA
|
||||
- `planet_equatorial_apparent()` — same combined correction
|
||||
|
||||
The underlying `_observe()` and `_equatorial()` (geometric) functions are unchanged.
|
||||
|
||||
### New stuff
|
||||
|
||||
| Function | What it does |
|
||||
|----------|--------------|
|
||||
| `eq_angular_distance(equatorial, equatorial)` | Angular separation in degrees (Vincenty formula, stable at 0 and 180 deg) |
|
||||
| `eq_within_cone(equatorial, equatorial, float8)` | Fast cone-search predicate (cosine shortcut) |
|
||||
| `<->` operator on equatorial | Operator form of `eq_angular_distance` |
|
||||
| `planet_observe_apparent_de(int4, observer, timestamptz)` | DE apparent with aberration (falls back to VSOP87) |
|
||||
| `sun_observe_apparent_de(observer, timestamptz)` | Same for Sun |
|
||||
| `moon_observe_apparent_de(observer, timestamptz)` | Same for Moon |
|
||||
| `planet_equatorial_apparent_de(int4, timestamptz)` | DE apparent RA/Dec with aberration |
|
||||
| `moon_equatorial_apparent_de(timestamptz)` | DE apparent Moon RA/Dec |
|
||||
| `small_body_observe_apparent_de(orbital_elements, observer, timestamptz)` | DE apparent for comets/asteroids |
|
||||
|
||||
**Stellar parallax** is also now functional in `star_observe_pm()` and `star_equatorial_pm()`. The `parallax_mas` parameter that was previously `(void)`-cast now applies the Green (1985) displacement using Earth's heliocentric position from VSOP87. Proxima Centauri (768 mas) shows 1.02 arcsec displacement in our tests. Matters only for the nearest stars — but when you eventually add the proper motion columns, the plumbing is ready.
|
||||
|
||||
### Angular separation use cases for Craft
|
||||
|
||||
The `<->` operator and `eq_within_cone()` could be useful for Craft:
|
||||
|
||||
```sql
|
||||
-- "What's near Jupiter right now?"
|
||||
SELECT co.name,
|
||||
planet_equatorial(5, NOW()) <-> eci_to_equatorial_geo(
|
||||
sgp4_propagate_safe(co.tle, NOW()), NOW()
|
||||
) AS separation_deg
|
||||
FROM celestial_object co
|
||||
WHERE co.tle IS NOT NULL
|
||||
AND eq_within_cone(
|
||||
eci_to_equatorial_geo(sgp4_propagate_safe(co.tle, NOW()), NOW()),
|
||||
planet_equatorial(5, NOW()),
|
||||
10.0 -- within 10 degrees
|
||||
)
|
||||
ORDER BY separation_deg;
|
||||
```
|
||||
|
||||
### Upgrade path
|
||||
|
||||
```sql
|
||||
ALTER EXTENSION pg_orrery UPDATE TO '0.10.0';
|
||||
```
|
||||
|
||||
The migration chains from 0.9.0. Since you chained directly from 0.3.0 to 0.9.0, the path is: your current 0.9.0 -> 0.10.0 via `pg_orrery--0.9.0--0.10.0.sql`.
|
||||
|
||||
## Answers to your questions
|
||||
|
||||
### 1. `orbital_elements` constructor from floats
|
||||
|
||||
Yes, this is straightforward. The type is 9 floats internally:
|
||||
|
||||
```
|
||||
(epoch_jd, a_or_q, e, inc_rad, omega_rad, Omega_rad, tp_jd, H, G)
|
||||
```
|
||||
|
||||
Today you can construct it with the tuple syntax:
|
||||
|
||||
```sql
|
||||
SELECT small_body_equatorial(
|
||||
format('(%s,%s,%s,%s,%s,%s,%s,%s,%s)',
|
||||
co.epoch_jd, co.q_au, co.e, co.inc_rad,
|
||||
co.arg_peri_rad, co.node_rad, co.tp_jd, co.h_mag, co.g_slope
|
||||
)::orbital_elements,
|
||||
NOW()
|
||||
) FROM celestial_object co WHERE co.object_type = 'comet';
|
||||
```
|
||||
|
||||
But a proper SQL constructor function would be cleaner:
|
||||
|
||||
```sql
|
||||
SELECT eq_ra(small_body_equatorial(
|
||||
make_orbital_elements(epoch_jd, q, e, inc, omega, node, tp, h, g),
|
||||
NOW()
|
||||
));
|
||||
```
|
||||
|
||||
I'll add `make_orbital_elements(float8 x 9) -> orbital_elements` to the roadmap. Low effort, high convenience for your use case.
|
||||
|
||||
### 2. `galilean_equatorial()`
|
||||
|
||||
Feasible. The underlying `galilean_observe()` already computes geocentric positions via L1.2 theory. Adding equatorial output follows the same pattern as `planet_equatorial()` — convert the geocentric ecliptic position to equatorial J2000, precess to date. Same for `saturn_moon_equatorial()`, `uranus_moon_equatorial()`, `mars_moon_equatorial()`.
|
||||
|
||||
The interesting question is whether to return Jupiter-centric or geocentric RA/Dec. For telescope pointing you want geocentric (where to point the scope). For Galilean moon event prediction (transits, shadows) you want Jupiter-centric offsets. Both are useful.
|
||||
|
||||
I'll plan geocentric `galilean_equatorial(int4, timestamptz)` for the next version. Probably paired with the other moon families.
|
||||
|
||||
### 3. Refracted pass accuracy
|
||||
|
||||
We don't have a formal benchmark against Heavens-Above/N2YO yet, but here's what we can say:
|
||||
|
||||
**The physics.** Bennett (1982) refraction at the geometric horizon (0 deg) is 0.5695 deg. Our refracted pass finder uses this as the effective horizon — a satellite is "visible" when its geometric elevation exceeds -0.569 deg. The standard (non-refracted) finder uses 0 deg.
|
||||
|
||||
**The ~35s shift.** For a typical ISS pass, the satellite moves ~7 deg/min near the horizon. At 0.569 deg of refraction: `0.569 / 7 * 60 = ~4.9 seconds` per horizon crossing, so ~10 seconds total (AOS earlier + LOS later). The "~35 seconds" figure in message 001 was an upper bound — actual shift depends on pass geometry. Low-elevation grazing passes see more shift; overhead passes less.
|
||||
|
||||
**Regression test 14** (`refraction.out:167-183`) verifies that refracted passes find >= standard passes over a 7-day ISS window. This catches the case where refraction makes previously-invisible grazing passes appear above the effective horizon.
|
||||
|
||||
**Validation approach.** The cleanest comparison would be:
|
||||
1. Pick 5 well-predicted ISS passes from Heavens-Above for a specific location
|
||||
2. Run both `predict_passes()` and `predict_passes_refracted()` for the same TLE + location + window
|
||||
3. Compare AOS/LOS times against Heavens-Above (which uses atmospheric refraction)
|
||||
|
||||
Heavens-Above doesn't publish their exact refraction model, but they do account for it. N2YO likely uses geometric horizon (no refraction). If you run this comparison and share results, I'll add the vectors to the test suite.
|
||||
|
||||
**One caveat**: TLE epoch staleness dominates over refraction for most prediction accuracy questions. A 3-day-old TLE can be off by 1-10 seconds in pass timing. Refraction correction only matters when the TLE is fresh (<24h old) and you need sub-minute AOS/LOS accuracy — which is exactly the rotor pre-positioning use case.
|
||||
|
||||
---
|
||||
|
||||
**Next steps for recipient:**
|
||||
- [ ] Rebuild db image with v0.10.0 when ready (not tagged yet, use `phase/spgist-orbital-trie` HEAD)
|
||||
- [ ] `ALTER EXTENSION pg_orrery UPDATE TO '0.10.0'` — aberration improvement is automatic
|
||||
- [ ] Consider `eq_within_cone()` for "what's near X" queries in the sky engine
|
||||
- [ ] Run Heavens-Above comparison for 5 ISS passes if time permits
|
||||
- [ ] Let us know if `make_orbital_elements()` constructor is high priority
|
||||
@ -1,4 +1,4 @@
|
||||
comment = 'A database orrery — celestial mechanics types and functions for PostgreSQL'
|
||||
default_version = '0.9.0'
|
||||
default_version = '0.10.0'
|
||||
module_pathname = '$libdir/pg_orrery'
|
||||
relocatable = true
|
||||
|
||||
1339
sql/pg_orrery--0.10.0.sql
Normal file
1339
sql/pg_orrery--0.10.0.sql
Normal file
File diff suppressed because it is too large
Load Diff
63
sql/pg_orrery--0.9.0--0.10.0.sql
Normal file
63
sql/pg_orrery--0.9.0--0.10.0.sql
Normal file
@ -0,0 +1,63 @@
|
||||
-- pg_orrery 0.9.0 -> 0.10.0 migration
|
||||
--
|
||||
-- Adds annual aberration to existing _apparent() functions,
|
||||
-- 6 new _apparent_de() variants, equatorial angular separation
|
||||
-- operator and cone predicate, and stellar annual parallax.
|
||||
|
||||
-- ============================================================
|
||||
-- Equatorial angular distance and cone search
|
||||
-- ============================================================
|
||||
|
||||
CREATE FUNCTION eq_angular_distance(equatorial, equatorial) RETURNS float8
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION eq_angular_distance(equatorial, equatorial) IS
|
||||
'Angular separation in degrees between two equatorial positions. Vincenty formula (stable at 0 and 180 degrees).';
|
||||
|
||||
CREATE FUNCTION eq_within_cone(equatorial, equatorial, float8) RETURNS bool
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION eq_within_cone(equatorial, equatorial, float8) IS
|
||||
'True if first position is within radius_deg of second position. Cosine shortcut for fast rejection.';
|
||||
|
||||
CREATE OPERATOR <-> (
|
||||
LEFTARG = equatorial,
|
||||
RIGHTARG = equatorial,
|
||||
FUNCTION = eq_angular_distance,
|
||||
COMMUTATOR = <->
|
||||
);
|
||||
COMMENT ON OPERATOR <-> (equatorial, equatorial) IS
|
||||
'Angular separation in degrees between two equatorial positions.';
|
||||
|
||||
|
||||
-- ============================================================
|
||||
-- DE apparent observation functions (STABLE, light-time + aberration)
|
||||
-- ============================================================
|
||||
|
||||
CREATE FUNCTION planet_observe_apparent_de(int4, observer, timestamptz) RETURNS topocentric
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION planet_observe_apparent_de(int4, observer, timestamptz) IS
|
||||
'Observe a planet with light-time correction and annual aberration via JPL DE (falls back to VSOP87).';
|
||||
|
||||
CREATE FUNCTION sun_observe_apparent_de(observer, timestamptz) RETURNS topocentric
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION sun_observe_apparent_de(observer, timestamptz) IS
|
||||
'Observe the Sun with aberration via JPL DE (falls back to VSOP87).';
|
||||
|
||||
CREATE FUNCTION moon_observe_apparent_de(observer, timestamptz) RETURNS topocentric
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION moon_observe_apparent_de(observer, timestamptz) IS
|
||||
'Observe the Moon with light-time correction and annual aberration via JPL DE (falls back to ELP2000-82B).';
|
||||
|
||||
CREATE FUNCTION planet_equatorial_apparent_de(int4, timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION planet_equatorial_apparent_de(int4, timestamptz) IS
|
||||
'Geocentric apparent RA/Dec of a planet with light-time correction and annual aberration via JPL DE (falls back to VSOP87).';
|
||||
|
||||
CREATE FUNCTION moon_equatorial_apparent_de(timestamptz) RETURNS equatorial
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION moon_equatorial_apparent_de(timestamptz) IS
|
||||
'Geocentric apparent RA/Dec of the Moon with light-time correction and annual aberration via JPL DE (falls back to ELP2000-82B).';
|
||||
|
||||
CREATE FUNCTION small_body_observe_apparent_de(orbital_elements, observer, timestamptz) RETURNS topocentric
|
||||
AS 'MODULE_PATHNAME' LANGUAGE C STABLE STRICT PARALLEL SAFE;
|
||||
COMMENT ON FUNCTION small_body_observe_apparent_de(orbital_elements, observer, timestamptz) IS
|
||||
'Observe a comet/asteroid with light-time correction and annual aberration. Earth position via JPL DE (falls back to VSOP87).';
|
||||
118
src/astro_math.h
118
src/astro_math.h
@ -242,6 +242,124 @@ geocentric_to_equatorial(const double geo_ecl_au[3], double jd,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Apply classical annual aberration to J2000 equatorial RA/Dec.
|
||||
*
|
||||
* The apparent direction of an object is displaced toward the apex of
|
||||
* Earth's motion by v/c (~20.5 arcseconds maximum).
|
||||
*
|
||||
* earth_vel_equ: Earth's velocity in equatorial J2000 (AU/day)
|
||||
* ra, dec: pointers to J2000 RA and Dec (radians), modified in-place
|
||||
*
|
||||
* Reference: Green (1985) "Spherical Astronomy", Eq. 10.22
|
||||
*/
|
||||
static inline void
|
||||
apply_annual_aberration(const double earth_vel_equ[3],
|
||||
double *ra, double *dec)
|
||||
{
|
||||
double cos_ra = cos(*ra);
|
||||
double sin_ra = sin(*ra);
|
||||
double cos_dec = cos(*dec);
|
||||
double sin_dec = sin(*dec);
|
||||
double d_ra, d_dec;
|
||||
|
||||
/* Near celestial poles, cos(dec) -> 0, dRA diverges. Skip. */
|
||||
if (fabs(cos_dec) < 1e-12)
|
||||
return;
|
||||
|
||||
d_ra = (-earth_vel_equ[0] * sin_ra + earth_vel_equ[1] * cos_ra)
|
||||
/ (C_LIGHT_AU_DAY * cos_dec);
|
||||
|
||||
d_dec = (-earth_vel_equ[0] * cos_ra * sin_dec
|
||||
- earth_vel_equ[1] * sin_ra * sin_dec
|
||||
+ earth_vel_equ[2] * cos_dec)
|
||||
/ C_LIGHT_AU_DAY;
|
||||
|
||||
*ra += d_ra;
|
||||
*dec += d_dec;
|
||||
|
||||
if (*ra < 0.0)
|
||||
*ra += 2.0 * M_PI;
|
||||
if (*ra >= 2.0 * M_PI)
|
||||
*ra -= 2.0 * M_PI;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Geocentric observation pipeline with annual aberration.
|
||||
*
|
||||
* Same as observe_from_geocentric() plus aberration correction applied
|
||||
* in J2000 equatorial coordinates before precessing to date.
|
||||
*
|
||||
* earth_vel_ecl: Earth's velocity in ecliptic J2000 (AU/day)
|
||||
* — gets rotated to equatorial internally.
|
||||
*/
|
||||
static inline void
|
||||
observe_from_geocentric_aberrated(const double geo_ecl_au[3], double jd,
|
||||
const pg_observer *obs,
|
||||
const double earth_vel_ecl[3],
|
||||
pg_topocentric *result)
|
||||
{
|
||||
double geo_equ[3];
|
||||
double vel_equ[3];
|
||||
double ra_j2000, dec_j2000, geo_dist;
|
||||
double ra_date, dec_date;
|
||||
double gmst_val, lst, ha;
|
||||
double az, el;
|
||||
|
||||
ecliptic_to_equatorial(geo_ecl_au, geo_equ);
|
||||
ecliptic_to_equatorial(earth_vel_ecl, vel_equ);
|
||||
|
||||
cartesian_to_spherical(geo_equ, &ra_j2000, &dec_j2000, &geo_dist);
|
||||
|
||||
apply_annual_aberration(vel_equ, &ra_j2000, &dec_j2000);
|
||||
|
||||
precess_j2000_to_date(jd, ra_j2000, dec_j2000, &ra_date, &dec_date);
|
||||
|
||||
gmst_val = gmst_from_jd(jd);
|
||||
lst = gmst_val + obs->lon;
|
||||
ha = lst - ra_date;
|
||||
|
||||
equatorial_to_horizontal(ha, dec_date, obs->lat, &az, &el);
|
||||
|
||||
result->azimuth = az;
|
||||
result->elevation = el;
|
||||
result->range_km = geo_dist * AU_KM;
|
||||
result->range_rate = 0.0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Geocentric ecliptic J2000 (AU) -> equatorial RA/Dec of date, with aberration.
|
||||
*
|
||||
* Same as geocentric_to_equatorial() plus annual aberration before precession.
|
||||
* earth_vel_ecl: Earth's velocity in ecliptic J2000 (AU/day).
|
||||
*/
|
||||
static inline void
|
||||
geocentric_to_equatorial_aberrated(const double geo_ecl_au[3], double jd,
|
||||
const double earth_vel_ecl[3],
|
||||
pg_equatorial *result)
|
||||
{
|
||||
double geo_equ[3];
|
||||
double vel_equ[3];
|
||||
double ra_j2000, dec_j2000, geo_dist;
|
||||
double ra_date, dec_date;
|
||||
|
||||
ecliptic_to_equatorial(geo_ecl_au, geo_equ);
|
||||
ecliptic_to_equatorial(earth_vel_ecl, vel_equ);
|
||||
|
||||
cartesian_to_spherical(geo_equ, &ra_j2000, &dec_j2000, &geo_dist);
|
||||
|
||||
apply_annual_aberration(vel_equ, &ra_j2000, &dec_j2000);
|
||||
|
||||
precess_j2000_to_date(jd, ra_j2000, dec_j2000, &ra_date, &dec_date);
|
||||
|
||||
result->ra = ra_date;
|
||||
result->dec = dec_date;
|
||||
result->distance = geo_dist * AU_KM;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TEME position (km) to equatorial RA/Dec, geocentric.
|
||||
*
|
||||
|
||||
403
src/de_funcs.c
403
src/de_funcs.c
@ -27,6 +27,7 @@
|
||||
#include "eph_provider.h"
|
||||
#include "vsop87.h"
|
||||
#include "elp82b.h"
|
||||
#include "kepler.h"
|
||||
#include "lambert.h"
|
||||
#include "l12.h"
|
||||
#include "tass17.h"
|
||||
@ -49,6 +50,12 @@ PG_FUNCTION_INFO_V1(uranus_moon_observe_de);
|
||||
PG_FUNCTION_INFO_V1(mars_moon_observe_de);
|
||||
PG_FUNCTION_INFO_V1(planet_equatorial_de);
|
||||
PG_FUNCTION_INFO_V1(moon_equatorial_de);
|
||||
PG_FUNCTION_INFO_V1(planet_observe_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(sun_observe_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(moon_observe_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(planet_equatorial_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(moon_equatorial_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(small_body_observe_apparent_de);
|
||||
PG_FUNCTION_INFO_V1(pg_orrery_ephemeris_info);
|
||||
|
||||
|
||||
@ -704,6 +711,402 @@ moon_equatorial_de(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* Earth velocity via DE (numerical differentiation) or VSOP87 (analytic).
|
||||
*
|
||||
* DE path: central difference (earth(jd+dt) - earth(jd-dt)) / (2*dt)
|
||||
* VSOP87 path: analytic velocity from earth_xyz[3..5]
|
||||
*
|
||||
* use_de: must match the provider used for position (rule 7).
|
||||
* vel_ecl[3]: output Earth velocity in ecliptic J2000 (AU/day).
|
||||
* ================================================================
|
||||
*/
|
||||
static void
|
||||
earth_velocity_de(double jd, bool use_de, double vel_ecl[3])
|
||||
{
|
||||
if (use_de)
|
||||
{
|
||||
double pos_fwd[6], pos_bwd[6];
|
||||
double dt = 0.01; /* days */
|
||||
|
||||
bool got_fwd = eph_de_earth(jd + dt, pos_fwd);
|
||||
bool got_bwd = eph_de_earth(jd - dt, pos_bwd);
|
||||
|
||||
if (got_fwd && got_bwd)
|
||||
{
|
||||
vel_ecl[0] = (pos_fwd[0] - pos_bwd[0]) / (2.0 * dt);
|
||||
vel_ecl[1] = (pos_fwd[1] - pos_bwd[1]) / (2.0 * dt);
|
||||
vel_ecl[2] = (pos_fwd[2] - pos_bwd[2]) / (2.0 * dt);
|
||||
return;
|
||||
}
|
||||
/* DE boundary straddled — fall through to VSOP87 */
|
||||
}
|
||||
|
||||
{
|
||||
double earth_xyz[6];
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
vel_ecl[0] = earth_xyz[3];
|
||||
vel_ecl[1] = earth_xyz[4];
|
||||
vel_ecl[2] = earth_xyz[5];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* planet_observe_apparent_de(body_id int, observer, timestamptz) -> topocentric
|
||||
*
|
||||
* DE variant of planet_observe_apparent(). STABLE.
|
||||
* Light-time + annual aberration. Rule 7.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
planet_observe_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 body_id = PG_GETARG_INT32(0);
|
||||
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
|
||||
int64 ts = PG_GETARG_INT64(2);
|
||||
double jd;
|
||||
double earth_xyz[6];
|
||||
double planet_xyz[6];
|
||||
double geo_ecl[3];
|
||||
double geo_dist, tau;
|
||||
double vel_ecl[3];
|
||||
pg_topocentric *result;
|
||||
bool have_de;
|
||||
|
||||
if (body_id < BODY_MERCURY || body_id > BODY_NEPTUNE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("planet_observe_apparent_de: body_id %d must be 1-8 (Mercury-Neptune)",
|
||||
body_id)));
|
||||
|
||||
if (body_id == BODY_EARTH)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot observe Earth from Earth")));
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
/* Rule 7: both planet and Earth from same provider */
|
||||
have_de = eph_de_planet(body_id, jd, planet_xyz) &&
|
||||
eph_de_earth(jd, earth_xyz);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
int vsop_body = body_id - 1;
|
||||
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable for this query, falling back to VSOP87")));
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
GetVsop87Coor(jd, vsop_body, planet_xyz);
|
||||
}
|
||||
|
||||
/* Geometric geocentric distance */
|
||||
geo_ecl[0] = planet_xyz[0] - earth_xyz[0];
|
||||
geo_ecl[1] = planet_xyz[1] - earth_xyz[1];
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
geo_dist = sqrt(geo_ecl[0]*geo_ecl[0] + geo_ecl[1]*geo_ecl[1] + geo_ecl[2]*geo_ecl[2]);
|
||||
|
||||
/* Retarded planet position (same provider) */
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
|
||||
if (have_de)
|
||||
eph_de_planet(body_id, jd - tau, planet_xyz);
|
||||
else
|
||||
GetVsop87Coor(jd - tau, body_id - 1, planet_xyz);
|
||||
|
||||
geo_ecl[0] = planet_xyz[0] - earth_xyz[0];
|
||||
geo_ecl[1] = planet_xyz[1] - earth_xyz[1];
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
|
||||
/* Earth velocity for aberration */
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* sun_observe_apparent_de(observer, timestamptz) -> topocentric
|
||||
*
|
||||
* DE variant of sun_observe_apparent(). STABLE.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
sun_observe_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(0);
|
||||
int64 ts = PG_GETARG_INT64(1);
|
||||
double jd;
|
||||
double earth_xyz[6];
|
||||
double geo_ecl[3];
|
||||
double vel_ecl[3];
|
||||
pg_topocentric *result;
|
||||
bool have_de;
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
have_de = eph_de_earth(jd, earth_xyz);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable, falling back to VSOP87")));
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
}
|
||||
|
||||
geo_ecl[0] = -earth_xyz[0];
|
||||
geo_ecl[1] = -earth_xyz[1];
|
||||
geo_ecl[2] = -earth_xyz[2];
|
||||
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* moon_observe_apparent_de(observer, timestamptz) -> topocentric
|
||||
*
|
||||
* DE variant with light-time + aberration. STABLE.
|
||||
* Moon position from DE, Earth velocity from DE (numerical) or VSOP87.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
moon_observe_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(0);
|
||||
int64 ts = PG_GETARG_INT64(1);
|
||||
double jd;
|
||||
double moon_ecl[3];
|
||||
double geo_dist, tau;
|
||||
double vel_ecl[3];
|
||||
pg_topocentric *result;
|
||||
bool have_de;
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
have_de = eph_de_moon(jd, moon_ecl);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable, falling back to ELP2000-82B")));
|
||||
|
||||
GetElp82bCoor(jd, moon_ecl);
|
||||
}
|
||||
|
||||
/* Light-time correction */
|
||||
geo_dist = sqrt(moon_ecl[0]*moon_ecl[0] + moon_ecl[1]*moon_ecl[1] + moon_ecl[2]*moon_ecl[2]);
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
|
||||
if (have_de)
|
||||
eph_de_moon(jd - tau, moon_ecl);
|
||||
else
|
||||
GetElp82bCoor(jd - tau, moon_ecl);
|
||||
|
||||
/* Earth velocity for aberration (DE numerical or VSOP87 analytic) */
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric_aberrated(moon_ecl, jd, obs, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* planet_equatorial_apparent_de(body_id int, timestamptz) -> equatorial
|
||||
*
|
||||
* DE variant with light-time + aberration. STABLE.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
planet_equatorial_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int32 body_id = PG_GETARG_INT32(0);
|
||||
int64 ts = PG_GETARG_INT64(1);
|
||||
double jd;
|
||||
double earth_xyz[6];
|
||||
double planet_xyz[6];
|
||||
double geo_ecl[3];
|
||||
double geo_dist, tau;
|
||||
double vel_ecl[3];
|
||||
pg_equatorial *result;
|
||||
bool have_de;
|
||||
|
||||
if (body_id < BODY_MERCURY || body_id > BODY_NEPTUNE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||
errmsg("planet_equatorial_apparent_de: body_id %d must be 1-8 (Mercury-Neptune)",
|
||||
body_id)));
|
||||
|
||||
if (body_id == BODY_EARTH)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot observe Earth from Earth")));
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
have_de = eph_de_planet(body_id, jd, planet_xyz) &&
|
||||
eph_de_earth(jd, earth_xyz);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
int vsop_body = body_id - 1;
|
||||
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable for this query, falling back to VSOP87")));
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
GetVsop87Coor(jd, vsop_body, planet_xyz);
|
||||
}
|
||||
|
||||
geo_ecl[0] = planet_xyz[0] - earth_xyz[0];
|
||||
geo_ecl[1] = planet_xyz[1] - earth_xyz[1];
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
geo_dist = sqrt(geo_ecl[0]*geo_ecl[0] + geo_ecl[1]*geo_ecl[1] + geo_ecl[2]*geo_ecl[2]);
|
||||
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
|
||||
if (have_de)
|
||||
eph_de_planet(body_id, jd - tau, planet_xyz);
|
||||
else
|
||||
GetVsop87Coor(jd - tau, body_id - 1, planet_xyz);
|
||||
|
||||
geo_ecl[0] = planet_xyz[0] - earth_xyz[0];
|
||||
geo_ecl[1] = planet_xyz[1] - earth_xyz[1];
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
geocentric_to_equatorial_aberrated(geo_ecl, jd, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* moon_equatorial_apparent_de(timestamptz) -> equatorial
|
||||
*
|
||||
* DE variant with light-time + aberration. STABLE.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
moon_equatorial_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int64 ts = PG_GETARG_INT64(0);
|
||||
double jd;
|
||||
double moon_ecl[3];
|
||||
double geo_dist, tau;
|
||||
double vel_ecl[3];
|
||||
pg_equatorial *result;
|
||||
bool have_de;
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
have_de = eph_de_moon(jd, moon_ecl);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable, falling back to ELP2000-82B")));
|
||||
|
||||
GetElp82bCoor(jd, moon_ecl);
|
||||
}
|
||||
|
||||
geo_dist = sqrt(moon_ecl[0]*moon_ecl[0] + moon_ecl[1]*moon_ecl[1] + moon_ecl[2]*moon_ecl[2]);
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
|
||||
if (have_de)
|
||||
eph_de_moon(jd - tau, moon_ecl);
|
||||
else
|
||||
GetElp82bCoor(jd - tau, moon_ecl);
|
||||
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
geocentric_to_equatorial_aberrated(moon_ecl, jd, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* small_body_observe_apparent_de(orbital_elements, observer, timestamptz) -> topocentric
|
||||
*
|
||||
* DE variant of small_body_observe_apparent(). Uses DE for Earth
|
||||
* position (rule 7 satisfied: body is always Kepler, Earth from
|
||||
* best available provider). STABLE.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
small_body_observe_apparent_de(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_orbital_elements *oe = (pg_orbital_elements *) PG_GETARG_POINTER(0);
|
||||
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
|
||||
int64 ts = PG_GETARG_INT64(2);
|
||||
double jd;
|
||||
double body_helio[3];
|
||||
double earth_xyz[6];
|
||||
double geo_ecl[3];
|
||||
double geo_dist, tau;
|
||||
double vel_ecl[3];
|
||||
pg_topocentric *result;
|
||||
bool have_de;
|
||||
|
||||
jd = timestamptz_to_jd(ts);
|
||||
|
||||
kepler_position(oe->q, oe->e, oe->inc, oe->arg_peri, oe->raan,
|
||||
oe->tp, jd, body_helio);
|
||||
|
||||
have_de = eph_de_earth(jd, earth_xyz);
|
||||
|
||||
if (!have_de)
|
||||
{
|
||||
if (eph_de_available())
|
||||
ereport(NOTICE,
|
||||
(errmsg("DE ephemeris unavailable, falling back to VSOP87")));
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
}
|
||||
|
||||
geo_ecl[0] = body_helio[0] - earth_xyz[0];
|
||||
geo_ecl[1] = body_helio[1] - earth_xyz[1];
|
||||
geo_ecl[2] = body_helio[2] - earth_xyz[2];
|
||||
geo_dist = sqrt(geo_ecl[0]*geo_ecl[0] + geo_ecl[1]*geo_ecl[1] + geo_ecl[2]*geo_ecl[2]);
|
||||
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
kepler_position(oe->q, oe->e, oe->inc, oe->arg_peri, oe->raan,
|
||||
oe->tp, jd - tau, body_helio);
|
||||
|
||||
geo_ecl[0] = body_helio[0] - earth_xyz[0];
|
||||
geo_ecl[1] = body_helio[1] - earth_xyz[1];
|
||||
geo_ecl[2] = body_helio[2] - earth_xyz[2];
|
||||
|
||||
earth_velocity_de(jd, have_de, vel_ecl);
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, vel_ecl, result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* pg_orrery_ephemeris_info() -> RECORD
|
||||
*
|
||||
|
||||
@ -46,6 +46,10 @@ PG_FUNCTION_INFO_V1(planet_equatorial);
|
||||
PG_FUNCTION_INFO_V1(sun_equatorial);
|
||||
PG_FUNCTION_INFO_V1(moon_equatorial);
|
||||
|
||||
/* Angular distance and cone search */
|
||||
PG_FUNCTION_INFO_V1(eq_angular_distance);
|
||||
PG_FUNCTION_INFO_V1(eq_within_cone);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Static helper -- observer geodetic to ECEF.
|
||||
@ -364,3 +368,69 @@ moon_equatorial(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* eq_angular_distance(equatorial, equatorial) -> float8
|
||||
*
|
||||
* Angular separation in degrees between two equatorial positions.
|
||||
* Uses the Vincenty formula (numerically stable at all separations
|
||||
* including 0 and 180 degrees, unlike the haversine or dot product).
|
||||
*
|
||||
* Vincenty: atan2(num, den) where
|
||||
* num = sqrt((cos d2 sin dRA)^2 + (cos d1 sin d2 - sin d1 cos d2 cos dRA)^2)
|
||||
* den = sin d1 sin d2 + cos d1 cos d2 cos dRA
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
eq_angular_distance(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_equatorial *a = (pg_equatorial *) PG_GETARG_POINTER(0);
|
||||
pg_equatorial *b = (pg_equatorial *) PG_GETARG_POINTER(1);
|
||||
double d_ra, cos_d_ra, sin_d_ra;
|
||||
double sin_d1, cos_d1, sin_d2, cos_d2;
|
||||
double num1, num2, num, den;
|
||||
|
||||
d_ra = b->ra - a->ra;
|
||||
cos_d_ra = cos(d_ra);
|
||||
sin_d_ra = sin(d_ra);
|
||||
|
||||
sin_d1 = sin(a->dec);
|
||||
cos_d1 = cos(a->dec);
|
||||
sin_d2 = sin(b->dec);
|
||||
cos_d2 = cos(b->dec);
|
||||
|
||||
num1 = cos_d2 * sin_d_ra;
|
||||
num2 = cos_d1 * sin_d2 - sin_d1 * cos_d2 * cos_d_ra;
|
||||
num = sqrt(num1 * num1 + num2 * num2);
|
||||
den = sin_d1 * sin_d2 + cos_d1 * cos_d2 * cos_d_ra;
|
||||
|
||||
PG_RETURN_FLOAT8(atan2(num, den) * RAD_TO_DEG);
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================
|
||||
* eq_within_cone(equatorial, equatorial, float8) -> bool
|
||||
*
|
||||
* Returns true if the first position is within `radius_deg` degrees
|
||||
* of the second position. Cosine shortcut avoids the expensive
|
||||
* atan2 in the Vincenty formula for most reject cases.
|
||||
* ================================================================
|
||||
*/
|
||||
Datum
|
||||
eq_within_cone(PG_FUNCTION_ARGS)
|
||||
{
|
||||
pg_equatorial *a = (pg_equatorial *) PG_GETARG_POINTER(0);
|
||||
pg_equatorial *b = (pg_equatorial *) PG_GETARG_POINTER(1);
|
||||
double radius = PG_GETARG_FLOAT8(2);
|
||||
double cos_r, d_ra, cos_sep;
|
||||
|
||||
if (radius < 0.0)
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
cos_r = cos(radius * DEG_TO_RAD);
|
||||
|
||||
d_ra = b->ra - a->ra;
|
||||
cos_sep = sin(a->dec) * sin(b->dec)
|
||||
+ cos(a->dec) * cos(b->dec) * cos(d_ra);
|
||||
|
||||
PG_RETURN_BOOL(cos_sep >= cos_r);
|
||||
}
|
||||
|
||||
@ -588,7 +588,7 @@ small_body_observe_apparent(PG_FUNCTION_ARGS)
|
||||
geo_ecl[2] = body_helio[2] - earth_xyz[2];
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric(geo_ecl, jd, obs, result);
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@ -677,7 +677,7 @@ small_body_equatorial_apparent(PG_FUNCTION_ARGS)
|
||||
geo_ecl[2] = body_helio[2] - earth_xyz[2];
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
geocentric_to_equatorial(geo_ecl, jd, result);
|
||||
geocentric_to_equatorial_aberrated(geo_ecl, jd, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ planet_observe_apparent(PG_FUNCTION_ARGS)
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric(geo_ecl, jd, obs, result);
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@ -301,7 +301,7 @@ sun_observe_apparent(PG_FUNCTION_ARGS)
|
||||
geo_ecl[2] = -earth_xyz[2];
|
||||
|
||||
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
|
||||
observe_from_geocentric(geo_ecl, jd, obs, result);
|
||||
observe_from_geocentric_aberrated(geo_ecl, jd, obs, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@ -365,7 +365,7 @@ planet_equatorial_apparent(PG_FUNCTION_ARGS)
|
||||
geo_ecl[2] = planet_xyz[2] - earth_xyz[2];
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
geocentric_to_equatorial(geo_ecl, jd, result);
|
||||
geocentric_to_equatorial_aberrated(geo_ecl, jd, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@ -387,6 +387,7 @@ moon_equatorial_apparent(PG_FUNCTION_ARGS)
|
||||
int64 ts = PG_GETARG_INT64(0);
|
||||
double jd;
|
||||
double moon_ecl[3];
|
||||
double earth_xyz[6];
|
||||
double geo_dist, tau;
|
||||
pg_equatorial *result;
|
||||
|
||||
@ -401,8 +402,11 @@ moon_equatorial_apparent(PG_FUNCTION_ARGS)
|
||||
tau = geo_dist / C_LIGHT_AU_DAY;
|
||||
GetElp82bCoor(jd - tau, moon_ecl);
|
||||
|
||||
/* Earth velocity for aberration (ELP doesn't provide it) */
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
geocentric_to_equatorial(moon_ecl, jd, result);
|
||||
geocentric_to_equatorial_aberrated(moon_ecl, jd, &earth_xyz[3], result);
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "utils/timestamp.h"
|
||||
#include "types.h"
|
||||
#include "astro_math.h"
|
||||
#include "vsop87.h"
|
||||
|
||||
PG_FUNCTION_INFO_V1(star_observe);
|
||||
PG_FUNCTION_INFO_V1(star_observe_safe);
|
||||
@ -197,9 +198,35 @@ star_observe_pm(PG_FUNCTION_ARGS)
|
||||
if (ra_corrected < 0.0)
|
||||
ra_corrected += 2.0 * M_PI;
|
||||
|
||||
/* Parallax and RV accepted for API completeness; positional effect
|
||||
* is sub-arcsecond for all but the nearest stars. */
|
||||
(void) parallax_mas;
|
||||
/* Annual parallax: displace star position by Earth's heliocentric
|
||||
* position projected onto the star direction.
|
||||
* Green (1985), Eq. 11.3: d_RA and d_Dec from parallax. */
|
||||
if (parallax_mas > 0.0)
|
||||
{
|
||||
double earth_xyz[6], earth_equ[3];
|
||||
double p_rad = (parallax_mas / 1000.0) * ARCSEC_TO_RAD;
|
||||
double sin_ra_c = sin(ra_corrected);
|
||||
double cos_ra_c = cos(ra_corrected);
|
||||
double sin_dec_c = sin(dec_corrected);
|
||||
double cos_dec_c = cos(dec_corrected);
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
ecliptic_to_equatorial(earth_xyz, earth_equ);
|
||||
|
||||
/* Parallax displacement (Green 1985 Eq. 11.3) */
|
||||
if (fabs(cos_dec_c) > 1e-12)
|
||||
{
|
||||
ra_corrected += p_rad * (earth_equ[0] * sin_ra_c
|
||||
- earth_equ[1] * cos_ra_c) / cos_dec_c;
|
||||
dec_corrected += p_rad * (earth_equ[0] * cos_ra_c * sin_dec_c
|
||||
+ earth_equ[1] * sin_ra_c * sin_dec_c
|
||||
- earth_equ[2] * cos_dec_c);
|
||||
}
|
||||
|
||||
ra_corrected = fmod(ra_corrected, 2.0 * M_PI);
|
||||
if (ra_corrected < 0.0)
|
||||
ra_corrected += 2.0 * M_PI;
|
||||
}
|
||||
(void) rv_kms;
|
||||
|
||||
precess_j2000_to_date(jd, ra_corrected, dec_corrected, &ra_date, &dec_date);
|
||||
@ -285,6 +312,33 @@ star_equatorial_pm(PG_FUNCTION_ARGS)
|
||||
if (ra_corrected < 0.0)
|
||||
ra_corrected += 2.0 * M_PI;
|
||||
|
||||
/* Annual parallax displacement (Green 1985 Eq. 11.3) */
|
||||
if (parallax_mas > 0.0)
|
||||
{
|
||||
double earth_xyz[6], earth_equ[3];
|
||||
double p_rad = (parallax_mas / 1000.0) * ARCSEC_TO_RAD;
|
||||
double sin_ra_c = sin(ra_corrected);
|
||||
double cos_ra_c = cos(ra_corrected);
|
||||
double sin_dec_c = sin(dec_corrected);
|
||||
double cos_dec_c = cos(dec_corrected);
|
||||
|
||||
GetVsop87Coor(jd, 2, earth_xyz);
|
||||
ecliptic_to_equatorial(earth_xyz, earth_equ);
|
||||
|
||||
if (fabs(cos_dec_c) > 1e-12)
|
||||
{
|
||||
ra_corrected += p_rad * (earth_equ[0] * sin_ra_c
|
||||
- earth_equ[1] * cos_ra_c) / cos_dec_c;
|
||||
dec_corrected += p_rad * (earth_equ[0] * cos_ra_c * sin_dec_c
|
||||
+ earth_equ[1] * sin_ra_c * sin_dec_c
|
||||
- earth_equ[2] * cos_dec_c);
|
||||
}
|
||||
|
||||
ra_corrected = fmod(ra_corrected, 2.0 * M_PI);
|
||||
if (ra_corrected < 0.0)
|
||||
ra_corrected += 2.0 * M_PI;
|
||||
}
|
||||
|
||||
precess_j2000_to_date(jd, ra_corrected, dec_corrected, &ra_date, &dec_date);
|
||||
|
||||
result = (pg_equatorial *) palloc(sizeof(pg_equatorial));
|
||||
|
||||
247
test/expected/aberration.out
Normal file
247
test/expected/aberration.out
Normal file
@ -0,0 +1,247 @@
|
||||
-- aberration regression tests
|
||||
--
|
||||
-- Tests annual aberration in _apparent() functions, DE apparent variants,
|
||||
-- equatorial angular distance/cone search, and stellar annual parallax.
|
||||
\set boulder '''40.015N 105.270W 1655m'''::observer
|
||||
-- ============================================================
|
||||
-- Test 1: Aberration magnitude — planet_equatorial_apparent
|
||||
-- vs planet_equatorial (geometric). Jupiter aberration should
|
||||
-- be in the range 0-20 arcsec (~0.001 hours at Jupiter's dec).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_planet' AS test,
|
||||
round((abs(
|
||||
eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))
|
||||
- eq_ra(planet_equatorial(5, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))
|
||||
- eq_ra(planet_equatorial(5, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 1 AND 50 AS magnitude_valid;
|
||||
test | diff_arcsec | magnitude_valid
|
||||
-------------------+-------------+-----------------
|
||||
aberration_planet | 29 | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 2: Aberration magnitude — sun_observe_apparent vs sun_observe
|
||||
-- Sun aberration should be ~20 arcsec (Earth orbital velocity).
|
||||
-- Compare elevations (both from same observer, same time).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_sun' AS test,
|
||||
round((abs(
|
||||
topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))
|
||||
- topo_elevation(sun_observe(:boulder, '2024-06-21 12:00:00+00'))
|
||||
) * 3600)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))
|
||||
- topo_elevation(sun_observe(:boulder, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 BETWEEN 1 AND 25 AS magnitude_valid;
|
||||
test | diff_arcsec | magnitude_valid
|
||||
----------------+-------------+-----------------
|
||||
aberration_sun | 15 | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 3: Moon aberration should be present (same ~20 arcsec
|
||||
-- as all other objects — aberration depends on observer velocity,
|
||||
-- not object distance).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_moon' AS test,
|
||||
round((abs(
|
||||
eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))
|
||||
- eq_ra(moon_equatorial('2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))
|
||||
- eq_ra(moon_equatorial('2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 1 AND 25 AS magnitude_valid;
|
||||
test | diff_arcsec | magnitude_valid
|
||||
-----------------+-------------+-----------------
|
||||
aberration_moon | 22 | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 4: DE apparent fallback — without DE configured,
|
||||
-- _apparent_de() should match _apparent() exactly.
|
||||
-- ============================================================
|
||||
SELECT 'de_apparent_fallback' AS test,
|
||||
round(eq_ra(planet_equatorial_apparent_de(5, '2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))::numeric, 6) AS planet_match,
|
||||
round(eq_ra(moon_equatorial_apparent_de('2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))::numeric, 6) AS moon_match;
|
||||
test | planet_match | moon_match
|
||||
----------------------+--------------+------------
|
||||
de_apparent_fallback | t | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 5: DE apparent topocentric fallback
|
||||
-- ============================================================
|
||||
SELECT 'de_topo_fallback' AS test,
|
||||
round(topo_elevation(planet_observe_apparent_de(5, :boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(planet_observe_apparent(5, :boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS planet_match,
|
||||
round(topo_elevation(sun_observe_apparent_de(:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS sun_match,
|
||||
topo_elevation(moon_observe_apparent_de(:boulder, '2024-06-21 12:00:00+00')) BETWEEN -90 AND 90 AS moon_valid;
|
||||
test | planet_match | sun_match | moon_valid
|
||||
------------------+--------------+-----------+------------
|
||||
de_topo_fallback | t | t | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 6: Small body DE apparent fallback
|
||||
-- ============================================================
|
||||
SELECT 'de_smallbody_fallback' AS test,
|
||||
round(topo_elevation(small_body_observe_apparent_de(
|
||||
'(2460400.5,2.5577,0.0785,0.1849,1.2836,1.4013,2460500.0,3.53,0.12)'::orbital_elements,
|
||||
:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(small_body_observe_apparent(
|
||||
'(2460400.5,2.5577,0.0785,0.1849,1.2836,1.4013,2460500.0,3.53,0.12)'::orbital_elements,
|
||||
:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS match;
|
||||
test | match
|
||||
-----------------------+-------
|
||||
de_smallbody_fallback | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 7: Angular distance — Dubhe and Merak (Big Dipper pointers)
|
||||
-- Dubhe: RA 11.062h, Dec 61.751 deg
|
||||
-- Merak: RA 11.031h, Dec 56.382 deg
|
||||
-- Expected separation: ~5.4 degrees
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance' AS test,
|
||||
round(eq_angular_distance(
|
||||
star_equatorial(11.062, 61.751, '2024-06-21 12:00:00+00'),
|
||||
star_equatorial(11.031, 56.382, '2024-06-21 12:00:00+00')
|
||||
)::numeric, 1) AS sep_deg;
|
||||
test | sep_deg
|
||||
------------------+---------
|
||||
angular_distance | 5.4
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 8: Angular distance — same position should be 0
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance_zero' AS test,
|
||||
round(eq_angular_distance(
|
||||
'(12.00000000,45.00000000,0.000)'::equatorial,
|
||||
'(12.00000000,45.00000000,0.000)'::equatorial
|
||||
)::numeric, 6) AS sep_deg;
|
||||
test | sep_deg
|
||||
-----------------------+----------
|
||||
angular_distance_zero | 0.000000
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 9: Angular distance — opposite poles should be 180
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance_poles' AS test,
|
||||
round(eq_angular_distance(
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
'(0.00000000,-90.00000000,0.000)'::equatorial
|
||||
)::numeric, 1) AS sep_deg;
|
||||
test | sep_deg
|
||||
------------------------+---------
|
||||
angular_distance_poles | 180.0
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 10: <-> operator (same as eq_angular_distance)
|
||||
-- ============================================================
|
||||
SELECT 'operator_arrow' AS test,
|
||||
round((
|
||||
star_equatorial(11.062, 61.751, '2024-06-21 12:00:00+00')
|
||||
<->
|
||||
star_equatorial(11.031, 56.382, '2024-06-21 12:00:00+00')
|
||||
)::numeric, 1) AS sep_deg;
|
||||
test | sep_deg
|
||||
----------------+---------
|
||||
operator_arrow | 5.4
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 11: Cone search — Polaris within 5 deg of NCP
|
||||
-- ============================================================
|
||||
SELECT 'cone_inside' AS test,
|
||||
eq_within_cone(
|
||||
star_equatorial(2.530303, 89.2641, '2024-06-21 12:00:00+00'),
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
5.0
|
||||
) AS inside;
|
||||
test | inside
|
||||
-------------+--------
|
||||
cone_inside | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 12: Cone search — Sirius not within 5 deg of NCP
|
||||
-- ============================================================
|
||||
SELECT 'cone_outside' AS test,
|
||||
eq_within_cone(
|
||||
star_equatorial(6.7525, -16.7161, '2024-06-21 12:00:00+00'),
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
5.0
|
||||
) AS inside;
|
||||
test | inside
|
||||
--------------+--------
|
||||
cone_outside | f
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 13: Stellar parallax — Proxima Centauri (768 mas)
|
||||
-- Compare with-parallax vs without-parallax at the SAME epoch
|
||||
-- to isolate the parallax displacement from proper motion and
|
||||
-- precession. Expected: ~0.2-1.5 arcsec depending on Earth's
|
||||
-- orbital phase (max near quadrature for this RA).
|
||||
-- Proxima: RA 14.495h, Dec -62.679 deg
|
||||
-- ============================================================
|
||||
SELECT 'stellar_parallax' AS test,
|
||||
round((abs(
|
||||
eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 768.07, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
- eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 2) AS shift_arcsec,
|
||||
abs(
|
||||
eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 768.07, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
- eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 0.01 AND 2.0 AS magnitude_valid;
|
||||
test | shift_arcsec | magnitude_valid
|
||||
------------------+--------------+-----------------
|
||||
stellar_parallax | 1.02 | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 14: Parallax = 0 should not change star position
|
||||
-- (same as without parallax)
|
||||
-- ============================================================
|
||||
SELECT 'parallax_zero' AS test,
|
||||
round(eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-06-21 12:00:00+00'))::numeric, 6) AS match;
|
||||
test | match
|
||||
---------------+-------
|
||||
parallax_zero | t
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
-- Test 15: star_observe_pm parallax affects topocentric result
|
||||
-- Barnard's Star with parallax should differ from without
|
||||
-- ============================================================
|
||||
SELECT 'parallax_topo' AS test,
|
||||
abs(
|
||||
topo_elevation(star_observe_pm(
|
||||
17.963472, 4.6933, -798.58, 10328.12, 545.4, -110.51,
|
||||
:boulder, '2024-07-15 04:00:00+00'))
|
||||
- topo_elevation(star_observe_pm(
|
||||
17.963472, 4.6933, -798.58, 10328.12, 0.0, -110.51,
|
||||
:boulder, '2024-07-15 04:00:00+00'))
|
||||
) * 3600 BETWEEN 0.01 AND 2.0 AS displacement_valid;
|
||||
test | displacement_valid
|
||||
---------------+--------------------
|
||||
parallax_topo | t
|
||||
(1 row)
|
||||
|
||||
@ -224,7 +224,7 @@ SELECT 'jupiter_apparent' AS test,
|
||||
)::numeric, 4) AS ra_diff_hours;
|
||||
test | ra_diff_hours
|
||||
------------------+---------------
|
||||
jupiter_apparent | 0.0002
|
||||
jupiter_apparent | 0.0005
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
@ -238,7 +238,7 @@ SELECT 'moon_apparent' AS test,
|
||||
)::numeric, 6) AS ra_diff_hours;
|
||||
test | ra_diff_hours
|
||||
---------------+---------------
|
||||
moon_apparent | 0.000015
|
||||
moon_apparent | 0.000405
|
||||
(1 row)
|
||||
|
||||
-- ============================================================
|
||||
|
||||
188
test/sql/aberration.sql
Normal file
188
test/sql/aberration.sql
Normal file
@ -0,0 +1,188 @@
|
||||
-- aberration regression tests
|
||||
--
|
||||
-- Tests annual aberration in _apparent() functions, DE apparent variants,
|
||||
-- equatorial angular distance/cone search, and stellar annual parallax.
|
||||
|
||||
\set boulder '''40.015N 105.270W 1655m'''::observer
|
||||
|
||||
-- ============================================================
|
||||
-- Test 1: Aberration magnitude — planet_equatorial_apparent
|
||||
-- vs planet_equatorial (geometric). Jupiter aberration should
|
||||
-- be in the range 0-20 arcsec (~0.001 hours at Jupiter's dec).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_planet' AS test,
|
||||
round((abs(
|
||||
eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))
|
||||
- eq_ra(planet_equatorial(5, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))
|
||||
- eq_ra(planet_equatorial(5, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 1 AND 50 AS magnitude_valid;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 2: Aberration magnitude — sun_observe_apparent vs sun_observe
|
||||
-- Sun aberration should be ~20 arcsec (Earth orbital velocity).
|
||||
-- Compare elevations (both from same observer, same time).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_sun' AS test,
|
||||
round((abs(
|
||||
topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))
|
||||
- topo_elevation(sun_observe(:boulder, '2024-06-21 12:00:00+00'))
|
||||
) * 3600)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))
|
||||
- topo_elevation(sun_observe(:boulder, '2024-06-21 12:00:00+00'))
|
||||
) * 3600 BETWEEN 1 AND 25 AS magnitude_valid;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 3: Moon aberration should be present (same ~20 arcsec
|
||||
-- as all other objects — aberration depends on observer velocity,
|
||||
-- not object distance).
|
||||
-- ============================================================
|
||||
SELECT 'aberration_moon' AS test,
|
||||
round((abs(
|
||||
eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))
|
||||
- eq_ra(moon_equatorial('2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 0) AS diff_arcsec,
|
||||
abs(
|
||||
eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))
|
||||
- eq_ra(moon_equatorial('2024-06-21 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 1 AND 25 AS magnitude_valid;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 4: DE apparent fallback — without DE configured,
|
||||
-- _apparent_de() should match _apparent() exactly.
|
||||
-- ============================================================
|
||||
SELECT 'de_apparent_fallback' AS test,
|
||||
round(eq_ra(planet_equatorial_apparent_de(5, '2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(planet_equatorial_apparent(5, '2024-06-21 12:00:00+00'))::numeric, 6) AS planet_match,
|
||||
round(eq_ra(moon_equatorial_apparent_de('2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(moon_equatorial_apparent('2024-06-21 12:00:00+00'))::numeric, 6) AS moon_match;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 5: DE apparent topocentric fallback
|
||||
-- ============================================================
|
||||
SELECT 'de_topo_fallback' AS test,
|
||||
round(topo_elevation(planet_observe_apparent_de(5, :boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(planet_observe_apparent(5, :boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS planet_match,
|
||||
round(topo_elevation(sun_observe_apparent_de(:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(sun_observe_apparent(:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS sun_match,
|
||||
topo_elevation(moon_observe_apparent_de(:boulder, '2024-06-21 12:00:00+00')) BETWEEN -90 AND 90 AS moon_valid;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 6: Small body DE apparent fallback
|
||||
-- ============================================================
|
||||
SELECT 'de_smallbody_fallback' AS test,
|
||||
round(topo_elevation(small_body_observe_apparent_de(
|
||||
'(2460400.5,2.5577,0.0785,0.1849,1.2836,1.4013,2460500.0,3.53,0.12)'::orbital_elements,
|
||||
:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) =
|
||||
round(topo_elevation(small_body_observe_apparent(
|
||||
'(2460400.5,2.5577,0.0785,0.1849,1.2836,1.4013,2460500.0,3.53,0.12)'::orbital_elements,
|
||||
:boulder, '2024-06-21 12:00:00+00'))::numeric, 4) AS match;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 7: Angular distance — Dubhe and Merak (Big Dipper pointers)
|
||||
-- Dubhe: RA 11.062h, Dec 61.751 deg
|
||||
-- Merak: RA 11.031h, Dec 56.382 deg
|
||||
-- Expected separation: ~5.4 degrees
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance' AS test,
|
||||
round(eq_angular_distance(
|
||||
star_equatorial(11.062, 61.751, '2024-06-21 12:00:00+00'),
|
||||
star_equatorial(11.031, 56.382, '2024-06-21 12:00:00+00')
|
||||
)::numeric, 1) AS sep_deg;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 8: Angular distance — same position should be 0
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance_zero' AS test,
|
||||
round(eq_angular_distance(
|
||||
'(12.00000000,45.00000000,0.000)'::equatorial,
|
||||
'(12.00000000,45.00000000,0.000)'::equatorial
|
||||
)::numeric, 6) AS sep_deg;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 9: Angular distance — opposite poles should be 180
|
||||
-- ============================================================
|
||||
SELECT 'angular_distance_poles' AS test,
|
||||
round(eq_angular_distance(
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
'(0.00000000,-90.00000000,0.000)'::equatorial
|
||||
)::numeric, 1) AS sep_deg;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 10: <-> operator (same as eq_angular_distance)
|
||||
-- ============================================================
|
||||
SELECT 'operator_arrow' AS test,
|
||||
round((
|
||||
star_equatorial(11.062, 61.751, '2024-06-21 12:00:00+00')
|
||||
<->
|
||||
star_equatorial(11.031, 56.382, '2024-06-21 12:00:00+00')
|
||||
)::numeric, 1) AS sep_deg;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 11: Cone search — Polaris within 5 deg of NCP
|
||||
-- ============================================================
|
||||
SELECT 'cone_inside' AS test,
|
||||
eq_within_cone(
|
||||
star_equatorial(2.530303, 89.2641, '2024-06-21 12:00:00+00'),
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
5.0
|
||||
) AS inside;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 12: Cone search — Sirius not within 5 deg of NCP
|
||||
-- ============================================================
|
||||
SELECT 'cone_outside' AS test,
|
||||
eq_within_cone(
|
||||
star_equatorial(6.7525, -16.7161, '2024-06-21 12:00:00+00'),
|
||||
'(0.00000000,90.00000000,0.000)'::equatorial,
|
||||
5.0
|
||||
) AS inside;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 13: Stellar parallax — Proxima Centauri (768 mas)
|
||||
-- Compare with-parallax vs without-parallax at the SAME epoch
|
||||
-- to isolate the parallax displacement from proper motion and
|
||||
-- precession. Expected: ~0.2-1.5 arcsec depending on Earth's
|
||||
-- orbital phase (max near quadrature for this RA).
|
||||
-- Proxima: RA 14.495h, Dec -62.679 deg
|
||||
-- ============================================================
|
||||
SELECT 'stellar_parallax' AS test,
|
||||
round((abs(
|
||||
eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 768.07, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
- eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
) * 3600 * 15)::numeric, 2) AS shift_arcsec,
|
||||
abs(
|
||||
eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 768.07, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
- eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-03-20 12:00:00+00'))
|
||||
) * 3600 * 15 BETWEEN 0.01 AND 2.0 AS magnitude_valid;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 14: Parallax = 0 should not change star position
|
||||
-- (same as without parallax)
|
||||
-- ============================================================
|
||||
SELECT 'parallax_zero' AS test,
|
||||
round(eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-06-21 12:00:00+00'))::numeric, 6) =
|
||||
round(eq_ra(star_equatorial_pm(14.495, -62.679, -3775.40, 769.33, 0.0, -21.7,
|
||||
'2024-06-21 12:00:00+00'))::numeric, 6) AS match;
|
||||
|
||||
-- ============================================================
|
||||
-- Test 15: star_observe_pm parallax affects topocentric result
|
||||
-- Barnard's Star with parallax should differ from without
|
||||
-- ============================================================
|
||||
SELECT 'parallax_topo' AS test,
|
||||
abs(
|
||||
topo_elevation(star_observe_pm(
|
||||
17.963472, 4.6933, -798.58, 10328.12, 545.4, -110.51,
|
||||
:boulder, '2024-07-15 04:00:00+00'))
|
||||
- topo_elevation(star_observe_pm(
|
||||
17.963472, 4.6933, -798.58, 10328.12, 0.0, -110.51,
|
||||
:boulder, '2024-07-15 04:00:00+00'))
|
||||
) * 3600 BETWEEN 0.01 AND 2.0 AS displacement_valid;
|
||||
Loading…
x
Reference in New Issue
Block a user