stdpipe 0.4.0__tar.gz → 0.4.2__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.
- stdpipe-0.4.2/CHANGELOG.md +100 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/MANIFEST.in +1 -0
- {stdpipe-0.4.0/stdpipe.egg-info → stdpipe-0.4.2}/PKG-INFO +3 -3
- {stdpipe-0.4.0 → stdpipe-0.4.2}/pyproject.toml +2 -2
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/__init__.py +1 -1
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/astrometry.py +104 -28
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/astrometry_quad.py +184 -68
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/catalogs.py +78 -21
- stdpipe-0.4.2/stdpipe/data/legacysurvey_bricks.fits.gz +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry.py +135 -36
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_iraf.py +15 -2
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_measure.py +221 -53
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_model.py +108 -10
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_psf.py +90 -47
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/pipeline.py +8 -8
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/plots.py +205 -58
- stdpipe-0.4.2/stdpipe/psf.py +2434 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/realbogus_features.py +3 -1
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/resolve.py +34 -5
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/simulation.py +2 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/templates.py +186 -80
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/utils.py +71 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2/stdpipe.egg-info}/PKG-INFO +3 -3
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe.egg-info/SOURCES.txt +1 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe.egg-info/requires.txt +1 -1
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_astrometry.py +74 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_astrometry_quad.py +49 -2
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_background_gradients.py +126 -8
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_catalogs.py +141 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry.py +158 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry_biases.py +6 -2
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry_iraf.py +11 -10
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry_measure.py +109 -16
- stdpipe-0.4.2/tests/test_photometry_model.py +602 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry_psf.py +348 -10
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_plots.py +133 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_psf.py +64 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_resolve.py +76 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_sep_psf_bias.py +1 -1
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_templates.py +221 -20
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_utils.py +153 -0
- stdpipe-0.4.0/stdpipe/data/legacysurvey_bricks.fits.gz +0 -0
- stdpipe-0.4.0/stdpipe/psf.py +0 -1542
- stdpipe-0.4.0/tests/test_photometry_model.py +0 -291
- {stdpipe-0.4.0 → stdpipe-0.4.2}/LICENSE.md +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/README.md +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/setup.cfg +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/setup.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/artefacts.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/astrometry_wcs.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/cutouts.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/data/ps1skycells.txt +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/db.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/fringe_removal.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/lcs.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_background.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/photometry_quality.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/realbogus.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/reproject.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/sfft.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/smoothing.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe/subtraction.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe.egg-info/dependency_links.txt +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe.egg-info/not-zip-safe +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/stdpipe.egg-info/top_level.txt +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_artefacts.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_astrometry_wcs.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_background_estimation.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_background_fringes.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_background_nongrid.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_background_polynomial.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_fringe_removal.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_fringe_removal_fourier.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_lcs.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_moffat_photometry_limits.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_photometry_quality.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_pipeline.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_realbogus.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_realbogus_features.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_reproject.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_sfft.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_simulation.py +0 -0
- {stdpipe-0.4.0 → stdpipe-0.4.2}/tests/test_smoothing.py +0 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to STDPipe are documented in this file. Changes in
|
|
4
|
+
releases up to 0.4.0 are only available in the
|
|
5
|
+
[commit history](https://github.com/karpov-sv/stdpipe/commits/master).
|
|
6
|
+
|
|
7
|
+
## 0.4.2 - 2026-10-31
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- PSF fitters (`photometry_psf.measure_objects_psf()`,
|
|
12
|
+
`photometry_measure.measure_objects_sep()` and
|
|
13
|
+
`photometry_iraf.measure_objects_psf()`) now store fitted positions in
|
|
14
|
+
`x`/`y` and keep the input ones in `x_orig`/`y_orig`, as centroiding does.
|
|
15
|
+
The `x_psf`/`y_psf` columns are gone; `get_objects_sextractor()` still
|
|
16
|
+
reports SExtractor's own `x_psf`/`y_psf`.
|
|
17
|
+
- `psf.create_psf_model()` no longer uses photutils `EPSFBuilder`. The new
|
|
18
|
+
builder places star pixels at their true sub-pixel phases, iteratively
|
|
19
|
+
recentres and renormalizes the model, rejects outliers per pixel, subtracts
|
|
20
|
+
neighbours with the model itself, and sizes the stamp automatically from the
|
|
21
|
+
stacked stellar profile. This removes a ~0.8% PSF flux bias and 2-6% flux
|
|
22
|
+
losses for Moffat-like PSFs, and is several times faster. Resulting models
|
|
23
|
+
differ slightly from previous versions.
|
|
24
|
+
- `measure_objects_psf()` default `maxiters` raised from 3 to 100 (it is a
|
|
25
|
+
limit on function evaluations, and fits did not converge before), and
|
|
26
|
+
non-convergence is now detected using the correct photutils flag bit.
|
|
27
|
+
- `measure_objects_sep()` now uses a 5-sigma clipped mean for the local
|
|
28
|
+
background by default, for both aperture photometry (was unclipped median)
|
|
29
|
+
and optimal extraction (was 3-sigma clipped mean).
|
|
30
|
+
- Legacy Survey templates are taken from DR11 instead of DR10. Previously
|
|
31
|
+
cached DR10 bricks are reused as is, as their filenames do not encode the
|
|
32
|
+
data release.
|
|
33
|
+
- `resolve.simbadResolve()` queries Simbad only (instead of Simbad, NED and
|
|
34
|
+
VizieR), using the current Sesame URL.
|
|
35
|
+
- Requires `sep-x>=1.5.2`, which correctly resamples point-sampled
|
|
36
|
+
supersampled PSF models (PSFEx, `create_psf_model()`).
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- `create_psf_model(degree='auto')` selects the spatial polynomial degree of
|
|
41
|
+
the PSF model by spatial cross-validation, up to `max_degree`.
|
|
42
|
+
- `order='auto'` for SIP order selection in `refine_wcs_quadhash()` and
|
|
43
|
+
`refine_wcs_simple()`, as already supported by `refine_wcs_scamp()`.
|
|
44
|
+
- `bg_estimator` (`'mode'`, `'median'`, `'mean'`) with `clip_sigma`/`clip_iters`
|
|
45
|
+
for the local background in `measure_objects()` and `measure_objects_sep()`.
|
|
46
|
+
- `filter_type='matched'` in `get_objects_sextractor()`, so that the detection
|
|
47
|
+
threshold is in units of the filtered noise, as in `sep.extract()`.
|
|
48
|
+
- `get_objects_sep()` reports background flux in the aperture (`bg_flux`,
|
|
49
|
+
`bg_fluxerr`), and per-source `fwhm_model` and `aper` columns with a
|
|
50
|
+
position-dependent FWHM model. `measure_objects_sep()` also reports
|
|
51
|
+
`bg_fluxerr`.
|
|
52
|
+
- VizieR mirror fallback in `get_cat_vizier()` (`catalogs.vizier_mirrors`,
|
|
53
|
+
used on network errors only) and a `timeout` argument.
|
|
54
|
+
- Sesame mirror fallback in `resolve` (`resolve.sesame_mirrors`), and
|
|
55
|
+
`resolve()` falls through to the next method when Simbad or TNS lookups
|
|
56
|
+
fail.
|
|
57
|
+
- `cache` option in `templates.get_hips_image()`.
|
|
58
|
+
- `utils.file_lock()` and inter-process locking of skycell cache downloads in
|
|
59
|
+
`templates.get_skycells()`.
|
|
60
|
+
- `plots.render_lock` serializing Matplotlib rendering in `figure_saver`
|
|
61
|
+
across threads.
|
|
62
|
+
- Support for photutils 3.0.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- Local background in `measure_objects()` was truncated to integers.
|
|
67
|
+
- Background aperture noise in `get_objects_sep()` counted the sky Poisson
|
|
68
|
+
noise twice.
|
|
69
|
+
- Masked object positions were measured at the values hidden under the mask.
|
|
70
|
+
- PSF fits of stars with masked pixels nearby were discarded as failed.
|
|
71
|
+
- PSF sub-pixel phase errors: centre of even-sized PSFEx models, conversion
|
|
72
|
+
of sub-pixel integrated models in the photutils backend, and resampling of
|
|
73
|
+
point-sampled models in the SEP backend.
|
|
74
|
+
- `get_psf_stamp()` lost >5% of the peak of even-sized point-sampled models.
|
|
75
|
+
- Robust `photometry_model.match()` with statsmodels 0.15, and huge cancelling
|
|
76
|
+
coefficients for rank-deficient designs; errors of terms not constrained by
|
|
77
|
+
the data are now NaN.
|
|
78
|
+
- Catalogue band augmentation: Gaia DR2 produced no Johnson-Cousins
|
|
79
|
+
magnitudes since astroquery 0.4.8, Gaia EDR3 was treated as DR2, and
|
|
80
|
+
`augment_bands=False` was ignored in `get_cat_vizier()`.
|
|
81
|
+
- Legacy Survey bricks present in both northern and southern lists were
|
|
82
|
+
always taken from the south, and bricks lacking the requested band produced
|
|
83
|
+
failed downloads.
|
|
84
|
+
- Remote FITS downloads no longer leave temporary files or fill the astropy
|
|
85
|
+
download cache.
|
|
86
|
+
- Three-color images in `plots.imshow()` are now properly normalized.
|
|
87
|
+
- `refine_wcs_simple(method='astrometrynet')` passed unmatched objects to
|
|
88
|
+
`fit-wcs`.
|
|
89
|
+
|
|
90
|
+
## 0.4.1 - 2026-08-07
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- NumPy 2 incompatibilities in flag handling and set membership.
|
|
95
|
+
- A perfect photometric fit in `photometry_model.match()` is treated as
|
|
96
|
+
success rather than a degenerate model.
|
|
97
|
+
|
|
98
|
+
## 0.4.0 - 2026-08-02
|
|
99
|
+
|
|
100
|
+
First release published through the automated PyPI release workflow.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stdpipe
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Simple Transient Detection Pipeline
|
|
5
5
|
Author-email: Sergey Karpov <karpov.sv@gmail.com>
|
|
6
6
|
Maintainer-email: Sergey Karpov <karpov.sv@gmail.com>
|
|
@@ -30,7 +30,7 @@ Project-URL: Homepage, https://github.com/karpov-sv/stdpipe
|
|
|
30
30
|
Project-URL: Documentation, https://stdpipe.readthedocs.io/
|
|
31
31
|
Project-URL: Repository, https://github.com/karpov-sv/stdpipe
|
|
32
32
|
Project-URL: Bug Tracker, https://github.com/karpov-sv/stdpipe/issues
|
|
33
|
-
Project-URL: Changelog, https://github.com/karpov-sv/stdpipe/
|
|
33
|
+
Project-URL: Changelog, https://github.com/karpov-sv/stdpipe/blob/master/CHANGELOG.md
|
|
34
34
|
Keywords: astronomy,astrophysics,transients,photometry,astrometry,image-processing,pipeline
|
|
35
35
|
Classifier: Development Status :: 4 - Beta
|
|
36
36
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -53,7 +53,7 @@ Requires-Dist: scipy
|
|
|
53
53
|
Requires-Dist: astropy>=4.1
|
|
54
54
|
Requires-Dist: matplotlib
|
|
55
55
|
Requires-Dist: astroquery>0.4.1
|
|
56
|
-
Requires-Dist: sep-x
|
|
56
|
+
Requires-Dist: sep-x>=1.5.2
|
|
57
57
|
Requires-Dist: astroscrappy
|
|
58
58
|
Requires-Dist: photutils>=2.2
|
|
59
59
|
Requires-Dist: scikit-image>=0.22.0
|
|
@@ -47,7 +47,7 @@ dependencies = [
|
|
|
47
47
|
"astropy>=4.1",
|
|
48
48
|
"matplotlib",
|
|
49
49
|
"astroquery>0.4.1",
|
|
50
|
-
"sep-x",
|
|
50
|
+
"sep-x>=1.5.2",
|
|
51
51
|
"astroscrappy",
|
|
52
52
|
"photutils>=2.2",
|
|
53
53
|
"scikit-image>=0.22.0", # Requires >=0.22.0 for NumPy 2.0 compatibility
|
|
@@ -103,7 +103,7 @@ Homepage = "https://github.com/karpov-sv/stdpipe"
|
|
|
103
103
|
Documentation = "https://stdpipe.readthedocs.io/"
|
|
104
104
|
Repository = "https://github.com/karpov-sv/stdpipe"
|
|
105
105
|
"Bug Tracker" = "https://github.com/karpov-sv/stdpipe/issues"
|
|
106
|
-
Changelog = "https://github.com/karpov-sv/stdpipe/
|
|
106
|
+
Changelog = "https://github.com/karpov-sv/stdpipe/blob/master/CHANGELOG.md"
|
|
107
107
|
|
|
108
108
|
# ============================================================================
|
|
109
109
|
# Tool configurations
|
|
@@ -594,6 +594,8 @@ def refine_wcs_simple(
|
|
|
594
594
|
cat_col_ra='RAJ2000',
|
|
595
595
|
cat_col_dec='DEJ2000',
|
|
596
596
|
method='astropy',
|
|
597
|
+
auto_max_order=5,
|
|
598
|
+
auto_threshold=0.05,
|
|
597
599
|
_tmpdir=None,
|
|
598
600
|
verbose=False,
|
|
599
601
|
):
|
|
@@ -605,8 +607,12 @@ def refine_wcs_simple(
|
|
|
605
607
|
List of detected objects with at least ``x``, ``y``, ``ra``, ``dec`` columns.
|
|
606
608
|
cat : astropy.table.Table
|
|
607
609
|
Reference astrometric catalogue.
|
|
608
|
-
order : int, optional
|
|
609
|
-
SIP polynomial order for distortion solution
|
|
610
|
+
order : int or str, optional
|
|
611
|
+
SIP polynomial order for distortion solution, or ``'auto'`` to select it
|
|
612
|
+
based on hold-out residuals: the matched pairs are split in two halves,
|
|
613
|
+
every order is fitted on one and its residuals measured on the other,
|
|
614
|
+
and the lowest order that is within ``auto_threshold`` from the best one
|
|
615
|
+
is used.
|
|
610
616
|
match : bool, optional
|
|
611
617
|
If True, perform nearest-neighbor matching between objects and catalogue.
|
|
612
618
|
If False, assume they are already matched line by line.
|
|
@@ -620,6 +626,11 @@ def refine_wcs_simple(
|
|
|
620
626
|
Catalogue column name for Declination.
|
|
621
627
|
method : str, optional
|
|
622
628
|
Fitting method. Either ``'astropy'`` or ``'astrometrynet'``.
|
|
629
|
+
auto_max_order : int, optional
|
|
630
|
+
Largest SIP order to try when ``order='auto'``. Default is 5.
|
|
631
|
+
auto_threshold : float, optional
|
|
632
|
+
Minimal relative improvement of hold-out residuals for accepting a
|
|
633
|
+
higher SIP order when ``order='auto'``. Default is 0.05, i.e. 5 percent.
|
|
623
634
|
_tmpdir : str, optional
|
|
624
635
|
If specified, temporary files will be created inside this path.
|
|
625
636
|
verbose : bool or callable, optional
|
|
@@ -634,6 +645,9 @@ def refine_wcs_simple(
|
|
|
634
645
|
# Simple wrapper around print for logging in verbose mode only
|
|
635
646
|
log = (verbose if callable(verbose) else print) if verbose else lambda *args, **kwargs: None
|
|
636
647
|
|
|
648
|
+
if isinstance(order, str) and order != 'auto':
|
|
649
|
+
raise ValueError("order must be an integer or 'auto', not '%s'" % order)
|
|
650
|
+
|
|
637
651
|
if match:
|
|
638
652
|
# Perform simple nearest-neighbor matching within given radius
|
|
639
653
|
oidx, cidx, dist = spherical_match(
|
|
@@ -646,34 +660,39 @@ def refine_wcs_simple(
|
|
|
646
660
|
_obj = obj
|
|
647
661
|
_cat = cat
|
|
648
662
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if method == 'astropy':
|
|
652
|
-
wcs = fit_wcs_from_points(
|
|
653
|
-
[_obj['x'], _obj['y']],
|
|
654
|
-
SkyCoord(_cat[cat_col_ra], _cat[cat_col_dec], unit='deg'),
|
|
655
|
-
sip_degree=order,
|
|
656
|
-
)
|
|
657
|
-
elif method == 'astrometrynet':
|
|
658
|
-
binname = None
|
|
659
|
-
|
|
660
|
-
# Rough estimate of frame dimensions
|
|
661
|
-
width = np.max(obj['x'])
|
|
662
|
-
height = np.max(obj['y'])
|
|
663
|
-
|
|
663
|
+
binname = None
|
|
664
|
+
if method == 'astrometrynet':
|
|
664
665
|
for path in ['.', '/usr/local', '/opt/local']:
|
|
665
666
|
if os.path.isfile(os.path.join(path, 'astrometry', 'bin', 'fit-wcs')):
|
|
666
667
|
binname = os.path.join(path, 'astrometry', 'bin', 'fit-wcs')
|
|
667
668
|
break
|
|
668
669
|
|
|
669
|
-
if binname:
|
|
670
|
+
if not binname:
|
|
671
|
+
log("Astrometry.Net fit-wcs binary not found")
|
|
672
|
+
|
|
673
|
+
def fit(order, _obj, _cat, quiet=False):
|
|
674
|
+
"""Fit WCS of a given SIP order to matched pairs of objects and catalogue stars"""
|
|
675
|
+
flog = (lambda *args, **kwargs: None) if quiet else log
|
|
676
|
+
wcs = None
|
|
677
|
+
|
|
678
|
+
if method == 'astropy':
|
|
679
|
+
wcs = fit_wcs_from_points(
|
|
680
|
+
[_obj['x'], _obj['y']],
|
|
681
|
+
SkyCoord(_cat[cat_col_ra], _cat[cat_col_dec], unit='deg'),
|
|
682
|
+
sip_degree=int(order),
|
|
683
|
+
)
|
|
684
|
+
elif method == 'astrometrynet' and binname:
|
|
685
|
+
# Rough estimate of frame dimensions
|
|
686
|
+
width = np.max(obj['x'])
|
|
687
|
+
height = np.max(obj['y'])
|
|
688
|
+
|
|
670
689
|
dirname = tempfile.mkdtemp(prefix='astrometry', dir=_tmpdir)
|
|
671
690
|
|
|
672
691
|
columns = [
|
|
673
|
-
fits.Column(name='FIELD_X', format='1D', array=
|
|
674
|
-
fits.Column(name='FIELD_Y', format='1D', array=
|
|
675
|
-
fits.Column(name='INDEX_RA', format='1D', array=
|
|
676
|
-
fits.Column(name='INDEX_DEC', format='1D', array=
|
|
692
|
+
fits.Column(name='FIELD_X', format='1D', array=_obj['x'] + 1),
|
|
693
|
+
fits.Column(name='FIELD_Y', format='1D', array=_obj['y'] + 1),
|
|
694
|
+
fits.Column(name='INDEX_RA', format='1D', array=_cat[cat_col_ra]),
|
|
695
|
+
fits.Column(name='INDEX_DEC', format='1D', array=_cat[cat_col_dec]),
|
|
677
696
|
]
|
|
678
697
|
tbhdu = fits.BinTableHDU.from_columns(columns)
|
|
679
698
|
filename = os.path.join(dirname, 'list.fits')
|
|
@@ -690,22 +709,79 @@ def refine_wcs_simple(
|
|
|
690
709
|
order,
|
|
691
710
|
)
|
|
692
711
|
|
|
693
|
-
|
|
694
|
-
|
|
712
|
+
flog('Running Astrometry.Net WCS fitter like that:')
|
|
713
|
+
flog(command)
|
|
695
714
|
|
|
696
715
|
os.system(command)
|
|
697
716
|
|
|
698
717
|
if os.path.isfile(wcsname):
|
|
699
718
|
header = fits.getheader(wcsname)
|
|
700
719
|
wcs = WCS(header)
|
|
701
|
-
|
|
720
|
+
flog('WCS fitter run succeeded')
|
|
702
721
|
else:
|
|
703
|
-
|
|
722
|
+
flog('WCS fitter run failed')
|
|
704
723
|
|
|
705
724
|
shutil.rmtree(dirname)
|
|
706
725
|
|
|
707
|
-
|
|
708
|
-
|
|
726
|
+
return wcs
|
|
727
|
+
|
|
728
|
+
def select_order():
|
|
729
|
+
"""Choose the SIP order by fitting one half of the matched pairs and
|
|
730
|
+
measuring the residuals on the other, unused one. Unlike the residuals
|
|
731
|
+
of the fit itself, these get worse as soon as it starts to overfit, so
|
|
732
|
+
we may just pick the lowest order that reaches the best of them. All
|
|
733
|
+
the orders are checked, as the improvement is not monotonic. Order 1
|
|
734
|
+
is skipped, as SIP only has terms of second order and above."""
|
|
735
|
+
log('Selecting the SIP order using half of the matches as a test set')
|
|
736
|
+
|
|
737
|
+
obj_fit, cat_fit = _obj[::2], _cat[::2]
|
|
738
|
+
obj_test, cat_test = _obj[1::2], _cat[1::2]
|
|
739
|
+
|
|
740
|
+
results = {}
|
|
741
|
+
|
|
742
|
+
for o in [0] + list(range(2, int(auto_max_order) + 1)):
|
|
743
|
+
# Linear part (CRPIX + CD) plus SIP terms with 2 <= i + j <= order,
|
|
744
|
+
# for both axes. Require at least 3 pairs per free parameter
|
|
745
|
+
n_sip = (o + 1) * (o + 2) // 2 - 3 if o >= 2 else 0
|
|
746
|
+
if len(obj_fit) < 3 * (6 + 2 * n_sip):
|
|
747
|
+
log(' order %d: %d matches, less than %d needed' % (o, len(obj_fit), 3 * (6 + 2 * n_sip)))
|
|
748
|
+
break
|
|
749
|
+
|
|
750
|
+
try:
|
|
751
|
+
w = fit(o, obj_fit, cat_fit, quiet=True)
|
|
752
|
+
except Exception:
|
|
753
|
+
w = None
|
|
754
|
+
if w is None:
|
|
755
|
+
break
|
|
756
|
+
|
|
757
|
+
ra, dec = w.all_pix2world(obj_test['x'], obj_test['y'], 0)
|
|
758
|
+
# Median is robust to the outliers among the test pairs
|
|
759
|
+
res = 3600 * np.nanmedian(
|
|
760
|
+
spherical_distance(ra, dec, cat_test[cat_col_ra], cat_test[cat_col_dec])
|
|
761
|
+
)
|
|
762
|
+
if not np.isfinite(res):
|
|
763
|
+
break
|
|
764
|
+
|
|
765
|
+
results[o] = res
|
|
766
|
+
|
|
767
|
+
log(' order %d: test residual %.3f arcsec' % (o, res))
|
|
768
|
+
|
|
769
|
+
if not results:
|
|
770
|
+
log('Order selection failed, using order 0')
|
|
771
|
+
return 0
|
|
772
|
+
|
|
773
|
+
# Lowest order that is within the threshold from the best residual
|
|
774
|
+
best_res = min(results.values())
|
|
775
|
+
best_order = min(o for o, res in results.items() if res <= best_res * (1 + auto_threshold))
|
|
776
|
+
|
|
777
|
+
log('Using SIP order %d' % best_order)
|
|
778
|
+
|
|
779
|
+
return best_order
|
|
780
|
+
|
|
781
|
+
if order == 'auto':
|
|
782
|
+
order = select_order()
|
|
783
|
+
|
|
784
|
+
wcs = fit(order, _obj, _cat)
|
|
709
785
|
|
|
710
786
|
if wcs:
|
|
711
787
|
if update:
|