Gaia DR3 Library
Gaia DR3, local. This dialog is four things in one: a downloader for the full Gaia DR3 XP spectral library (34 million calibrated stellar spectra), a downloader for the full Gaia DR3 astrometric catalog (1.6 billion stars), a 3D Stellar Neighborhood Explorer that puts every nearby star in its real place in galactic coordinates, and a Spectrum Viewer for inspecting individual XP spectra. The spectral and astrometric data power color calibration and plate solving throughout SASpro; the Neighborhood Explorer and the asterism overlay are their own payoff — a tour of what's actually out there within a few hundred parsecs of the Sun.
The scale of what you're installing. The spectral library tops out at 34,414,104 XP spectra (~134 GB at full depth). The astrometric library tops out at 1,585,128,120 stars across 41 files (~184 GB at full depth). The pre-built neighborhood extract is 14,708,327 stars within 300 parsecs with precomputed galactic Cartesian positions — 2.5 GB and the fastest way to see something interesting. You do not need the full catalog to get value; the Bright tiers alone cover most practical use.
Honest about DR3's limits. The DR3 astrometric builder did not capture parallax_error, so SASpro cannot do the standard parallax_over_error > 5 distance-reliability cut. It guards with (a) parallax > 0, (b) a RUWE < 1.4 cut, and (c) a hard 300 pc radius on the neighborhood extract. RUWE flags bad astrometric fits (unresolved binaries, blends) — it does NOT flag a clean fit whose parallax is simply too small to measure, which is why the 300 pc cap matters. Separately, Gaia saturates below G ≈ 2.2 and XP tops out at G ≈ 15, so the brightest naked-eye stars (Sirius, Vega, Alpha Cen, Procyon…) are missing entirely from the Gaia data and faint M dwarfs beyond 15 are missing from the spectra. Where it matters, this dialog says so in-line rather than hiding it. Gaia DR4 (expected late 2025) will add parallax_error and bp_rp and this whole picture gets honestly deeper.
1. The four tabs at a glance
- Spectrum Library — bulk XP spectra, five magnitude tiers, 220 MB (ultra-bright) up to ~73 GB (very faint). Install one tier at a time; the dialog resumes partial downloads automatically.
- Astrometry — bulk gaia_source astrometry (RA, Dec, proper motion, parallax, RUWE), six tiers plus the Stellar Neighborhood extract. 2.5 GB (neighborhood only) up to ~184 GB (full depth).
- Neighborhood Explorer — the 3D view. Needs the neighborhood extract installed, or falls back to a hardcoded list of naked-eye stars so you still see something. Supports true-color rendering if an XP spectral tier is also installed.
- Spectrum Viewer — look up one star's XP spectrum by name (SIMBAD-resolved), RA/Dec, or Gaia source_id.
Spectral and astrometric libraries live in separate folders (both configurable) because they're independent downloads — you might want all the astrometry for plate solving but only the Ultra-Bright + Bright spectral tiers for color calibration.
2. Spectrum Library tab — magnitude tiers
XP spectra are 343-point calibrated flux spectra from 336-1020 nm (2 nm step), extracted from Gaia's xp_sampled_mean_spectrum table. Sized by target magnitude range:
- Ultra-Bright (G < 8) — 54,735 spectra, ~220 MB. Arneb, Muphrid, Tania Australis, and tens of thousands of bright reference stars. Recommended minimum install.
- Bright (G 8-10) — 385,269 spectra, ~1.5 GB. Covers most calibration stars reachable from backyard setups. Recommended.
- Medium (G 10-12) — 2,426,887 spectra, ~9.5 GB across 4 files. Dense coverage for wide-field and narrowband imaging. Recommended.
- Faint (G 12-14) — 12,852,953 spectra, ~50 GB across 10 files. Deep coverage for long-exposure narrowband; file sizes range 2-9 GB each.
- Very Faint (G 14-15) — 18,694,260 spectra, ~73 GB across 10 files. The practical faint limit of DR3 XP.
Lookup priority. Any SASpro tool that needs an XP spectrum tries (1) your installed bulk library, (2) the live download cache (gaia_xp_cache.sqlite), (3) the live Gaia archive. The library is first because it's instant; the archive is last because it's slow and needs internet. The practical consequence: install enough of the library that your typical frames hit (1) and never see the latency of (2) or (3).
3. Astrometry tab — magnitude tiers and the neighborhood extract
Astrometric data (RA, Dec, pmra, pmdec, parallax, G-mag, RUWE) across 41 physical files grouped into six user-facing tiers. Also hosts the pre-built Stellar Neighborhood extract that powers the 3D explorer:
- Stellar Neighborhood (within 300 pc) — 14,708,327 stars, ~2.5 GB (one file). Precomputed galactic XYZ (Sun at origin), ready to stream straight into the 3D view. Install this first if you want the Neighborhood Explorer to work; it's self-contained and the single biggest payoff per gigabyte in the whole dialog.
- Bright (G < 17) — 139,482,635 stars, 16.9 GB across 4 files. Enough for most wide-field plate solves. Recommended.
- Faint (G 17-18) — 129,386,971 stars, 15.7 GB across 5 files. Typical narrow-field solves.
- Very Faint (G 18-19) — 239,207,768 stars, 28.9 GB across 10 files.
- Extreme (G 19-20) — 420,229,783 stars, 50.4 GB across 10 files.
- Maximum Depth (G ≥ 20) — 656,820,963 stars, 72.0 GB across 12 files. Full DR3 depth; the practical faint limit.
Separate library folders. Spectral and astrometric libraries have independent locations (set each via the "Change Location…" button on its tab). A full astrometric install at 184 GB probably wants to live on a different drive than your system drive; the migration dialog has move / copy / switch-pointer options and shows per-file progress.
4. Install / Resume / Browse / Remove
Each tier row has four possible actions depending on state:
- Install — download all missing files for the tier from the SetiAstro CDN. Multi-file tiers show per-file and per-tier progress bars.
- Resume — appears when a tier is partially installed. Skips files already present (verified by size match) and downloads only what's missing. Interrupted downloads are safe to retry; the
.tmp staging file prevents corrupted partials.
- Browse… — point SASpro at files you already have (downloaded manually, copied from a backup, shared from another SASpro install). If one file is selected and others for the same tier sit in the same folder, SASpro asks whether to copy them all.
- Remove — delete all installed files for a tier. Confirmation dialog shows the exact disk space to be freed. Removes files only; nothing else in the directory is touched.
- Cancel — appears during active downloads. The partial
.tmp is cleaned up; already-completed files stay.
Change Location migration. The "Change Location…" button opens a dialog with three options for what to do with files already installed: move them to the new location (recommended — frees space on the old drive), copy them (useful for keeping a backup), or switch pointer only (leave files behind; they become inaccessible to SASpro until you move them manually). The move/copy uses shutil.move/copy2 so cross-device transfers work, and the migration is cancellable mid-flight with no data loss.
5. The Neighborhood Explorer — 3D view of your own neighborhood
The headline feature. Open the tab, and you're looking at every Gaia DR3 star with a usable parallax inside the current radius, placed in galactic Cartesian coordinates with the Sun at the origin, the galactic plane in XY, and +Z toward the north galactic pole. A grid shows the midplane; three colored axes label the galactic directions; drop-lines fall from each star to the midplane so you can tell at a glance whether a bright point is far above the disk or just far from you.
Interaction. Drag to orbit · scroll to zoom · Ctrl+drag to pan · left-click a star to select · right-click a star for an action menu (view XP spectrum, resolve in SIMBAD, open in browser, wedge-slice from here, copy source_id). The nearest-star table on the right lists up to 300 stars by distance; clicking a row selects and highlights.
5.1. Region controls
- Region shape — Sphere (radius) keeps everything within r of the Sun. Cube (fills the grid) uses an axis-aligned box of half-side r on each axis, which exactly matches the drawn grid and fills the corners a sphere leaves empty.
- Radius (pc) — from 0.5 pc out to 750 pc. The extract itself stops at 300 pc; past that you only see the hardcoded naked-eye stars (which reach out to about 680 pc), and the status line says so.
- Preset buttons — jump to 3, 10, 25, 50, 100, 300, 750 pc.
- Min dist (pc) — when checked, carves a shell: everything closer than this is excluded. Use it to slice out (say) 200-220 pc and see where a structure actually lives instead of burying it under all the foreground stars.
- Max RUWE — astrometric-fit-quality cut, default 1.4. See the honest caveat at the top of this doc: RUWE catches bad fits, not distant stars with weak parallax.
- Max G mag — brightness cut on apparent G. Combined with the radius this is how you avoid rendering ten million faint dwarfs when you only care about O/B/A stars.
- Render cap — hard limit on how many stars will be fetched, decompressed and uploaded to the GPU. Default 2 million, hard ceiling 15 million. Values above 5 million prompt a one-time confirmation; the current value is persisted across sessions.
5.2. Color-by modes
- Absolute G (luminosity) — default. Computes MG = G - 5·log10(d) + 5 and maps the 5th-95th percentile to a blue-white-orange ramp: blue-white = luminous, orange = dim. Not exactly temperature (a luminous red giant and a hot blue dwarf can share an absolute magnitude), but it reads like a stellar HR-diagram slice through the field.
- Apparent G — maps the raw G magnitude. Shows what the sky looks like, not what the stars are.
- Distance — maps dist_pc. Nearest = blue-white, farthest = orange. Useful for seeing structure along the line of sight.
- XP Spectrum (true colour) — the real color each star emits, computed by integrating its actual XP spectrum against the CIE 1931 color matching functions and transforming to sRGB. Stars without an XP spectrum render as dim neutral grey so you can see which stars Gaia characterized and which it didn't. Requires at least one spectral tier installed. Capped at 20,000 spectra per load (the brightest ones are prioritized).
Why true color is slow. The XP library's bp_rp column is NULL and its synth_phot table is empty in DR3 (not populated by the bulk builder), so there's no shortcut: SASpro integrates the actual 343-point spectrum against the color matching functions on demand, which costs a zlib.decompress plus three dot products per star. Fine for thousands, impossible for millions. The 20k cap keeps the mode usable; results are cached by source_id so re-entering the mode or reloading the same radius doesn't re-integrate. Hardcoded bright stars already carry a color from B-V and are served instantly.
5.3. Depth cues
- Point size slider — scales apparent brightness → pixel size. Brighter stars render bigger (log-scaled, 5th-95th percentile normalization so a few bright outliers don't flatten the curve). Default base = 4 pixels.
- Drop lines — a vertical segment from each star down to the galactic midplane (z = 0), fading from the star's own color at the top to invisible at the plane. Without them a 3D scatter is genuinely ambiguous: a star high on screen could be above the plane or just distant. Capped at 20,000 stars (the nearest ones, where depth ambiguity matters most).
- Galactic axes — red (toward galactic centre, l = 0°), green (direction the Sun is moving in its orbit, l = 90°), blue (galactic north, +Z). A fourth, yellow axis labeled "Sun's N pole" shows the Sun's rotation axis as a tilt — not a direction of motion, not perpendicular to the galactic plane; it's offset from the galactic Z by the Sun's obliquity.
- Scale ticks — label each grid ring with its distance from the Sun in parsecs. Grid spacing is one fifth of the current radius; ticks appear on +X and +Y so at least one axis is always edge-on-readable.
5.4. The wedge slice
Right-click any star and pick Wedge slice from here… to open a popup that lets you cut an angular cone through the field centered on that star's galactic direction. You pick two half-angles — "along plane" (how far the wedge opens in galactic longitude, left/right along the disk) and "north-south" (how far it opens in galactic latitude, above/below the disk) — and the wedge stays active across radius and cut changes until you click Clear Wedge.
This is how you isolate a moving group, a stream, or anything else elongated along a direction without rotating the camera to look edge-on. Combine with a shell (Min dist + Radius) and you can slice a thin wedge-of-a-shell: say, a 20°-wide cone through 100-150 pc. The direction locks on the star's galactic position, so if you later change the radius the wedge still points the same way; only Clear Wedge removes it.
5.5. Selection and SIMBAD
Left-click a star in the view (or a row in the nearest-star table): a cyan highlight marker appears and the info panel shows source_id, distance in parsecs and light-years, G mag, absolute MG, RUWE, RA/Dec, and galactic XYZ. For hardcoded bright stars the panel instead shows the SIMBAD identifier, spectral type, B-V-derived Teff, and flags "weak parallax" when the Hipparcos SNR is below 5.
Resolve in SIMBAD does a cone search (20″ then 60″, to accommodate Gaia-J2016 vs SIMBAD-J2000 proper motion); for hardcoded stars it uses the direct identifier. Open in browser opens the SIMBAD web page (sim-coo for Gaia sources — their id often won't resolve directly in SIMBAD since most faint neighborhood dwarfs are uncatalogued; sim-id for named stars).
6. The Asterism Explorer — constellations in true 3D
Pick a constellation from the Asterism dropdown and the Gaia layer hides itself for a tour of what the familiar shape actually looks like in three dimensions. Nine asterisms ship:
- Orion — Betelgeuse / Bellatrix / the Belt / Rigel / Saiph / Meissa. Alnilam at ~600 pc is the far belt star; the rest of the figure sits at 77-380 pc.
- Big Dipper — Dubhe and Alkaid are the group's bookends and sit at 37.7 and 31.9 pc; the inner five are a Hipparcos-confirmed moving group at ~25 pc. The handle genuinely bends in 3D.
- Leo — the Sickle plus the body and tail. Regulus (24 pc) and Denebola (11 pc) are the near anchors; Eta Leo at ~550 pc is a distant supergiant hiding in plain sight.
- Scorpius — Antares and the stinger. Two foreground surprises: Epsilon Sco (20 pc) and Eta Sco (22 pc) sit far in front of the ~150-180 pc main figure.
- Cygnus (Northern Cross) — the depth champion. Gienah at 22 pc is a nearby wingtip; Sadr at ~560 pc is the distant chest. The cross is a nearby corner on a distant body.
- Boötes (the Kite) — Arcturus and Muphrid (both ~11 pc) sit nearly on top of you; the rest of the kite floats at 26-68 pc. The bottom apex of the kite is practically in your lap.
- Taurus — Aldebaran (20 pc) is a line-of-sight impostor floating in front of the Hyades V at 47 pc. The horns stretch out to 130 pc at Zeta Tau.
- Cassiopeia (the W) — Caph (17 pc) is near; Gamma Cas (168 pc) sits behind. The zigzag skews hard in depth.
- Summer Triangle — the single hardest depth-shock. Vega (7.68 pc), Altair (5.13 pc), Deneb (~433 pc). Three "equal-looking" first-magnitude stars spanning almost 85× in depth.
Earth View. Click the Earth View button and the camera snaps to the Sun looking toward the asterism's centroid. The 3D lines collapse into exactly the flat pattern you see from Earth. Then drag to orbit and watch the stars pull apart in depth — the pattern breaks, usually dramatically, and you see the real geometry. This is the whole point of the tool: a working demonstration that constellations are an accident of our viewing angle, not structures.
Distances are honest about where they aren't precise. RA/Dec are rock-solid (sky positions don't move at this precision). Nearby star distances are from Hipparcos/Gaia parallaxes and good to a few percent. Distant OB-star distances (Orion's belt especially, Deneb) are contested in the literature by factors of 2× and are flagged "approx" in the source. The whole point of the demo is the depth spread, not three-sig-fig ranges. Published values for Alnilam alone vary from 400 to 900 pc depending on the method.
While an asterism is active the Gaia layer is paused and radius/cut changes don't affect the view — the asterism owns the camera. Pick "— none —" from the dropdown to clear and return to the Gaia neighborhood.
7. The Spectrum Viewer tab
One star at a time, three search modes:
- Star Name (SIMBAD) — type a name, SIMBAD resolves it to a Gaia DR3 source_id (via its IDS column) or to RA/Dec. If the direct ID resolves and is in your library, it's served instantly; otherwise SASpro falls back to a positional cone search with progressively larger radii (default → 30″ → 120″ → 600″) to catch proper-motion offsets between SIMBAD's J2000 and Gaia's J2016.
- RA / Dec — decimal degrees plus a search radius in arcseconds, finds the nearest star in the library.
- Gaia source_id — direct lookup. Fastest if you already know the ID.
The matplotlib plot shows BP (336-680 nm, blue fill) and RP (640-1020 nm, red fill) with a shaded flux-error band if present. The info panel below gives source_id, RA/Dec, G mag, and the peak-wavelength / flux range summary.
The Neighborhood Explorer's right-click View XP Spectrum option opens a non-modal popup version of this viewer, so you can keep the 3D view's camera where it is and open several spectra side by side for comparison.
8. Workflow recipes
Minimum useful install (color calibration + the explorer). Spectrum Library → install Ultra-Bright (220 MB) and Bright (1.5 GB). Astrometry → install Stellar Neighborhood (within 300 pc) (2.5 GB). Total: ~4 GB. You now have offline color calibration for every reasonably bright reference star and the full 3D explorer with true-color mode. Good starting point before you commit to the big tiers.
Full color calibration setup. Spectrum Library → install Ultra-Bright + Bright + Medium (~11 GB). Covers calibration stars down to G 12, which is dense enough for most wide-field and narrowband work. Add Faint (50 GB) only if you're doing long-exposure narrowband where the medium tier runs out of reference stars.
Full plate-solving setup. Astrometry → install Bright (G < 17) (16.9 GB) + Stellar Neighborhood (2.5 GB). Covers most wide-field solves and enables the explorer. Add Faint (G 17-18) (15.7 GB) if you do narrow-field solves below 1° FOV.
Explore the ten nearest stars. Neighborhood Explorer → set radius to 5 pc, color by XP Spectrum (true colour), click Reset View. Alpha Centauri, Sirius, Procyon, and friends sit around the Sun; most are hardcoded naked-eye stars (Gaia saturates on them), so they get real colors from B-V and the panel shows SIMBAD identifier + spectral type. Right-click any to open its SIMBAD page.
The Hyades edge-on. Pick Taurus from the asterism dropdown, click Earth View, then orbit. You'll see Aldebaran break away from the Hyades V (it's a 20 pc foreground star, not a cluster member) and the Hyades themselves resolve into a compact clump at 47 pc, with the horns stretching back to 130 pc. This is the clearest demonstration of a constellation that is mostly real geometry with one impostor.
Orion in true 3D. Pick Orion from the dropdown and Earth View. The flat hourglass collapses. Now orbit: Alnilam leaps backward to ~600 pc, Bellatrix stays near at 77 pc, and Betelgeuse hangs around 168 pc. The belt is not a line, the asterism is not a plane. Press Reset View to see the Gaia neighborhood again.
Slice a shell: what's actually at 200-220 pc? Set radius 220, check Min dist and set 200, color by Distance. You see only stars in a 20-parsec-thick shell. Combined with the Scorpius-Centaurus OB association region (galactic longitude ~290-350°), this is how you expose a specific moving group without the near-field haze. The wedge slice (next recipe) narrows it further.
The wedge trick. Click a star you suspect is in a stream or association, right-click, Wedge slice from here…, pick a narrow along-plane half-angle (10-15°) and a tight north-south half-angle (5-10°). The field reduces to a cone of galactic directions centered on that star. Combine with a shell cut (above) and you have a parsec-thick, degree-wide slice through the local field — enough to see whether its neighbors share its distance and velocity direction.
9. Credits and citation
Written by Franklin Marek as part of
SetiAstro Suite. Data from
Gaia DR3 (Gaia Collaboration et al., 2022, A&A 649, A1;
gea.esac.esa.int/archive) — XP spectra from
xp_sampled_mean_spectrum, astrometry from
gaia_source, calibrated by DPAC. CIE 1931 color matching functions via the Wyman, Sloan & Shirley (2013, JCGT) analytical multi-lobe Gaussian fits. Hardcoded naked-eye star table draws distances from
Hipparcos and
Gaia parallaxes (via SIMBAD) and derives sRGB from B-V photometry through the same CIE integrator. ICRS → galactic frame transforms via
astropy.coordinates. 3D rendering via
pyqtgraph.opengl and
PyOpenGL. If you publish science using data served through this dialog, cite Gaia DR3 directly and acknowledge DPAC's calibration pipeline.
Technical details
10.1. Data layer — bulk + cache + archive
Every XP spectrum lookup goes through a three-tier priority chain:
- Local bulk library (
GaiaBulkLibrary) — one sqlite3 connection per installed tier file, opened with PRAGMA query_only = ON. Primary-key lookup on source_id.
- Live download cache (
GaiaSpectraDB at gaia_xp_cache.sqlite) — populated by the on-demand downloader as fallback hits accumulate.
- Live Gaia archive — only hit when neither local source has the id.
Spectra are stored as zlib-compressed 343-element float32 arrays:
\lambda \in [336, 1020]\ \mathrm{nm},\quad \Delta\lambda = 2\ \mathrm{nm}
plus an optional per-point flux error array. The decompressed flux maps onto the shared WL_GRID at load time.
10.2. Dec-zone + RA composite index (why no R-tree)
SQLite's R-Tree extension isn't compiled into every platform's shipped SQLite — especially in frozen builds — so SASpro avoids it. Instead the astrometric builder assigns each star a declination zone:
z = \lfloor (\delta + 90^\circ) / h_z \rfloor
where hz is the zone height in degrees (stored in the file's metadata, typically 1°), and builds a composite index on (dec_zone, ra). Bounding-box queries then scan only the relevant zone-slabs and the relevant RA range inside each. The exact dec filter is applied in Python at the end to tighten the coarse zone bounds. RA wraparound at 0/360° is handled by splitting the query into two ranges when ra_lo > ra_hi.
10.3. Row-count hardcoding
Every GroupDef carries an exact n_items known from the build (54,735 for Ultra-Bright; 656,820,963 for Maximum Depth). The UI reads this directly instead of running COUNT(*) on multi-GB WITHOUT ROWID tables, which is minutes of pointless disk churn to rediscover a known number. The library objects still expose get_group_status() for external callers — but with a header warning that it's slow on the deep tiers.
10.4. Sphere vs cube with the dist_pc index
The neighborhood extract has a dist_pc index. The sphere query is a trivial range scan on it:
d_{\mathrm{pc}} \leq r
The cube query is the interesting one. Three separate BETWEEN clauses on x_pc, y_pc, z_pc couldn't use the dist_pc index, forcing a full-table scan of ~14M rows. Trick: a cube of half-side e is contained in the sphere of radius e√3 (the cube's circumradius):
R_{\mathrm{sph}}(e) = e\sqrt{3}
So the cube query is written as all four predicates together:
|x_{\mathrm{pc}}|, |y_{\mathrm{pc}}|, |z_{\mathrm{pc}}| \leq e,\quad d_{\mathrm{pc}} \leq e\sqrt{3}
The dist_pc clause prunes with the index before the per-axis test runs, turning a full-table scan into an index-range scan plus a cheap filter. Shell queries (Min dist checked) add a floor:
r_{\min} \leq d_{\mathrm{pc}} \leq r_{\max}
which also rides the dist_pc index.
10.5. The wedge query
Angular wedges are expressed purely in galactic Cartesian coordinates — no SQL trigonometry (not guaranteed in a frozen SQLite's math extensions). Given a direction (ℓ0, b0) and half-widths (Δℓ, Δb), the latitude band becomes a z-over-dist bound:
\sin(b_{\min}) \leq z_{\mathrm{pc}} / d_{\mathrm{pc}} \leq \sin(b_{\max})
and the longitude band uses a dot product against the direction unit vector, with the "within Δℓ" condition expressed as a sign plus a cosine inequality:
x_{\mathrm{pc}} \cos\ell_0 + y_{\mathrm{pc}} \sin\ell_0 \geq 0
(x_{\mathrm{pc}} \cos\ell_0 + y_{\mathrm{pc}} \sin\ell_0)^2 \geq (x_{\mathrm{pc}}^2 + y_{\mathrm{pc}}^2) \cos^2(\Delta\ell)
Both ride the dist_pc index because they coexist with the sphere/shell/cube predicates. The longitude test is skipped entirely at Δℓ ≥ 90° ("no longitude limit").
10.6. True color — XP spectrum to sRGB
For each star with an XP spectrum, SASpro integrates the flux against the CIE 1931 color matching functions (via the Wyman-Sloan-Shirley analytical fits — multi-lobe piecewise Gaussians, ~1% accurate, no lookup table):
X = \int_{336}^{1020} f(\lambda)\, \bar{x}(\lambda)\, d\lambda
Y = \int_{336}^{1020} f(\lambda)\, \bar{y}(\lambda)\, d\lambda
Z = \int_{336}^{1020} f(\lambda)\, \bar{z}(\lambda)\, d\lambda
Luminance is normalized out — chromaticity only, because point size already encodes apparent brightness:
(x, y, z) = (X, Y, Z) / Y
Then the standard D65-adapted linear sRGB transform:
\mathbf{c}_{\mathrm{lin}} = \mathbf{M}_{\mathrm{D65}} \cdot (X, Y, Z)^\top
with the sRGB transfer function applied in two cases:
c_{\mathrm{sRGB}} = 12.92\, c_{\mathrm{lin}} \quad \mathrm{if}\ c_{\mathrm{lin}} \leq 0.0031308
c_{\mathrm{sRGB}} = 1.055\, c_{\mathrm{lin}}^{1/2.4} - 0.055 \quad \mathrm{otherwise}
Out-of-gamut negative values (common for very hot blue stars) are clipped to zero. Verified against blackbody spectra: 2500 K → deep orange, 5772 K (Sun) → near-white, 10000 K → blue, with the B/R ratio strictly increasing in temperature. Trapezoidal integration via np.trapezoid with a fallback to np.trapz for pre-NumPy-2.0 installs.
10.7. Distance from parallax
Classic inversion:
d_{\mathrm{pc}} = 1000 / \varpi_{\mathrm{mas}}
Only sources with ϖ > 0 are kept at build time. Absolute magnitude from the distance modulus:
M_G = G - 5 \log_{10}(d_{\mathrm{pc}}) + 5
used for the "Absolute G (luminosity)" color mode. Both values are stored precomputed in the neighborhood extract so neither per-star arithmetic nor a LOG10 extension is needed at query time.
10.8. Color ramps
For the non-true-color modes (absmag, appmag, dist), SASpro computes a per-star scalar v, finds its 5th and 95th percentiles over the loaded sample, and maps:
t = \mathrm{clip}((v - P_5) / (P_{95} - P_5),\; 0,\; 1)
then applies a two-segment ramp: t = 0 → blue-white (luminous), t = 0.5 → warm cream, t = 1 → orange-red (dim). Percentile normalization keeps a few outliers from flattening the entire ramp. Non-finite values (unknown G) sit at t = 0.5.
10.9. Point sizes
Brighter stars render bigger. Normalize G by the 2nd-98th percentile:
t_G = \mathrm{clip}((G - P_2(G)) / (P_{98}(G) - P_2(G)),\; 0,\; 1)
and apply a linear curve where t = 0 (brightest) gets a size multiplier of 2.6 and t = 1 (faintest) gets 0.5:
s = s_{\mathrm{base}} \cdot (2.6 - 2.1\, t_G)
with a minimum of 1.2 pixels so faint stars don't disappear sub-pixel. The slider sets s_base (default 4 px).
10.10. 3D picking via projected screen position
pyqtgraph has no built-in 3D picking, so SASpro does it directly. Transform every displayed point through the current MVP matrix:
\mathbf{c}_{\mathrm{clip}} = \mathbf{M}_{\mathrm{proj}} \cdot \mathbf{M}_{\mathrm{view}} \cdot (x, y, z, 1)^\top
Divide by w to get normalized device coordinates:
(x_{\mathrm{ndc}}, y_{\mathrm{ndc}}) = (c_x / c_w, c_y / c_w)
Map to screen pixels (note the y-axis flip — NDC +Y is up, screen +Y is down):
(x_s, y_s) = \left(\frac{1}{2}(x_{\mathrm{ndc}} + 1) \cdot W,\; \frac{1}{2}(1 - y_{\mathrm{ndc}}) \cdot H\right)
The point with the smallest screen distance to the click wins, with a 12-pixel tolerance and a w > 0 "in front of the camera" filter. Click vs. drag is distinguished by cursor travel: < 4 pixels = click, more = orbit drag. Right-clicks that pass the click test open the context menu; left-clicks emit pointPicked. Cheap enough at these point counts to avoid a dedicated color-picking render pass.
10.11. Projection matrix across pyqtgraph versions
pyqtgraph 0.13.x's projectionMatrix() takes no required arguments; 0.14.0 requires both region and viewport. Calling the 0.13 form on 0.14 raises TypeError; the reverse works but with a region derived from the viewport. SASpro tries the 0.14 signature first, falls back to 0.13 on TypeError, and logs anything else — a prior bare except here silently returned None, which meant every click did nothing on 0.14.
10.12. Asterism 3D placement
Each hardcoded asterism star carries ICRS RA, Dec, and distance in parsecs. Conversion to galactic Cartesian uses astropy's frame transforms, matching the frame the neighborhood extract and the hardcoded naked-eye star table already use. The equivalent closed form (what astropy's transform produces to within precession/nutation corrections):
x = d \cos(b) \cos(\ell)
y = d \cos(b) \sin(\ell)
z = d \sin(b)
where (ℓ, b) are the galactic coordinates derived from the ICRS (α, δ) by astropy's rotation. Results are cached per-asterism so switching between constellations is instant on the second visit.
10.13. Earth View camera math
The Earth View button for asterisms places the camera at the Sun and points it at the asterism's centroid c, so the 3D lines collapse to the familiar sky projection. From centroid position c = (cx, cy, cz) and r = |c|:
\mathrm{azimuth} = \mathrm{atan2}(-c_y, -c_x)
\mathrm{elevation} = \arcsin(-c_z / r)
(the negatives come from the convention that pyqtgraph's azimuth/elevation describe the camera's look-from direction, not look-at). Distance is set to r so the near plane sits at the Sun. Orbit from there makes the depth structure obvious.
10.14. Hardcoded naked-eye stars
Gaia saturates below G ≈ 3, which excludes the brightest ~800 naked-eye stars entirely. SASpro ships a hardcoded table built from Hipparcos / Gaia parallaxes (via SIMBAD cross-match) with per-star SIMBAD identifier, V mag, spectral type, B-V-derived Teff, pre-computed galactic XYZ, pre-computed sRGB (from the same CIE integrator as the XP mode, applied to a B-V-indexed stellar spectrum), and a plx_ok flag (false when the Hipparcos parallax SNR is < 5). Their source_ids are negative as a sentinel — any accidental Gaia lookup fails loudly instead of silently hitting the wrong star. They're folded into every neighborhood load that includes them in its radius; the one plausible overlap (Arneb, Alpha Leporis, G = 2.55, in the XP library) sits at 680 pc, well outside any routine query.
10.15. Lazy GL initialization and the dialog-flicker tradeoff
Constructing a QOpenGLWidget inside an already-visible top-level window makes Qt destroy and recreate that window's native handle. The GL view here is deliberately built in the Neighborhood Explorer tab's showEvent, not in the dialog's constructor. The consequence: the first visit to the Neighborhood Explorer tab shows a brief flicker of this dialog as its native window is rebuilt. The alternative — building the GL view at construction time — would push that flicker to SASpro's main window, which is much worse. A one-off flicker of a modal dialog is the right place to pay the cost. Subsequent visits do nothing (an idempotent _gl_ready flag guards against re-entry if showEvent is pumped during construction).
10.16. Non-blocking downloads and migration
Every download, every file migration, and the neighborhood load itself run on dedicated QThread workers. The main dialog signals through them; a cancel flag lets any in-flight operation stop at the next chunk boundary. The dialog's closeEvent cancels all workers and wait()s for them to settle (2-second timeout) before the dialog actually closes, so a user who hits Close during a 50-GB download gets clean file-system state (partial .tmp cleaned up, no orphan handles).
10.17. DR4 wishlist — what's being worked around
None of these are fixable without a re-fetch when Gaia DR4 lands:
parallax_error absent — the big one. Blocks the standard parallax_over_error > 5 cut. Mitigated with RUWE + 300 pc radius guard (both documented inline wherever they appear in the UI).
bp_rp empty in the sources table — would let every astrometric source have a real color even without an XP spectrum. Currently ~80% of the solar neighborhood renders grey in true-color mode because it has no XP spectrum.
synth_phot table empty — the schema exists but was never populated by the bulk builder. If populated at build time, true color would become three real reads instead of a zlib.decompress plus three CIE integrals per star.
- XP magnitude limits (G 2.2 to G 15) — may extend in DR4; the tier boundaries would need revisiting.
Everything above the DR4 line is a feature of the current data, documented honestly in the UI rather than fixed in post-processing. When DR4 lands, both catalogs get rebuilt and the explorer reaches farther honestly, not just optimistically.