stargazing-core 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. stargazing_core-0.1.0/LICENSE +21 -0
  2. stargazing_core-0.1.0/PKG-INFO +59 -0
  3. stargazing_core-0.1.0/README.md +34 -0
  4. stargazing_core-0.1.0/pyproject.toml +76 -0
  5. stargazing_core-0.1.0/setup.cfg +4 -0
  6. stargazing_core-0.1.0/src/stargazing_core/__init__.py +70 -0
  7. stargazing_core-0.1.0/src/stargazing_core/_catalog.py +128 -0
  8. stargazing_core-0.1.0/src/stargazing_core/_celestial_models.py +53 -0
  9. stargazing_core-0.1.0/src/stargazing_core/_constellation.py +14 -0
  10. stargazing_core-0.1.0/src/stargazing_core/_coord.py +16 -0
  11. stargazing_core-0.1.0/src/stargazing_core/_ephemeris.py +10 -0
  12. stargazing_core-0.1.0/src/stargazing_core/_filtering.py +560 -0
  13. stargazing_core-0.1.0/src/stargazing_core/_geo.py +70 -0
  14. stargazing_core-0.1.0/src/stargazing_core/_moon.py +94 -0
  15. stargazing_core-0.1.0/src/stargazing_core/_mosaic.py +156 -0
  16. stargazing_core-0.1.0/src/stargazing_core/_pagination.py +27 -0
  17. stargazing_core-0.1.0/src/stargazing_core/_planets.py +57 -0
  18. stargazing_core-0.1.0/src/stargazing_core/_shooting_plan.py +281 -0
  19. stargazing_core-0.1.0/src/stargazing_core/_telescope.py +188 -0
  20. stargazing_core-0.1.0/src/stargazing_core/_timegrid.py +32 -0
  21. stargazing_core-0.1.0/src/stargazing_core/data/objects.json +1 -0
  22. stargazing_core-0.1.0/src/stargazing_core.egg-info/PKG-INFO +59 -0
  23. stargazing_core-0.1.0/src/stargazing_core.egg-info/SOURCES.txt +37 -0
  24. stargazing_core-0.1.0/src/stargazing_core.egg-info/dependency_links.txt +1 -0
  25. stargazing_core-0.1.0/src/stargazing_core.egg-info/requires.txt +5 -0
  26. stargazing_core-0.1.0/src/stargazing_core.egg-info/top_level.txt +1 -0
  27. stargazing_core-0.1.0/tests/test_catalog.py +105 -0
  28. stargazing_core-0.1.0/tests/test_celestial_models.py +96 -0
  29. stargazing_core-0.1.0/tests/test_constellation.py +20 -0
  30. stargazing_core-0.1.0/tests/test_coord.py +23 -0
  31. stargazing_core-0.1.0/tests/test_filtering.py +421 -0
  32. stargazing_core-0.1.0/tests/test_geo.py +69 -0
  33. stargazing_core-0.1.0/tests/test_moon.py +98 -0
  34. stargazing_core-0.1.0/tests/test_mosaic.py +184 -0
  35. stargazing_core-0.1.0/tests/test_pagination.py +105 -0
  36. stargazing_core-0.1.0/tests/test_planets.py +49 -0
  37. stargazing_core-0.1.0/tests/test_shooting_plan.py +240 -0
  38. stargazing_core-0.1.0/tests/test_telescope.py +129 -0
  39. stargazing_core-0.1.0/tests/test_timegrid.py +43 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StarGazer1995
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: stargazing-core
3
+ Version: 0.1.0
4
+ Summary: Shared astronomical computation for stargazing tools — telescope optics, deep-sky catalog, equipment presets
5
+ Author: StarGazer1995
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: astropy<8.0,>=7.0
20
+ Requires-Dist: numpy>=1.24
21
+ Requires-Dist: pandas>=3.0.3
22
+ Requires-Dist: pydantic>=2.0
23
+ Requires-Dist: pytz
24
+ Dynamic: license-file
25
+
26
+ # stargazing-core
27
+
28
+ Shared astronomical computation package for the stargazing toolchain —
29
+ telescope optics, deep-sky catalog, and equipment presets used by both
30
+ [mcp-stargazing](https://github.com/StarGazer1995/mcp-stargazing) and
31
+ [stargazing-place-finder](https://github.com/StarGazer1995/stargazing-place-finder).
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install stargazing-core
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from stargazing_core import TelescopeConfig, TelescopeOptics, TELESCOPE_PRESETS
43
+
44
+ # Look up a preset
45
+ config = TELESCOPE_PRESETS['seestar-s50']
46
+
47
+ # Compute derived optics
48
+ optics = config.compute_optics()
49
+ print(f'FOV: {optics.fov_width_deg:.1f}° × {optics.fov_height_deg:.1f}°')
50
+ # → FOV: 1.7° × 1.3°
51
+ ```
52
+
53
+ ## Development
54
+
55
+ ```bash
56
+ uv sync
57
+ uv run pytest -v
58
+ uv run ruff format --check . && uv run ruff check .
59
+ ```
@@ -0,0 +1,34 @@
1
+ # stargazing-core
2
+
3
+ Shared astronomical computation package for the stargazing toolchain —
4
+ telescope optics, deep-sky catalog, and equipment presets used by both
5
+ [mcp-stargazing](https://github.com/StarGazer1995/mcp-stargazing) and
6
+ [stargazing-place-finder](https://github.com/StarGazer1995/stargazing-place-finder).
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install stargazing-core
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```python
17
+ from stargazing_core import TelescopeConfig, TelescopeOptics, TELESCOPE_PRESETS
18
+
19
+ # Look up a preset
20
+ config = TELESCOPE_PRESETS['seestar-s50']
21
+
22
+ # Compute derived optics
23
+ optics = config.compute_optics()
24
+ print(f'FOV: {optics.fov_width_deg:.1f}° × {optics.fov_height_deg:.1f}°')
25
+ # → FOV: 1.7° × 1.3°
26
+ ```
27
+
28
+ ## Development
29
+
30
+ ```bash
31
+ uv sync
32
+ uv run pytest -v
33
+ uv run ruff format --check . && uv run ruff check .
34
+ ```
@@ -0,0 +1,76 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "stargazing-core"
7
+ version = "0.1.0"
8
+ description = "Shared astronomical computation for stargazing tools — telescope optics, deep-sky catalog, equipment presets"
9
+ requires-python = ">=3.11"
10
+ authors = [
11
+ { name = "StarGazer1995" },
12
+ ]
13
+ readme = "README.md"
14
+ license = { text = "MIT" }
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Scientific/Engineering :: Astronomy",
25
+ ]
26
+
27
+ dependencies = [
28
+ "astropy>=7.0,<8.0",
29
+ "numpy>=1.24",
30
+ "pandas>=3.0.3",
31
+ "pydantic>=2.0",
32
+ "pytz",
33
+ ]
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "pytest>=7.0",
38
+ "pytest-cov>=4.0",
39
+ "ruff>=0.5",
40
+ ]
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["src"]
44
+ include = ["stargazing_core*"]
45
+
46
+ [tool.setuptools.package-data]
47
+ "stargazing_core" = ["data/*.json"]
48
+
49
+ # ── Ruff ──────────────────────────────
50
+
51
+ [tool.ruff]
52
+ target-version = "py311"
53
+ line-length = 100
54
+
55
+ [tool.ruff.lint]
56
+ select = [
57
+ "E", "W", "F", "I", # pyflakes + isort
58
+ "N", # pep8-naming
59
+ "B", # flake8-bugbear
60
+ "SIM", # flake8-simplify
61
+ "C4", # flake8-comprehensions
62
+ ]
63
+
64
+ [tool.ruff.format]
65
+ quote-style = "single"
66
+ indent-style = "space"
67
+
68
+ [tool.ruff.lint.isort]
69
+ known-first-party = ["stargazing_core"]
70
+
71
+ # ── pytest ─────────────────────────────
72
+
73
+ [tool.pytest.ini_options]
74
+ testpaths = ["tests"]
75
+ pythonpath = ["src"]
76
+ addopts = "-v --tb=short"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,70 @@
1
+ """stargazing-core — shared astronomical computation.
2
+
3
+ Provides :class:`TelescopeConfig`, :class:`TelescopeOptics`, equipment presets,
4
+ and deep-sky catalog loading used by both ``mcp-stargazing`` and
5
+ ``stargazing-place-finder``.
6
+ """
7
+
8
+ from stargazing_core._catalog import DeepSkyCatalog, load_objects
9
+ from stargazing_core._celestial_models import (
10
+ CelestialPosition,
11
+ MoonInfo,
12
+ RiseSet,
13
+ VisiblePlanet,
14
+ )
15
+ from stargazing_core._constellation import identify_constellation
16
+ from stargazing_core._coord import validate_coordinates
17
+ from stargazing_core._filtering import (
18
+ filter_candidates_by_lst,
19
+ match_telescope_targets,
20
+ score_deep_sky_objects,
21
+ )
22
+ from stargazing_core._geo import GeoBounds, GeoPoint, TimeInfo
23
+ from stargazing_core._moon import calculate_moon_info, get_moon_altaz
24
+ from stargazing_core._mosaic import MosaicGrid, MosaicPanel, compute_mosaic_grid
25
+ from stargazing_core._pagination import PaginatedResult
26
+ from stargazing_core._planets import get_visible_planets
27
+ from stargazing_core._shooting_plan import (
28
+ ShootingPlan,
29
+ ShootingSlot,
30
+ generate_shooting_schedule,
31
+ )
32
+ from stargazing_core._telescope import (
33
+ TELESCOPE_PRESETS,
34
+ TelescopeConfig,
35
+ TelescopeOptics,
36
+ )
37
+ from stargazing_core._timegrid import find_rise_set_indices, generate_time_grid
38
+
39
+ __all__ = [
40
+ 'TelescopeConfig',
41
+ 'TelescopeOptics',
42
+ 'TELESCOPE_PRESETS',
43
+ 'DeepSkyCatalog',
44
+ 'load_objects',
45
+ 'GeoPoint',
46
+ 'GeoBounds',
47
+ 'TimeInfo',
48
+ 'CelestialPosition',
49
+ 'RiseSet',
50
+ 'MoonInfo',
51
+ 'VisiblePlanet',
52
+ 'PaginatedResult',
53
+ 'validate_coordinates',
54
+ 'calculate_moon_info',
55
+ 'get_moon_altaz',
56
+ 'get_visible_planets',
57
+ 'filter_candidates_by_lst',
58
+ 'match_telescope_targets',
59
+ 'score_deep_sky_objects',
60
+ 'identify_constellation',
61
+ 'generate_time_grid',
62
+ 'find_rise_set_indices',
63
+ 'ShootingPlan',
64
+ 'ShootingSlot',
65
+ 'generate_shooting_schedule',
66
+ 'MosaicPanel',
67
+ 'MosaicGrid',
68
+ 'compute_mosaic_grid',
69
+ ]
70
+ __version__ = '0.1.0a1'
@@ -0,0 +1,128 @@
1
+ """Deep-sky object catalog loader.
2
+
3
+ Reads the embedded ``objects.json`` (11 000+ Messier / NGC / IC objects with
4
+ angular-size data) via :mod:`importlib.resources`. Callers get plain Python
5
+ ``list[dict]`` — no astropy required.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import threading
12
+ from importlib import resources
13
+ from typing import Any, Optional
14
+
15
+ # ── Fallback angular sizes by object type ──────────────────────
16
+ # Same values as used by the mcp-stargazing download_data.py script.
17
+
18
+ _FALLBACK_SIZES: dict[str, tuple[float, float]] = {
19
+ # galaxies (elongated — min ≈ 0.5 × maj)
20
+ 'Gx': (2.0, 1.0),
21
+ 'GiG': (2.0, 1.0),
22
+ 'GiP': (2.0, 1.0),
23
+ 'GiC': (1.5, 0.7),
24
+ 'SyG': (1.5, 0.7),
25
+ 'Sy1': (1.5, 0.7),
26
+ 'Sy2': (1.5, 0.7),
27
+ 'LIN': (1.5, 0.7),
28
+ 'AGN': (1.5, 0.7),
29
+ 'BLL': (1.5, 0.7),
30
+ 'SBG': (1.5, 0.7),
31
+ 'H2G': (1.5, 0.7),
32
+ 'PaG': (1.5, 0.7),
33
+ 'G': (2.0, 1.0),
34
+ 'AG?': (1.5, 0.7),
35
+ 'IG': (2.0, 1.0),
36
+ 'rG': (1.5, 1.0),
37
+ 'EmG': (2.0, 1.0),
38
+ 'BiC': (2.0, 1.0),
39
+ 'G?': (2.0, 1.0),
40
+ 'PoG': (1.0, 1.0),
41
+ 'mul': (2.0, 2.0),
42
+ # clusters (round)
43
+ 'GlC': (5.0, 5.0),
44
+ 'Gb': (4.0, 4.0),
45
+ 'OpC': (15.0, 15.0),
46
+ 'Cl*': (10.0, 10.0),
47
+ 'Cl?': (10.0, 10.0),
48
+ 'ClG': (10.0, 10.0),
49
+ 'OC': (10.0, 10.0),
50
+ # nebulae
51
+ 'PN': (0.5, 0.5),
52
+ 'HII': (15.0, 10.0),
53
+ 'RNe': (15.0, 10.0),
54
+ 'Nb': (15.0, 10.0),
55
+ 'C+N': (15.0, 10.0),
56
+ 'ISM': (20.0, 12.0),
57
+ 'sh': (15.0, 10.0),
58
+ 'Em*': (5.0, 5.0),
59
+ 'GNe': (15.0, 10.0),
60
+ 'EmO': (10.0, 10.0),
61
+ 'HH': (0.5, 0.5),
62
+ 'Opt': (1.0, 1.0),
63
+ # supernova remnants
64
+ 'SNR': (8.0, 8.0),
65
+ }
66
+
67
+ # thread-safe cache
68
+ _catalog_cache: Optional[list[dict[str, Any]]] = None
69
+ _catalog_lock = threading.Lock()
70
+
71
+
72
+ def _load_data_resource(filename: str) -> list[dict[str, Any]]:
73
+ """Read packaged JSON data from ``stargazing_core/data/``."""
74
+ pkg = resources.files('stargazing_core')
75
+ return json.loads((pkg / 'data' / filename).read_text(encoding='utf-8'))
76
+
77
+
78
+ def load_objects() -> list[dict[str, Any]]:
79
+ """Return the full deep-sky object catalog (thread-safe, cached)."""
80
+ global _catalog_cache
81
+
82
+ if _catalog_cache is not None:
83
+ return _catalog_cache
84
+
85
+ with _catalog_lock:
86
+ if _catalog_cache is not None: # pragma: no cover — thread-safety
87
+ return _catalog_cache
88
+ _catalog_cache = _load_data_resource('objects.json')
89
+
90
+ return _catalog_cache
91
+
92
+
93
+ def get_angular_size_fallback(obj_type: str) -> Optional[tuple[float, float]]:
94
+ """Return ``(maj_arcmin, min_arcmin)`` fallback for *obj_type*, or ``None``."""
95
+ return _FALLBACK_SIZES.get(obj_type)
96
+
97
+
98
+ class DeepSkyCatalog:
99
+ """Lightweight wrapper around the deep-sky object catalog.
100
+
101
+ Loads the 11 000+ object catalog from packaged JSON data on first use.
102
+ """
103
+
104
+ def __init__(self) -> None:
105
+ self._objects = load_objects()
106
+
107
+ def __len__(self) -> int:
108
+ return len(self._objects)
109
+
110
+ def all(self) -> list[dict[str, Any]]:
111
+ """Return every object in the catalog."""
112
+ return self._objects
113
+
114
+ def by_types(self, types: set[str]) -> list[dict[str, Any]]:
115
+ """Filter objects whose ``type`` field is in *types*."""
116
+ return [o for o in self._objects if o.get('type') in types]
117
+
118
+ def with_angular_size(self) -> list[dict[str, Any]]:
119
+ """Return objects that have measured (non-fallback) angular-size data."""
120
+ return [o for o in self._objects if o.get('angular_size_maj_arcmin') is not None]
121
+
122
+ def type_counts(self) -> dict[str, int]:
123
+ """Return ``{type: count}`` for the full catalog."""
124
+ counts: dict[str, int] = {}
125
+ for o in self._objects:
126
+ t = o.get('type', '?')
127
+ counts[t] = counts.get(t, 0) + 1
128
+ return counts
@@ -0,0 +1,53 @@
1
+ """Pydantic models for celestial (astronomy) domain data.
2
+
3
+ These models provide typed representations for values computed by the
4
+ astronomical functions in this package (``calculate_moon_info``,
5
+ ``get_visible_planets``, etc.).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class CelestialPosition(BaseModel):
14
+ """Altitude and azimuth of a celestial object."""
15
+
16
+ altitude: float = Field(
17
+ description='Altitude above horizon in degrees (0°=horizon, 90°=zenith)'
18
+ )
19
+ azimuth: float = Field(description='Compass direction in degrees (0°=North, 90°=East)')
20
+
21
+
22
+ class RiseSet(BaseModel):
23
+ """Rise and set times of a celestial object."""
24
+
25
+ rise_time: str | None = Field(
26
+ default=None, description='Rise time as ISO string (local timezone)'
27
+ )
28
+ set_time: str | None = Field(
29
+ default=None, description='Set time as ISO string (local timezone)'
30
+ )
31
+
32
+
33
+ class MoonInfo(BaseModel):
34
+ """Detailed information about the Moon's phase and position."""
35
+
36
+ illumination: float = Field(description='Fraction of the moon illuminated (0.0 to 1.0)')
37
+ phase_name: str = Field(description="Phase description (e.g. 'Waxing Gibbous')")
38
+ age_days: float = Field(description='Approximate age of the moon in days since New Moon')
39
+ elongation: float = Field(description='Angular separation from Sun in degrees')
40
+ earth_distance: float = Field(description='Distance from Earth in km')
41
+ altitude: float | None = Field(
42
+ default=None, description='Altitude above horizon in degrees (requires lat/lon)'
43
+ )
44
+ azimuth: float | None = Field(
45
+ default=None, description='Compass direction in degrees (requires lat/lon)'
46
+ )
47
+
48
+
49
+ class VisiblePlanet(CelestialPosition):
50
+ """A planet currently visible above the horizon."""
51
+
52
+ name: str = Field(description='Planet name')
53
+ constellation: str | None = Field(default=None, description='Constellation the planet is in')
@@ -0,0 +1,14 @@
1
+ """Constellation identification.
2
+
3
+ Copied from ``mcp-stargazing/src/celestial.py``. Pure computation — uses
4
+ astropy's built-in constellation boundaries.
5
+ """
6
+
7
+ from astropy.coordinates import SkyCoord, get_constellation
8
+
9
+ from . import _ephemeris # noqa: F401 — ensure ephemeris is configured
10
+
11
+
12
+ def identify_constellation(sky_coord: SkyCoord) -> str:
13
+ """Identify which constellation a coordinate belongs to."""
14
+ return get_constellation(sky_coord)
@@ -0,0 +1,16 @@
1
+ """Pure coordinate utilities — no astropy or other heavy deps required.
2
+
3
+ Originally from ``mcp-stargazing/src/utils.py``. Kept dependency-free so
4
+ both projects can use these without pulling in astropy.
5
+ """
6
+
7
+
8
+ def validate_coordinates(lat: float, lon: float) -> bool:
9
+ """Return ``True`` when *lat* and *lon* are within valid geographic ranges.
10
+
11
+ >>> validate_coordinates(40.0, 116.0)
12
+ True
13
+ >>> validate_coordinates(100.0, 0.0)
14
+ False
15
+ """
16
+ return -90.0 <= lat <= 90.0 and -180.0 <= lon <= 180.0
@@ -0,0 +1,10 @@
1
+ """One-time astropy ephemeris configuration.
2
+
3
+ Importing this module sets the solar-system ephemeris to ``'builtin'`` so
4
+ that planet and moon calculations work without network access. Must be
5
+ imported before any other astropy coordinate transformations.
6
+ """
7
+
8
+ from astropy.coordinates import solar_system_ephemeris
9
+
10
+ solar_system_ephemeris.set('builtin')