Find the naked-eye stars Gaia's HIP cross-match lacks by position, and give two unnamed ones back their names

c64eea0 and ff744a0 left 34 of HYG's stars of V 6.5 or brighter off the map for want of a distance,
saying no survey gave them one. Gaia DR3 does, for twelve, above the ETL's own cut of five times
the parallax error: fetchStars looks Gaia up only by HIP number through hipparcos2_best_neighbour,
which lacks some of theirs (66 Ori's among them), and never looks up a HYG row with no HIP number.
HD 197770 is 1.102 +- 0.029 mas; HD 45291 and HD 124953 were on the map only as bare Gaia entries
a second of arc from where HYG has them, so searching their names found nothing.

fetchStars now fetches, once and cached, Gaia DR3's 35 910 sources brighter than G 7.5 with a
usable parallax, and for a naked-eye row nothing else places takes the nearest within 5" and a
magnitude of its V. At 15" theta-1 Ori (HIP 26220) took the source of theta-1 Ori C, 12.9" away and
already on the map; the twelve found are within 4.1". An ETL run from cache: 455 522 stars become
455 532, the naked-eye stars 8 886 become 8 898 (1 663 past 250 pc), with 66 Ori, HD 49567, HD 54309,
HD 74455, HD 114461, 10 Sge, HD 197770, HD 33948, HD 152249 and HD 162678 added, and HD 45291 and
HD 124953 folded into their Gaia entries under their names. 22 stay out, with no distance anywhere.
In the running app HD 197770 reads "907 +- 25 pc", 66 Ori "469 +- 61 pc", HD 45291 "108 pc" and
HD 124953 "46 pc". Four of the twelve have a RUWE above 3.7 in Gaia; the ETL has never filtered on it.

validateStars now also requires HD 197770 and HD 45291. Control: without the positional lookup,
the ETL fails with "HD 197770 is missing" (the naked-eye floor alone would not see twelve stars go),
and the baseline passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-30 17:36:44 +02:00
co-authored by Claude Opus 5.5
parent 030447b83c
commit 4d47896b4a
6 changed files with 85 additions and 11 deletions
+32 -4
View File
@@ -1,9 +1,9 @@
import { writeFileSync } from 'node:fs';
import { foldByIdentity, hipparcosDistancePc, mergeStarCatalogues, placementDistancePc } from '../../src/app/shared/astro/star-merge';
import { foldByIdentity, hipparcosDistancePc, mergeStarCatalogues, NAKED_EYE_MAGNITUDE, placementDistancePc } from '../../src/app/shared/astro/star-merge';
import { encodeStarCatalog } from '../../src/app/shared/models/star-catalog';
import { StarRecord, SUN_STAR_ID } from '../../src/app/shared/models/star.model';
import { fetchGaiaDistancesByHip, fetchHipparcosParallaxErrors, GaiaAnswerError } from './sources/gaia';
import { BrightGaiaSource, fetchBrightGaiaSources, fetchGaiaDistancesByHip, fetchHipparcosParallaxErrors, GaiaAnswerError } from './sources/gaia';
import { positionalSources } from './sources/registry';
import { fetchGaiaDesignationsByGj } from './sources/simbad';
import { PARALLAX_PRECISION_MAS } from './sources/star-sources';
@@ -85,6 +85,7 @@ export async function fetchStars(): Promise<StarRecord[]> {
// Not skipped when unreachable, unlike the positional sources below; see its own comment.
const gaiaByHip = await fetchGaiaDistancesByHip();
const hipparcosErrors = await fetchHipparcosParallaxErrors();
const brightGaia = await fetchBrightGaiaSources();
const stars: StarRecord[] = [];
let atGaiaDistance = 0;
@@ -101,10 +102,12 @@ export async function fetchStars(): Promise<StarRecord[]> {
const hygPc = Number(row['dist']);
const hipparcos = row['hip'] ? hipparcosErrors.get(Number(row['hip'])) : undefined;
const hipparcosPc = hipparcosDistancePc(hygPc, hipparcos);
const gaia = row['hip'] ? gaiaByHip.get(Number(row['hip'])) : undefined;
const gaiaPc = gaia?.distancePc;
const magnitudeV = parseOptionalNumber(row['mag']);
const magnitude = magnitudeV ?? UNKNOWN_MAGNITUDE;
const gaia =
(row['hip'] ? gaiaByHip.get(Number(row['hip'])) : undefined) ??
(hipparcosPc === undefined && magnitude <= NAKED_EYE_MAGNITUDE ? brightCounterpart(row, magnitude, brightGaia) : undefined);
const gaiaPc = gaia?.distancePc;
const hipparcosError = hipparcos?.relativeError;
const distancePc = placementDistancePc(hipparcosPc, gaiaPc, magnitude, DISTANCE_CUTOFF_PC, hipparcosError, gaia?.relativeError);
if (distancePc === null) {
@@ -170,6 +173,31 @@ export async function fetchStars(): Promise<StarRecord[]> {
return merged;
}
/** How far a naked-eye star may be from its Gaia source, and how much brighter or fainter in G. */
const BRIGHT_COUNTERPART_TOLERANCE_RAD = (5 / 3600) * (Math.PI / 180);
const BRIGHT_COUNTERPART_MAGNITUDES = 1;
/**
* The Gaia source that is a naked-eye HYG star neither survey places otherwise: the nearest within
* 5″ of its direction and a magnitude of its V. Found this way, HD 152249 is 4.1″ from where HYG
* has it, the rest within 1.1″. At 15″, θ¹ Ori (HIP 26220) took the source of θ¹ Ori C, 12.9″ away
* and already on the map.
*/
function brightCounterpart(row: Record<string, string>, magnitudeV: number, sources: readonly BrightGaiaSource[]): { distancePc: number; relativeError: number } | undefined {
const [x, y, z] = [Number(row['x']), Number(row['y']), Number(row['z'])];
const length = Math.hypot(x, y, z);
let best: BrightGaiaSource | undefined;
let bestCosine = Math.cos(BRIGHT_COUNTERPART_TOLERANCE_RAD);
for (const source of sources) {
const cosine = (x * source.direction.x + y * source.direction.y + z * source.direction.z) / length;
if (cosine >= bestCosine && Math.abs(source.magnitudeG - magnitudeV) <= BRIGHT_COUNTERPART_MAGNITUDES) {
best = source;
bestCosine = cosine;
}
}
return best;
}
/** HYG's Gliese designation as SIMBAD writes it: "Gl 734B" is "GJ 734 B". */
function simbadGliese(gl: string): string {
return gl.trim().replace(/^Gl\s+/, 'GJ ').replace(/^(GJ \d+(?:\.\d+)?)\s*([A-Z]+)$/, '$1 $2');