seestar-photometry 0.2.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.
- seestar_photometry-0.2.0/.gitignore +38 -0
- seestar_photometry-0.2.0/.readthedocs.yaml +23 -0
- seestar_photometry-0.2.0/CHANGELOG.md +51 -0
- seestar_photometry-0.2.0/CLAUDE.md +82 -0
- seestar_photometry-0.2.0/LICENSE +674 -0
- seestar_photometry-0.2.0/PKG-INFO +161 -0
- seestar_photometry-0.2.0/README.md +110 -0
- seestar_photometry-0.2.0/docs/README.md +31 -0
- seestar_photometry-0.2.0/docs/api.md +84 -0
- seestar_photometry-0.2.0/docs/architecture.md +102 -0
- seestar_photometry-0.2.0/docs/astrometry-and-gaia.md +114 -0
- seestar_photometry-0.2.0/docs/conf.py +61 -0
- seestar_photometry-0.2.0/docs/data-format.md +118 -0
- seestar_photometry-0.2.0/docs/diagnostics.md +108 -0
- seestar_photometry-0.2.0/docs/example-data.md +86 -0
- seestar_photometry-0.2.0/docs/frame-table.md +143 -0
- seestar_photometry-0.2.0/docs/index.md +101 -0
- seestar_photometry-0.2.0/docs/install.md +108 -0
- seestar_photometry-0.2.0/docs/light-curves.md +149 -0
- seestar_photometry-0.2.0/docs/migration-from-mwcam.md +92 -0
- seestar_photometry-0.2.0/docs/photometry-design.md +127 -0
- seestar_photometry-0.2.0/docs/quickstart.md +146 -0
- seestar_photometry-0.2.0/docs/usecases/batch.md +140 -0
- seestar_photometry-0.2.0/docs/usecases/depth.md +122 -0
- seestar_photometry-0.2.0/docs/usecases/lightcurve.md +187 -0
- seestar_photometry-0.2.0/docs/usecases/saturation.md +99 -0
- seestar_photometry-0.2.0/docs/usecases/stacking.md +127 -0
- seestar_photometry-0.2.0/docs/usecases/zeropoint.md +106 -0
- seestar_photometry-0.2.0/example_data/SHA256 +1 -0
- seestar_photometry-0.2.0/examples/exoplanet_transit.py +116 -0
- seestar_photometry-0.2.0/examples/mwcam_lightcurve.py +106 -0
- seestar_photometry-0.2.0/examples/sn2026sqf_lightcurve.py +182 -0
- seestar_photometry-0.2.0/pyproject.toml +95 -0
- seestar_photometry-0.2.0/src/seestar_photometry/__init__.py +64 -0
- seestar_photometry-0.2.0/src/seestar_photometry/_style.py +218 -0
- seestar_photometry-0.2.0/src/seestar_photometry/astrometry.py +291 -0
- seestar_photometry-0.2.0/src/seestar_photometry/calibration.py +283 -0
- seestar_photometry-0.2.0/src/seestar_photometry/catalogs.py +182 -0
- seestar_photometry-0.2.0/src/seestar_photometry/contamination.py +116 -0
- seestar_photometry-0.2.0/src/seestar_photometry/debayer.py +123 -0
- seestar_photometry-0.2.0/src/seestar_photometry/depth.py +134 -0
- seestar_photometry-0.2.0/src/seestar_photometry/examples.py +329 -0
- seestar_photometry-0.2.0/src/seestar_photometry/frames.py +445 -0
- seestar_photometry-0.2.0/src/seestar_photometry/kron.py +90 -0
- seestar_photometry-0.2.0/src/seestar_photometry/lightcurves.py +386 -0
- seestar_photometry-0.2.0/src/seestar_photometry/photometry.py +483 -0
- seestar_photometry-0.2.0/src/seestar_photometry/pipeline.py +468 -0
- seestar_photometry-0.2.0/src/seestar_photometry/plots.py +985 -0
- seestar_photometry-0.2.0/src/seestar_photometry/project.py +180 -0
- seestar_photometry-0.2.0/src/seestar_photometry/quality.py +170 -0
- seestar_photometry-0.2.0/src/seestar_photometry/report.py +337 -0
- seestar_photometry-0.2.0/src/seestar_photometry/stacking.py +248 -0
- seestar_photometry-0.2.0/tests/conftest.py +357 -0
- seestar_photometry-0.2.0/tests/test_calibration.py +222 -0
- seestar_photometry-0.2.0/tests/test_frames.py +183 -0
- seestar_photometry-0.2.0/tests/test_imports.py +68 -0
- seestar_photometry-0.2.0/tests/test_lightcurves.py +319 -0
- seestar_photometry-0.2.0/tests/test_photometry.py +197 -0
- seestar_photometry-0.2.0/tests/test_pipeline.py +210 -0
- seestar_photometry-0.2.0/tests/test_real_data.py +464 -0
- seestar_photometry-0.2.0/tests/test_report.py +160 -0
- seestar_photometry-0.2.0/tools/build_example_data.py +304 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.coverage
|
|
9
|
+
htmlcov/
|
|
10
|
+
|
|
11
|
+
# uv
|
|
12
|
+
.venv/
|
|
13
|
+
|
|
14
|
+
# Editors
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
|
|
18
|
+
# Secrets -- ASTROMETRY_KEY lives in the environment, never in the repo
|
|
19
|
+
.env
|
|
20
|
+
|
|
21
|
+
# Derived products. Everything the pipeline writes goes to a project work_dir, which
|
|
22
|
+
# should live outside the repo; these patterns are a backstop for a work_dir set to '.'
|
|
23
|
+
frames.ecsv
|
|
24
|
+
stars.ecsv
|
|
25
|
+
measurements.ecsv
|
|
26
|
+
catalogue_*.ecsv
|
|
27
|
+
diagnostics/
|
|
28
|
+
errors.log
|
|
29
|
+
*.wcs
|
|
30
|
+
|
|
31
|
+
# Build outputs
|
|
32
|
+
dist/
|
|
33
|
+
docs/_build/
|
|
34
|
+
|
|
35
|
+
# Example data: built by tools/build_example_data.py and published as a GitHub
|
|
36
|
+
# release asset, not committed. SHA256 is tracked so the checksum is reviewable.
|
|
37
|
+
example_data/*
|
|
38
|
+
!example_data/SHA256
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
os: ubuntu-24.04
|
|
5
|
+
tools:
|
|
6
|
+
python: "3.12"
|
|
7
|
+
|
|
8
|
+
sphinx:
|
|
9
|
+
configuration: docs/conf.py
|
|
10
|
+
fail_on_warning: false
|
|
11
|
+
|
|
12
|
+
python:
|
|
13
|
+
install:
|
|
14
|
+
# Install the package itself so autodoc imports the real modules and picks up the
|
|
15
|
+
# version. `sep` and the stacking extras are mocked in conf.py, so they are not needed
|
|
16
|
+
# to build -- but the docs extra brings sphinx, myst-parser and the theme.
|
|
17
|
+
- method: pip
|
|
18
|
+
path: .
|
|
19
|
+
extra_requirements:
|
|
20
|
+
- docs
|
|
21
|
+
|
|
22
|
+
formats:
|
|
23
|
+
- pdf
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 — 2026-08-05
|
|
4
|
+
|
|
5
|
+
First release intended for publication.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Raw sub-exposure support.** `load_frame` now recognises the single-plane Bayer layout
|
|
10
|
+
(`BAYERPAT`) and demosaics it on load, so a raw 20 s sub is interchangeable with an
|
|
11
|
+
on-board stack everywhere downstream. The native mosaic is kept on `frame.bayer`.
|
|
12
|
+
New `debayer` module.
|
|
13
|
+
- **Local stacking** (`stacking`, needs the `stack` extra): astroalign registration as a
|
|
14
|
+
similarity transform — rotation included, which alt-az frames require — plus a
|
|
15
|
+
footprint-weighted mean. `StackReport` records residual, field rotation, coverage and any
|
|
16
|
+
rejected sub. A local stack writes native-dialect exposure keywords, so `frame_metadata`
|
|
17
|
+
reads it exactly as it reads an on-board stack.
|
|
18
|
+
- **Bundled example data** (`examples`): 1000×1000 cutouts of real MW Cam observations —
|
|
19
|
+
three on-board stacks, a CrowdSky multi-extension frame, five raw subs, solved `.wcs`
|
|
20
|
+
sidecars and a trimmed Gaia DR3 table. Every documented example and every real-data test
|
|
21
|
+
runs offline.
|
|
22
|
+
- **Documentation** for Read the Docs: install, quickstart, six use-case pages driven by the
|
|
23
|
+
bundled data, an API reference, and the design-decision records.
|
|
24
|
+
- 40 real-data tests alongside the existing synthetic suite (162 total).
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- `wcs_cache_path` now strips a compression suffix, so `frame.fits.gz` caches its WCS to
|
|
29
|
+
`frame.wcs` rather than the unreachable `frame.fits.wcs`. Gzipped frames would otherwise
|
|
30
|
+
have been re-solved on every run.
|
|
31
|
+
- An empty light curve keeps its full column schema, so a downstream call reports "no epochs"
|
|
32
|
+
instead of raising a bare `KeyError: 'dmag'`.
|
|
33
|
+
- `pipeline` uses a thread pool when `workers == 1`; a single worker gained nothing from a
|
|
34
|
+
separate process and paid the full spawn cost. A dead process pool now raises a message
|
|
35
|
+
naming the usual Windows cause (a driver without an `if __name__ == "__main__"` guard)
|
|
36
|
+
instead of an opaque `BrokenProcessPool`.
|
|
37
|
+
- `forced_photometry` no longer emits a divide warning for a fully off-frame aperture; the
|
|
38
|
+
SNR is `nan`, which is the right answer.
|
|
39
|
+
- CrowdSky's `FWHM` header values are in **arcsec** while this package reports **pixels**.
|
|
40
|
+
`quality.onboard_quality` renames them `onboard_fwhm_*_arcsec` so the two are not compared
|
|
41
|
+
by accident — they looked 2.4× apart until the plate scale was applied, after which they
|
|
42
|
+
agree to 2%.
|
|
43
|
+
|
|
44
|
+
## 0.1.0
|
|
45
|
+
|
|
46
|
+
Initial extraction of the photometry engine from the MW Cam analysis repo, where it had
|
|
47
|
+
existed as three diverging copies. See `docs/migration-from-mwcam.md`.
|
|
48
|
+
|
|
49
|
+
Verified against the frozen results it replaced: zero point bit-identical on 20 of 22 frames
|
|
50
|
+
(all 15 photometric-grade frames exactly 0.000 mmag apart), and forced photometry
|
|
51
|
+
bit-identical across a 52-frame CrowdSky set.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# seestar-photometry — conventions
|
|
2
|
+
|
|
3
|
+
Read this before changing code. The rationale for every numerical choice lives in
|
|
4
|
+
`docs/`; this file is the shape of the package.
|
|
5
|
+
|
|
6
|
+
## Names
|
|
7
|
+
|
|
8
|
+
Distribution `seestar-photometry`, import package `seestar_photometry`. Python
|
|
9
|
+
import names can't contain hyphens.
|
|
10
|
+
|
|
11
|
+
## Hard conventions
|
|
12
|
+
|
|
13
|
+
- **`BANDS = ("R", "G", "B")` is the canonical axis-0 order.** Every frame is a
|
|
14
|
+
`(3, ny, nx)` float32 array in that order. Every per-band array (`Extraction.rms`,
|
|
15
|
+
`.aperture`, `.fwhm`) is shape `(3,)` in that order. **Green is index 1 and is the
|
|
16
|
+
science band** (≈ Johnson V through the Seestar's IRCUT filter).
|
|
17
|
+
|
|
18
|
+
- **Image shape comes from the data, never the header.** `frame.g.shape` gives
|
|
19
|
+
`(ny, nx)`. The CrowdSky multi-extension layout has an *empty* primary HDU with no
|
|
20
|
+
`NAXIS1`/`NAXIS2`, so any code reading the shape from a header is broken on half
|
|
21
|
+
the datasets.
|
|
22
|
+
|
|
23
|
+
- **`SeestarFrame` and `Project` are thin data containers, not god objects.** All
|
|
24
|
+
analysis is module-level *functions* taking the object as their first argument
|
|
25
|
+
(`photometry.extract_sources(frame, ...)`). Methods that briefly lived on the
|
|
26
|
+
frame class were moved out on purpose.
|
|
27
|
+
|
|
28
|
+
- **Optional dependencies are imported lazily**, inside the function that needs
|
|
29
|
+
them — never at module top. `matplotlib` in particular: `import
|
|
30
|
+
seestar_photometry` must work in a core install. `tests/test_imports.py` asserts
|
|
31
|
+
`matplotlib` is absent from `sys.modules` after import, so this can't rot.
|
|
32
|
+
|
|
33
|
+
- **The standard photometry path and the cross-checks live in separate modules.**
|
|
34
|
+
`photometry` is what the science uses; `kron` (Kron/AUTO) is kept only for
|
|
35
|
+
star/galaxy separation and total-flux cross-checks. Do not promote it.
|
|
36
|
+
|
|
37
|
+
- **Derived products never go into the data tree or the FITS headers.** Tables and
|
|
38
|
+
figures go to `Project.work_dir`. The one exception is the per-frame `.wcs`
|
|
39
|
+
sidecar, which lives next to its FITS file because it is expensive to recompute
|
|
40
|
+
and shared between projects.
|
|
41
|
+
|
|
42
|
+
- **No defensive error handling in the measurement layer.** If a frame is broken,
|
|
43
|
+
let it raise; the batch runner in `pipeline` catches per-frame exceptions, records
|
|
44
|
+
a status, and keeps going. Don't scatter `try/except` through the science code.
|
|
45
|
+
|
|
46
|
+
## Module layers
|
|
47
|
+
|
|
48
|
+
Each module imports only from layers above it.
|
|
49
|
+
|
|
50
|
+
| Layer | Modules |
|
|
51
|
+
|---|---|
|
|
52
|
+
| data | `frames` |
|
|
53
|
+
| measure | `photometry`, `astrometry`, `catalogs` |
|
|
54
|
+
| calibrate | `calibration`, `quality` |
|
|
55
|
+
| science | `lightcurves`, `contamination`, `depth` |
|
|
56
|
+
| orchestrate | `project`, `pipeline` |
|
|
57
|
+
| inspect | `plots`, `report` |
|
|
58
|
+
| cross-check | `kron` |
|
|
59
|
+
|
|
60
|
+
## Two FITS layouts, two header dialects
|
|
61
|
+
|
|
62
|
+
Both normalise to the same in-memory frame. See `docs/data-format.md`.
|
|
63
|
+
|
|
64
|
+
| | `"cube"` — native Seestar | `"mef"` — CrowdSky |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| structure | 3-D primary HDU, FITS axes `(nx, ny, 3)` → numpy `(3, ny, nx)` | empty primary + `RED`/`GREEN`/`BLUE` ImageHDUs (+ `FOOTPRINT`, `STAR-TAB`) |
|
|
67
|
+
| sub count | `STACKCNT` | `NIMAGES` |
|
|
68
|
+
| `EXPTIME` | **per sub-exposure** (10 s) | **total on-sky** (e.g. 410 s) |
|
|
69
|
+
| total on-sky | `TOTALEXP` | `EXPTIME` |
|
|
70
|
+
| exposure span | `DATE-OBS` only | `OB-START` / `OB-END` (use these) |
|
|
71
|
+
|
|
72
|
+
That `EXPTIME` collision is the trap: reading it naively gives a per-sub time of
|
|
73
|
+
410 s and a mid-exposure timestamp ~5 minutes wrong. `frames.frame_metadata`
|
|
74
|
+
resolves the dialect; never read these keywords directly.
|
|
75
|
+
|
|
76
|
+
## Environment
|
|
77
|
+
|
|
78
|
+
`uv` only. `uv sync --extra dev`, then `uv run pytest` / `uv run python ...`.
|
|
79
|
+
Never `uv run --active`. Point PyCharm at `.venv`; don't let it make its own.
|
|
80
|
+
|
|
81
|
+
`ASTROMETRY_KEY` (astrometry.net) is read from the environment and never stored in
|
|
82
|
+
the repo — only needed for `solver="nova"`; the default ASTAP solver is offline.
|