Place a naked-eye star HYG gives no distance for by its Hipparcos parallax, where that is 2.5 times its error
c64eea0 left out 41 naked-eye stars "because neither survey gives them a distance". Hipparcos does:
HYG's distances are 1 000 over van Leeuwen's 2007 parallaxes, which the ETL already downloads for
their errors, but HYG writes its 100 000 pc placeholder for every parallax under 1 mas whatever its
error, while keeping far less certain ones above it (Alnilam at 1.65 ± 0.45 mas is drawn). 30 of
the 41 have a positive parallax in the new reduction and 7 have one at least 2.5 times its error:
HD 74180 at 0.67 ± 0.16 mas (4.2 σ), Mu Cep at 0.55 ± 0.20.
hipparcosDistancePc (star-merge.ts, with HYG's placeholder constant moved beside it) keeps HYG's
distance and, where it gives the placeholder, takes 1 000 over the parallax when that is 2.5 times
its error; fetchHipparcosParallaxErrors now returns the parallax with its error. From cache the ETL
keeps 73 563 HYG rows against 73 556 and publishes 455 571 stars; the seven are Alp Cam (1.4 to 3.0
kpc), Psi-1 Aur, HD 74180 (1.2 to 2.0 kpc), HD 86352, HD 96918, Mu Cep (1.3 to 2.9 kpc) and HD
217476, each printed as the range its parallax's error gives. The other 34 stay out: no parallax,
or one within 2.5 times its error of zero. Parallax distances printed as ranges go from 1 109 to
1 116; the validators hold.
Controls, each failing its named test: placeholder rows given no distance, and any parallax 1.25
times its error placing a star (1 of 807 each).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { raDegDecDistanceToXyz } from './coordinates';
|
||||
import { StarRecord } from '../models/star.model';
|
||||
import { directionCosine, isSameStar, MERGE_ANGULAR_TOLERANCE_DEG, mergeStarCatalogues, NAKED_EYE_MAGNITUDE, placementDistancePc } from './star-merge';
|
||||
import { directionCosine, hipparcosDistancePc, HYG_UNKNOWN_DISTANCE_PC, isSameStar, MERGE_ANGULAR_TOLERANCE_DEG, mergeStarCatalogues, NAKED_EYE_MAGNITUDE, placementDistancePc } from './star-merge';
|
||||
|
||||
/** A star at a given sky position and distance, which is how catalogues actually report them. */
|
||||
function at(id: number, raDeg: number, decDeg: number, distancePc: number, overrides: Partial<StarRecord> = {}): StarRecord {
|
||||
@@ -309,6 +309,20 @@ describe('mergeStarCatalogues', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hipparcosDistancePc', () => {
|
||||
it("takes HYG's distance where it gives one", () => {
|
||||
expect(hipparcosDistancePc(606.06, { parallaxMas: 1.65, relativeError: 0.45 / 1.65 })).toBe(606.06);
|
||||
});
|
||||
|
||||
it("places a star HYG gives no distance for by its parallax, if the parallax is 2.5 times its error", () => {
|
||||
// HD 74180 at 0.67 ± 0.16 mas and Mu Cep at 0.55 ± 0.20; a parallax at 1.5 times its error stays out.
|
||||
expect(hipparcosDistancePc(HYG_UNKNOWN_DISTANCE_PC, { parallaxMas: 0.67, relativeError: 0.16 / 0.67 })).toBeCloseTo(1492.5, 1);
|
||||
expect(hipparcosDistancePc(HYG_UNKNOWN_DISTANCE_PC, { parallaxMas: 0.55, relativeError: 0.2 / 0.55 })).toBeCloseTo(1818.2, 1);
|
||||
expect(hipparcosDistancePc(HYG_UNKNOWN_DISTANCE_PC, { parallaxMas: 0.3, relativeError: 0.2 / 0.3 })).toBeUndefined();
|
||||
expect(hipparcosDistancePc(HYG_UNKNOWN_DISTANCE_PC)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('placementDistancePc', () => {
|
||||
it("draws a star both surveys measured at Gaia's distance", () => {
|
||||
expect(placementDistancePc(120, 118.4, 8, 250)).toBe(118.4);
|
||||
|
||||
@@ -86,6 +86,26 @@ export const MERGE_BRIGHTER_TOLERANCE = 1;
|
||||
*/
|
||||
export const MERGE_DISTANCE_RATIO_TOLERANCE = 0.5;
|
||||
|
||||
/** HYG's distance for a star whose parallax it does not give one for. */
|
||||
export const HYG_UNKNOWN_DISTANCE_PC = 100000;
|
||||
|
||||
/** How many times its error a parallax HYG leaves out must be to place a star by: a 40 % error. */
|
||||
const MIN_HIPPARCOS_PARALLAX_OVER_ERROR = 2.5;
|
||||
|
||||
/**
|
||||
* A HYG star's Hipparcos distance: HYG's own, which is the inverse of van Leeuwen's 2007 parallax,
|
||||
* or where HYG gives its placeholder instead, that inverse if the parallax is at least 2.5 times its
|
||||
* error. HYG gives no distance under 1 mas whatever the error, while keeping less certain parallaxes
|
||||
* above it: 41 naked-eye stars were left off the map as having no distance, HD 74180 at
|
||||
* 0.67 ± 0.16 mas and Mu Cep at 0.55 ± 0.20 among them, while Alnilam at 1.65 ± 0.45 was drawn.
|
||||
*/
|
||||
export function hipparcosDistancePc(hygPc: number, parallax?: { parallaxMas: number; relativeError: number }): number | undefined {
|
||||
if (Number.isFinite(hygPc) && hygPc > 0 && hygPc < HYG_UNKNOWN_DISTANCE_PC) {
|
||||
return hygPc;
|
||||
}
|
||||
return parallax && parallax.relativeError <= 1 / MIN_HIPPARCOS_PARALLAX_OVER_ERROR ? 1000 / parallax.parallaxMas : undefined;
|
||||
}
|
||||
|
||||
/** The faintest star, in V, the naked eye sees under a dark sky: the traditional limit of 6.5. */
|
||||
export const NAKED_EYE_MAGNITUDE = 6.5;
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
import { mergeStarCatalogues, placementDistancePc } from '../../src/app/shared/astro/star-merge';
|
||||
import { hipparcosDistancePc, mergeStarCatalogues, 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';
|
||||
@@ -11,7 +11,6 @@ import { fetchTextCached } from './lib/http';
|
||||
import { dataPath, ensureDataDir } from './lib/paths';
|
||||
|
||||
const HYG_CSV_URL = 'https://raw.githubusercontent.com/astronexus/HYG-Database/main/hyg/CURRENT/hygdata_v41.csv';
|
||||
const HYG_UNKNOWN_DISTANCE_PC = 100000; // HYG's placeholder for unmeasured/unreliable parallax
|
||||
|
||||
/**
|
||||
* Stand-in magnitude for a star with no photometry. Faint rather than 0, because 0 would mean
|
||||
@@ -96,12 +95,13 @@ export async function fetchStars(): Promise<StarRecord[]> {
|
||||
}
|
||||
|
||||
const hygPc = Number(row['dist']);
|
||||
const hipparcosPc = Number.isFinite(hygPc) && hygPc > 0 && hygPc < HYG_UNKNOWN_DISTANCE_PC ? hygPc : undefined;
|
||||
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 hipparcosError = row['hip'] ? hipparcosErrors.get(Number(row['hip'])) : undefined;
|
||||
const hipparcosError = hipparcos?.relativeError;
|
||||
const distancePc = placementDistancePc(hipparcosPc, gaiaPc, magnitude, DISTANCE_CUTOFF_PC, hipparcosError, gaia?.relativeError);
|
||||
if (distancePc === null) {
|
||||
continue;
|
||||
|
||||
@@ -295,25 +295,25 @@ export async function fetchGaiaDistancesByHip(): Promise<Map<number, { distanceP
|
||||
const MIN_HIPPARCOS_ERRORS = 110_000;
|
||||
|
||||
/**
|
||||
* The relative error of every Hipparcos parallax, keyed by HIP number: to choose between it and
|
||||
* Every Hipparcos parallax with its relative error, keyed by HIP number: to choose between it and
|
||||
* Gaia's, and for the stars that keep their Hipparcos distance — Rigel and Deneb, which Gaia has
|
||||
* no usable parallax for, and the few hundred bright stars where Gaia's is the less precise.
|
||||
*
|
||||
* From van Leeuwen's 2007 reduction, which the ESA archive hosts beside Gaia and HYG's distances
|
||||
* are the inverse of. HYG publishes the distance and not its error.
|
||||
*/
|
||||
export async function fetchHipparcosParallaxErrors(): Promise<Map<number, number>> {
|
||||
export async function fetchHipparcosParallaxErrors(): Promise<Map<number, { parallaxMas: number; relativeError: number }>> {
|
||||
const url = `${GAIA_TAP_URL}?REQUEST=doQuery&LANG=ADQL&FORMAT=csv&QUERY=${encodeURIComponent('select top 200000 hip, plx, e_plx from public.hipparcos_newreduction order by hip')}`;
|
||||
console.log('Fetching Hipparcos parallax errors (new reduction)...');
|
||||
const rows = parseCsvObjects(await fetchTextCached(url, `hipparcos-errors-${createHash('sha1').update(url).digest('hex').slice(0, 8)}.csv`));
|
||||
|
||||
const errors = new Map<number, number>();
|
||||
const errors = new Map<number, { parallaxMas: number; relativeError: number }>();
|
||||
for (const row of rows) {
|
||||
const hip = parseOptionalNumber(row['hip']);
|
||||
const parallaxMas = parseOptionalNumber(row['plx']);
|
||||
const errorMas = parseOptionalNumber(row['e_plx']);
|
||||
if (hip !== undefined && parallaxMas !== undefined && parallaxMas > 0 && errorMas !== undefined) {
|
||||
errors.set(hip, errorMas / parallaxMas);
|
||||
errors.set(hip, { parallaxMas, relativeError: errorMas / parallaxMas });
|
||||
}
|
||||
}
|
||||
if (errors.size < MIN_HIPPARCOS_ERRORS) {
|
||||
|
||||
Reference in New Issue
Block a user