Skip to main content

astro-resolver

Astrophotography toolkit for FITS workflows and imaging pipelines. Two independent packages, one library:

Package Purpose
astro_resolver Turns messy OBJECT headers into clean folder names
astro_quality Analyses FITS subframes for stacking quality

Installation

pip install astro-resolver

# With star detection / FWHM support (recommended):
pip install "astro-resolver[quality]"

astro_resolver — Object name resolution

Resolves FITS OBJECT header values like "M 81", "sh2-101" or "barnard 33" into filesystem-safe folder names such as "M81_BodesGalaxy" or "B33_HorseheadNebula".

Features

  • Local-first — SIMBAD only as a last resort
  • Solar System, Caldwell, custom aliases, OpenNGC (~14k objects), the complete Sharpless catalogue and selected Barnard objects bundled offline
  • Stellar catalog IDs (HD, HIP, SAO, TYC, …) skip the SIMBAD name round-trip and go straight to coordinate lookup
  • Coordinate fallback via SIMBAD when name lookup fails
  • Output is always filesystem-safe (no spaces, special chars)

Resolution priority

  1. Solar System (local)
  2. Caldwell (local)
  3. Custom aliases (local)
  4. OpenNGC offline (~14k NGC/IC objects)
  5. Sharpless catalog (Sh 2-xxx HII regions)
  6. Barnard catalog (dark nebulae)
  7. Sharpless by coordinates (local)
  8. SIMBAD by name (online)
  9. SIMBAD by coordinates (online)
  10. Sanitized header string as fallback

Usage

from astro_resolver import resolve_object_name

resolve_object_name("M 81")           # → "M81_BodesGalaxy"
resolve_object_name("sh2-101")        # → "Sh2-101_TulipNebula"
resolve_object_name("barnard 33")     # → "B33_HorseheadNebula"

# Stellar catalog ID with coordinates → SIMBAD coord lookup
resolve_object_name("HD 1", ra="00 05 09", dec="+67 50 24")

The resolver accepts an optional log callable so it can be plugged into Siril scripts (log=siril.log) or any other host environment.

The bundled Sharpless data contains all 313 H II regions from VizieR catalogue VII/20, based on Sharpless (1959), ApJS 4, 257 (1959ApJS....4..257S). Its coordinates retain the original B1900 frame. Common names are a separately curated convenience layer and are not fields from the historical catalogue.

from astro_resolver import sharpless_lookup

entry = sharpless_lookup("Sh 2-101")
entry.catalog_id          # → "Sh2-101"
entry.diameter_arcmin     # → 20
entry.ra_b1900            # original catalogue coordinate

astro_quality — Subframe quality analysis

Analyses FITS light frames for stacking quality. Supports any camera (DSLR, ZWO ASI, QHY, DWARF, …) — Bayer RAW and mono alike. Calibration frames (Flats, Darks, Bias) are detected and skipped automatically via filename patterns and the IMAGETYP FITS header.

Metrics per subframe

Metric Description Direction
background_noise Sky background σ (ADU, sigma-clipped) lower = better
snr_proxy Signal/noise ratio proxy higher = better
gradient Spatial background variation (light pollution gradient) lower = better
saturation_pct Fraction of saturated pixels (%) lower = better
star_count Number of detected stars (transparency proxy) higher = better
fwhm_median Median star width in luminance pixels (focus/seeing) lower = better
eccentricity Median star roundness 0=round 1=line (tracking quality) lower = better

Star count, FWHM and eccentricity require photutils (pip install "astrolib[quality]").

Scoring

Each subframe receives a relative quality score 0–100 within its session. Scores are relative: a score of 90 in a poor night may be worse in absolute terms than 70 in an excellent night. Use raw metric values for cross-session comparison.

Grade Score Meaning
A 85–100 Excellent
B 70–84 Good
C 50–69 Average
D 30–49 Poor
F 0–29 Reject

Usage

Analyse a single frame:

from pathlib import Path
from astro_quality import analyze_fits, score_sub, grade, compute_session_stats

result = analyze_fits(Path("IC443_0042.fits"))
if not result["error"]:
    m = result["metrics"]
    print(f"Noise: {m['background_noise']:.1f} ADU")
    print(f"SNR:   {m['snr_proxy']:.1f}")
    print(f"Stars: {m['star_count']}")   # -1 if photutils not installed

Score a session (relative quality):

from astro_quality import analyze_fits, compute_session_stats, score_sub, grade

results = [analyze_fits(f) for f in session_fits_files]
stats   = compute_session_stats(results)

for r in results:
    s = score_sub(r["metrics"], stats)
    print(f"{grade(s)} ({s:.1f})  {r['filename']}")

Scan an archive and build a quality report:

from pathlib import Path
from astro_quality import build_report_data
from astro_quality.archive import iter_sessions, iter_fits
from astro_quality.cache import load_cache, save_cache

ARCHIVE = Path("z:/AstroArchiv/Objects")
CACHE   = Path("quality_cache.json")

cache = load_cache(CACHE)

for obj, sess, sess_path in iter_sessions(ARCHIVE):
    for f in iter_fits(sess_path):          # skips Flats/Darks automatically
        if str(f) not in cache:
            result = analyze_fits(f)
            result["object"]  = obj
            result["session"] = sess
            cache[str(f)] = result

save_cache(cache, CACHE)

report = build_report_data(cache, obj_filter="IC443")
for sess, data in report["IC443_GemA"].items():
    print(f"{sess}: {data['score_mean']:.1f} ({data['grade']})  "
          f"noise={data['stats']['noise_med']:.1f}")

Calibration frame detection:

from astro_quality.archive import is_calib_frame
from pathlib import Path

is_calib_frame(Path("Flat_30s_ISO800_0001.fit"))   # True
is_calib_frame(Path("Light_M51_120s_0001.fit"))    # False

Archive layout expected by iter_sessions / iter_fits

<root>/
├── IC443_GemA/
│   ├── 2026-01-19_DWARF_3/
│   │   ├── IC 443_60s60_..._0001.fits   ← light frames
│   │   └── Flat_200ms_..._0001.fits     ← skipped automatically
│   └── 2026-04-01_DWARF_3/
│       └── ...
└── M51_WhirlpoolGalaxy/
    ├── 2026-04-25_ZWO_ASI585MC_Pro/
    │   └── Light_M51_120s_..._0001.fit
    └── presets/                          ← skipped (non-session dir)

Non-session subdirectories (presets, flats, calibration, masters, …) are skipped automatically.


Data sources

License

MIT

Release files for astro-resolver 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for astro-resolver 0.5.1
File Size Uploaded
astro_resolver-0.5.1.tar.gz 920.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for astro-resolver 0.5.1
File Interpreter ABI Platform
astro_resolver-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.9 MB

Release files / astro_resolver-0.5.1.tar.gz

Download URL astro_resolver-0.5.1.tar.gz
Size 920.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b3e548efeaba3ac72290c57a71d253d22afa35a88705806825dc77749898bcd1
BLAKE2b-256 checksum
How to use checksums
e5c362e7ba016f11ac5b69eade9ceb21771ff594b25cee95e63577714d92fcf0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / astro_resolver-0.5.1-py3-none-any.whl

Download URL astro_resolver-0.5.1-py3-none-any.whl
Size 931.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
18089561855074beb4ee4f41f164a034f1e50b29cd98c73a2d559a08ec3831fa
BLAKE2b-256 checksum
How to use checksums
490cdac3fe6efebe61ca99205e83fc189c79cb58de2cd710e9939edd66d4815d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

This release

0.5.1 This release

2 release files

0.5.0

2 release files

0.4.5

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page