Place a star at whichever of its two distances is the more precise, not at Gaia's regardless

placementDistancePc took Gaia's distance wherever the cross-match gave a usable one, and its
docstring and c64eea0 called that "the better measurement", adding that Gaia saturates on the
brightest stars "so those sit at their Hipparcos distance". For 273 of the 88 781 HYG stars with
both, Gaia's relative error is the larger, 257 of them naked-eye and 38 by more than twice: Eta Leo
was drawn at Gaia's 556.6 pc ±17 % against Hipparcos's 389 pc ±6.2 %, Schedar and Tarazed at
Gaia's though both have Gaia G of about 2.

placementDistancePc now takes both relative errors and picks the smaller, Gaia's as before when
either is missing; fetchStars gives the placed star the error and the Gaia-distance flag of the
distance it took. The merge's combine keeps the Gaia entry's direction, and now takes the other
entry's distance with its error where that is the more precise, so a bright star Gaia's main query
also holds lands at its Hipparcos distance too. Gaia still wins for the rest, whose parallaxes are
some fifty times more precise.

From cache: 424 stars move, 386 of them naked-eye; HYG rows at Gaia's distance 58 379 -> 58 109 and
HYG stars flagged so 8 129 -> 8 095. Eta Leo 556.6 -> 389.1 pc, Tarazed 178.9 -> 121.1, Imai 139.5 ->
105.8, Schedar 71.0 -> 70.0, Alphecca 23.7 -> 23.0, each now "±" its Hipparcos error. Where the two
disagree by more than 5 σ (21 stars, Iot Gem 11 σ), one of the formal errors is wrong, and this
takes the smaller one at its word. The validators pass unchanged.

Controls, each failing its named test: the placement ignoring the errors, the merge keeping Gaia's
distance whatever the errors, and taking the distance but not its error (1 of 805 each).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-29 21:37:35 +02:00
co-authored by Claude Opus 5.5
parent 23547defe0
commit 8c3a86097c
6 changed files with 57 additions and 22 deletions
+9 -7
View File
@@ -71,9 +71,9 @@ function spectralTypeOf(row: Record<string, string>): string {
/**
* Downloads the HYG (Hipparcos/Yale/Gliese) stellar database, places each star along its
* equatorial direction (epoch J2000.0) at the better of its Hipparcos and Gaia distances, keeps
* the ones either survey puts within range, unions the other positional sources, and writes
* `stars.bin` (packed positions) + `stars-index.json` (everything else).
* equatorial direction (epoch J2000.0) at whichever of its Hipparcos and Gaia distances has the
* smaller error, keeps the ones either survey puts within range, unions the other positional
* sources, and writes `stars.bin` (packed positions) + `stars-index.json` (everything else).
*/
export async function fetchStars(): Promise<StarRecord[]> {
console.log(`Fetching HYG star catalog (distance cutoff: ${DISTANCE_CUTOFF_PC} pc)...`);
@@ -101,10 +101,12 @@ export async function fetchStars(): Promise<StarRecord[]> {
const gaiaPc = gaia?.distancePc;
const magnitudeV = parseOptionalNumber(row['mag']);
const magnitude = magnitudeV ?? UNKNOWN_MAGNITUDE;
const distancePc = placementDistancePc(hipparcosPc, gaiaPc, magnitude, DISTANCE_CUTOFF_PC);
const hipparcosError = row['hip'] ? hipparcosErrors.get(Number(row['hip'])) : undefined;
const distancePc = placementDistancePc(hipparcosPc, gaiaPc, magnitude, DISTANCE_CUTOFF_PC, hipparcosError, gaia?.relativeError);
if (distancePc === null) {
continue;
}
const fromGaia = gaia !== undefined && distancePc === gaiaPc;
// HYG's own Cartesian columns rather than its `ra`/`dec`, which are in the same frame as
// `raDecDistanceToXyz` and would be redundant if the two agreed. They do not, for the stars
@@ -124,7 +126,7 @@ export async function fetchStars(): Promise<StarRecord[]> {
continue;
}
const scale = distancePc / length;
if (gaiaPc !== undefined) {
if (fromGaia) {
atGaiaDistance++;
}
if (distancePc > DISTANCE_CUTOFF_PC) {
@@ -133,7 +135,7 @@ export async function fetchStars(): Promise<StarRecord[]> {
// Gaia's error with Gaia's distance, Hipparcos's with its own; a Gliese row, with neither, has
// no published error, and its distance is as often photometric as measured.
const distanceError = gaia?.relativeError ?? (row['hip'] ? hipparcosErrors.get(Number(row['hip'])) : undefined);
const distanceError = fromGaia ? gaia.relativeError : hipparcosError;
const colorIndex = parseOptionalNumber(row['ci']);
stars.push({
@@ -148,7 +150,7 @@ export async function fetchStars(): Promise<StarRecord[]> {
colorIndex: colorIndex ?? null,
...(colorIndex === undefined ? {} : { colorSystem: 'B-V' as const }),
...(distanceError === undefined ? {} : { distanceError }),
distanceFromGaia: gaia !== undefined,
distanceFromGaia: fromGaia,
// Only for the Gliese-only rows, whose positions are what the merge needs the motion to see
// past. A Hipparcos position is good to under an arcsecond; given its motion too, 15 stars
// took their co-moving companion's Gaia entry, and the companion was kept twice.