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.
- stargazing_core-0.1.0/LICENSE +21 -0
- stargazing_core-0.1.0/PKG-INFO +59 -0
- stargazing_core-0.1.0/README.md +34 -0
- stargazing_core-0.1.0/pyproject.toml +76 -0
- stargazing_core-0.1.0/setup.cfg +4 -0
- stargazing_core-0.1.0/src/stargazing_core/__init__.py +70 -0
- stargazing_core-0.1.0/src/stargazing_core/_catalog.py +128 -0
- stargazing_core-0.1.0/src/stargazing_core/_celestial_models.py +53 -0
- stargazing_core-0.1.0/src/stargazing_core/_constellation.py +14 -0
- stargazing_core-0.1.0/src/stargazing_core/_coord.py +16 -0
- stargazing_core-0.1.0/src/stargazing_core/_ephemeris.py +10 -0
- stargazing_core-0.1.0/src/stargazing_core/_filtering.py +560 -0
- stargazing_core-0.1.0/src/stargazing_core/_geo.py +70 -0
- stargazing_core-0.1.0/src/stargazing_core/_moon.py +94 -0
- stargazing_core-0.1.0/src/stargazing_core/_mosaic.py +156 -0
- stargazing_core-0.1.0/src/stargazing_core/_pagination.py +27 -0
- stargazing_core-0.1.0/src/stargazing_core/_planets.py +57 -0
- stargazing_core-0.1.0/src/stargazing_core/_shooting_plan.py +281 -0
- stargazing_core-0.1.0/src/stargazing_core/_telescope.py +188 -0
- stargazing_core-0.1.0/src/stargazing_core/_timegrid.py +32 -0
- stargazing_core-0.1.0/src/stargazing_core/data/objects.json +1 -0
- stargazing_core-0.1.0/src/stargazing_core.egg-info/PKG-INFO +59 -0
- stargazing_core-0.1.0/src/stargazing_core.egg-info/SOURCES.txt +37 -0
- stargazing_core-0.1.0/src/stargazing_core.egg-info/dependency_links.txt +1 -0
- stargazing_core-0.1.0/src/stargazing_core.egg-info/requires.txt +5 -0
- stargazing_core-0.1.0/src/stargazing_core.egg-info/top_level.txt +1 -0
- stargazing_core-0.1.0/tests/test_catalog.py +105 -0
- stargazing_core-0.1.0/tests/test_celestial_models.py +96 -0
- stargazing_core-0.1.0/tests/test_constellation.py +20 -0
- stargazing_core-0.1.0/tests/test_coord.py +23 -0
- stargazing_core-0.1.0/tests/test_filtering.py +421 -0
- stargazing_core-0.1.0/tests/test_geo.py +69 -0
- stargazing_core-0.1.0/tests/test_moon.py +98 -0
- stargazing_core-0.1.0/tests/test_mosaic.py +184 -0
- stargazing_core-0.1.0/tests/test_pagination.py +105 -0
- stargazing_core-0.1.0/tests/test_planets.py +49 -0
- stargazing_core-0.1.0/tests/test_shooting_plan.py +240 -0
- stargazing_core-0.1.0/tests/test_telescope.py +129 -0
- 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,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')
|