thuban 0.0.4__tar.gz → 0.0.6__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 (48) hide show
  1. thuban-0.0.6/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  2. thuban-0.0.6/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. {thuban-0.0.4 → thuban-0.0.6}/.github/workflows/ci_fixed.yaml +1 -1
  4. {thuban-0.0.4 → thuban-0.0.6}/.pre-commit-config.yaml +6 -3
  5. thuban-0.0.6/CHANGELOG.md +32 -0
  6. {thuban-0.0.4/thuban.egg-info → thuban-0.0.6}/PKG-INFO +6 -4
  7. {thuban-0.0.4 → thuban-0.0.6}/README.md +1 -1
  8. {thuban-0.0.4 → thuban-0.0.6}/docs/conf.py +9 -1
  9. {thuban-0.0.4 → thuban-0.0.6}/pyproject.toml +2 -2
  10. {thuban-0.0.4 → thuban-0.0.6}/thuban/cli.py +37 -23
  11. {thuban-0.0.4 → thuban-0.0.6}/thuban/distortion.py +1 -1
  12. {thuban-0.0.4 → thuban-0.0.6}/thuban/pointing.py +60 -46
  13. {thuban-0.0.4 → thuban-0.0.6/thuban.egg-info}/PKG-INFO +6 -4
  14. {thuban-0.0.4 → thuban-0.0.6}/thuban.egg-info/SOURCES.txt +3 -1
  15. {thuban-0.0.4 → thuban-0.0.6}/thuban.egg-info/requires.txt +2 -1
  16. thuban-0.0.4/.github/workflows/docs.yaml +0 -36
  17. {thuban-0.0.4 → thuban-0.0.6}/.github/workflows/ci.yaml +0 -0
  18. {thuban-0.0.4 → thuban-0.0.6}/.github/workflows/publish.yaml +0 -0
  19. {thuban-0.0.4 → thuban-0.0.6}/.gitignore +0 -0
  20. {thuban-0.0.4 → thuban-0.0.6}/.readthedocs.yaml +0 -0
  21. {thuban-0.0.4 → thuban-0.0.6}/CITATION.cff +0 -0
  22. {thuban-0.0.4 → thuban-0.0.6}/LICENSE +0 -0
  23. {thuban-0.0.4 → thuban-0.0.6}/MANIFEST.in +0 -0
  24. {thuban-0.0.4 → thuban-0.0.6}/codecov.yaml +0 -0
  25. {thuban-0.0.4 → thuban-0.0.6}/docs/Makefile +0 -0
  26. {thuban-0.0.4 → thuban-0.0.6}/docs/_static/favicon.ico +0 -0
  27. {thuban-0.0.4 → thuban-0.0.6}/docs/_static/logo.png +0 -0
  28. {thuban-0.0.4 → thuban-0.0.6}/docs/index.rst +0 -0
  29. {thuban-0.0.4 → thuban-0.0.6}/docs/make.bat +0 -0
  30. {thuban-0.0.4 → thuban-0.0.6}/scripts/prepare_reduced_hip.py +0 -0
  31. {thuban-0.0.4 → thuban-0.0.6}/setup.cfg +0 -0
  32. {thuban-0.0.4 → thuban-0.0.6}/tests/__init__.py +0 -0
  33. {thuban-0.0.4 → thuban-0.0.6}/tests/test_catalog.py +0 -0
  34. {thuban-0.0.4 → thuban-0.0.6}/tests/test_cli.py +0 -0
  35. {thuban-0.0.4 → thuban-0.0.6}/tests/test_distortion.py +0 -0
  36. {thuban-0.0.4 → thuban-0.0.6}/tests/test_pointing.py +0 -0
  37. {thuban-0.0.4 → thuban-0.0.6}/tests/test_util.py +0 -0
  38. {thuban-0.0.4 → thuban-0.0.6}/tests/test_visualize.py +0 -0
  39. {thuban-0.0.4 → thuban-0.0.6}/thuban/__init__.py +0 -0
  40. {thuban-0.0.4 → thuban-0.0.6}/thuban/catalog.py +0 -0
  41. {thuban-0.0.4 → thuban-0.0.6}/thuban/data/reduced_hip.csv +0 -0
  42. {thuban-0.0.4 → thuban-0.0.6}/thuban/error.py +0 -0
  43. {thuban-0.0.4 → thuban-0.0.6}/thuban/simulate.py +0 -0
  44. {thuban-0.0.4 → thuban-0.0.6}/thuban/util.py +0 -0
  45. {thuban-0.0.4 → thuban-0.0.6}/thuban/visualize.py +0 -0
  46. {thuban-0.0.4 → thuban-0.0.6}/thuban.egg-info/dependency_links.txt +0 -0
  47. {thuban-0.0.4 → thuban-0.0.6}/thuban.egg-info/entry_points.txt +0 -0
  48. {thuban-0.0.4 → thuban-0.0.6}/thuban.egg-info/top_level.txt +0 -0
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Desktop (please complete the following information):**
27
+ - OS: [e.g. iOS]
28
+ - Browser [e.g. chrome, safari]
29
+ - Version [e.g. 22]
30
+
31
+ **Smartphone (please complete the following information):**
32
+ - Device: [e.g. iPhone6]
33
+ - OS: [e.g. iOS8.1]
34
+ - Browser [e.g. stock browser, safari]
35
+ - Version [e.g. 22]
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -36,7 +36,7 @@ jobs:
36
36
  pip install ".[test]"
37
37
  - name: Test with pytest
38
38
  run: |
39
- coverage run -m pytest
39
+ coverage run --branch --source=thuban/ -m pytest tests -m "not slow"
40
40
  - name: Upload coverage to Codecov
41
41
  uses: codecov/codecov-action@v4
42
42
  env:
@@ -3,19 +3,19 @@ repos:
3
3
  # This should be before any formatting hooks like isort
4
4
  - repo: https://github.com/astral-sh/ruff-pre-commit
5
5
  # Ruff version.
6
- rev: v0.6.1
6
+ rev: v0.11.4
7
7
  hooks:
8
8
  # Run the linter.
9
9
  - id: ruff
10
10
  types_or: [ python, pyi ]
11
11
  args: [ --fix ]
12
12
  - repo: https://github.com/PyCQA/isort
13
- rev: 5.13.0
13
+ rev: 6.0.1
14
14
  hooks:
15
15
  - id: isort
16
16
  exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*|extern.*|sunpy/extern)$"
17
17
  - repo: https://github.com/pre-commit/pre-commit-hooks
18
- rev: v4.5.0
18
+ rev: v5.0.0
19
19
  hooks:
20
20
  - id: check-ast
21
21
  - id: check-case-conflict
@@ -29,3 +29,6 @@ repos:
29
29
  exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*|.json)$|^CITATION.rst$"
30
30
  - id: mixed-line-ending
31
31
  exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*)$"
32
+ ci:
33
+ autofix_prs: false
34
+ autoupdate_schedule: "quarterly"
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ [Also available through GitHub interface](https://github.com/punch-mission/thuban/releases)
4
+
5
+ ## Version 0.0.6: May 22, 2025
6
+
7
+ * specify codecov path by @jmbhughes in https://github.com/punch-mission/thuban/pull/18
8
+ * Update issue templates by @jmbhughes in https://github.com/punch-mission/thuban/pull/19
9
+ * Support more PUNCH features in https://github.com/punch-mission/thuban/pull/25
10
+
11
+ ## Version 0.0.5: Nov 13, 2024
12
+
13
+ - Fix version names in docs by @jmbhughes in #12
14
+ - add tolerances by @jmbhughes in #13
15
+ - [pre-commit.ci] pre-commit autoupdate by @pre-commit-ci in #14
16
+
17
+ ## Version 0.0.4: Nov 1, 2024
18
+
19
+ - Add pinned environment ci by @jmbhughes in #10
20
+ - Create .readthedocs.yaml by @jmbhughes in #11
21
+
22
+ ## Version 0.0.3: Aug 24, 2024
23
+
24
+ - incorporates masking and pv by @jmbhughes in #8
25
+
26
+ ## Version 0.0.2: Aug 18, 2024
27
+
28
+ - add file manifest by @jmbhughes in #7
29
+
30
+ ## Version 0.0.1: Aug 18, 2024
31
+
32
+ Initial release
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: thuban
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: High precision and accuracy astrometric image solving from a guess
5
5
  Author-email: "J. Marcus Hughes" <hughes.jmb@gmail.com>
6
6
  Maintainer-email: "J. Marcus Hughes" <hughes.jmb@gmail.com>
@@ -19,7 +19,7 @@ Requires-Dist: numpy
19
19
  Requires-Dist: astropy
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: pandas
22
- Requires-Dist: sep-pjw
22
+ Requires-Dist: sep
23
23
  Requires-Dist: scipy
24
24
  Requires-Dist: scikit-learn
25
25
  Requires-Dist: lmfit
@@ -38,13 +38,15 @@ Requires-Dist: sphinx-automodapi; extra == "docs"
38
38
  Requires-Dist: pydata-sphinx-theme; extra == "docs"
39
39
  Requires-Dist: sphinx-favicon; extra == "docs"
40
40
  Requires-Dist: ipython; extra == "docs"
41
+ Requires-Dist: packaging; extra == "docs"
42
+ Dynamic: license-file
41
43
 
42
44
  # thuban
43
45
 
44
46
  High precision astrometric image solving from a guess.
45
47
 
46
48
  > [!WARNING]
47
- > This package is not yet ready for production usage.
49
+ > This package may still have significant changes until v1.
48
50
 
49
51
 
50
52
  ## To-do
@@ -3,7 +3,7 @@
3
3
  High precision astrometric image solving from a guess.
4
4
 
5
5
  > [!WARNING]
6
- > This package is not yet ready for production usage.
6
+ > This package may still have significant changes until v1.
7
7
 
8
8
 
9
9
  ## To-do
@@ -2,6 +2,9 @@
2
2
  #
3
3
  # For the full list of built-in configuration values, see the documentation:
4
4
  # https://www.sphinx-doc.org/en/master/usage/configuration.html
5
+ from importlib.metadata import version as get_version
6
+
7
+ from packaging.version import Version
5
8
 
6
9
  # -- Project information -----------------------------------------------------
7
10
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
@@ -9,7 +12,12 @@
9
12
  project = "thuban"
10
13
  copyright = "2024, PUNCH Science Operations Center"
11
14
  author = "PUNCH Science Operations Center"
12
- release = "0.0.3"
15
+
16
+ release: str = get_version("thuban")
17
+ version: str = release
18
+ _version = Version(release)
19
+ if _version.is_devrelease:
20
+ version = release = f"{_version.base_version}.dev{_version.dev}"
13
21
 
14
22
  # -- General configuration ---------------------------------------------------
15
23
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -10,7 +10,7 @@ dependencies = ["numpy",
10
10
  "astropy",
11
11
  "matplotlib",
12
12
  "pandas",
13
- "sep-pjw",
13
+ "sep",
14
14
  "scipy",
15
15
  "scikit-learn",
16
16
  "lmfit",
@@ -35,7 +35,7 @@ classifiers = [
35
35
 
36
36
  [project.optional-dependencies]
37
37
  test = ["pytest", "pytest-doctestplus", "pytest-cov", "ruff", "coverage"]
38
- docs = ["sphinx", "sphinx-autoapi", "sphinx-automodapi", "pydata-sphinx-theme", "sphinx-favicon", "ipython"]
38
+ docs = ["sphinx", "sphinx-autoapi", "sphinx-automodapi", "pydata-sphinx-theme", "sphinx-favicon", "ipython", "packaging"]
39
39
 
40
40
  [project.scripts]
41
41
  thuban = "thuban.cli:determine_pointing_and_distortion"
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
 
6
6
  import click
7
7
  import numpy as np
8
- import sep_pjw as sep
8
+ import sep
9
9
  from astropy.io import fits
10
10
  from astropy.wcs import WCS
11
11
  from tqdm import tqdm
@@ -38,9 +38,8 @@ def open_files(paths: [Path], byte_swap=True) -> (np.ndarray, [fits.Header]):
38
38
  for filename in tqdm(paths):
39
39
  with fits.open(filename) as hdul:
40
40
  if byte_swap:
41
- data = hdul[data_hdu_num].data.byteswap().newbyteorder().astype(float)
42
- else:
43
- data = hdul[data_hdu_num].data.astype(float)
41
+ raise NotImplementedError()
42
+ data = hdul[data_hdu_num].data.astype(float)
44
43
  head = hdul[data_hdu_num].header
45
44
  all_wcses.append(WCS(head, hdul, key=celestial_wcs_key))
46
45
  all_data.append(data)
@@ -79,8 +78,6 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
79
78
  # data[:, :edge, :] = 0.0
80
79
  # data[:, -edge:, :] = 0.0
81
80
  # data[:, :, -edge:] = 0.0
82
-
83
- print(headers)
84
81
  current_wcses = [convert_cd_matrix_to_pc_matrix(w) for w in all_wcses]
85
82
 
86
83
  # remove any SIP distortion model and make an empty table
@@ -91,7 +88,6 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
91
88
  wcs.sip = None
92
89
  wcs.cpdis1 = cpdis1
93
90
  wcs.cpdis2 = cpdis2
94
- print(current_wcses[0].wcs.cunit)
95
91
 
96
92
  counts = []
97
93
  for iteration in range(iterations):
@@ -110,13 +106,18 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
110
106
  current_wcses[file_index],
111
107
  file_index,
112
108
  detection_threshold=threshold,
113
- max_trials=5,
109
+ max_trials=10,
114
110
  dimmest_magnitude=magnitude,
115
111
  num_stars=num_stars,
116
112
  background_width=background,
117
113
  background_height=background,
118
- chisqr_threshold=0.05,
119
- method='least_squares'))
114
+ chisqr_threshold=0.1,
115
+ max_error=150,
116
+ method='least_squares',
117
+ fix_crota=False,
118
+ fix_crval=False,
119
+ fix_pv=iteration == 0 or iteration == iterations - 1,
120
+ fix_cdelt=True or iteration == iterations - 1 or iteration % 2 == 1))
120
121
 
121
122
  print("\tprocessing")
122
123
  with tqdm(total=len(futures)) as pbar:
@@ -124,21 +125,28 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
124
125
 
125
126
  updated_wcs, _, solution, num_trials, file_index = future.result()
126
127
  current_wcses[file_index] = updated_wcs
128
+ pbar.update(1)
127
129
 
130
+ global_pv = np.mean([w.wcs.get_pv()[0][-1] for w in current_wcses])
131
+ global_cdelt1 = np.mean([w.wcs.cdelt[0] for w in current_wcses])
132
+ global_cdelt2 = np.mean([w.wcs.cdelt[1] for w in current_wcses])
128
133
 
129
- bg = sep.Background(data[file_index], bw=background, bh=background)
130
- data_sub = data[file_index] - bg
131
- objects = sep.extract(data_sub, threshold, err=bg.globalrms)
132
- observed_coords = np.stack([objects["x"], objects["y"]], axis=-1)
134
+ for file_index in range(len(current_wcses)):
135
+ updated_wcs = current_wcses[file_index]
136
+ updated_wcs.wcs.cdelt = (global_cdelt1, global_cdelt2)
137
+ updated_wcs.wcs.set_pv([(2, 1, global_pv)])
133
138
 
134
- new_stars = find_catalog_in_image(
135
- filter_for_visible_stars(load_hipparcos_catalog(), dimmest_magnitude=8.0),
136
- updated_wcs, data.shape[1:], mode='wcs')
137
- all_no_distortion.append(np.stack([new_stars['x_pix'], new_stars['y_pix']], axis=-1))
139
+ bg = sep.Background(data[file_index], bw=background, bh=background)
140
+ data_sub = data[file_index] - bg
141
+ objects = sep.extract(data_sub, threshold, err=bg.globalrms)
142
+ observed_coords = np.stack([objects["x"], objects["y"]], axis=-1)
138
143
 
139
- all_found_positions.append(observed_coords)
140
- # all_no_distortion.append(no_distortion)
141
- pbar.update(1)
144
+ new_stars = find_catalog_in_image(
145
+ filter_for_visible_stars(load_hipparcos_catalog(), dimmest_magnitude=8.0),
146
+ updated_wcs, data.shape[1:], mode='wcs')
147
+ all_no_distortion.append(np.stack([new_stars['x_pix'], new_stars['y_pix']], axis=-1))
148
+
149
+ all_found_positions.append(observed_coords)
142
150
 
143
151
  # log.append((all_corrected_positions, all_found_positions))
144
152
  # TODO: move lower to fix counting of stars
@@ -149,7 +157,7 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
149
157
  for i in range(len(all_no_distortion)):
150
158
  observed_filtered, refined_filtered = remove_pairless_points(all_found_positions[i],
151
159
  all_no_distortion[i],
152
- max_distance=15)
160
+ max_distance=150)
153
161
  if (observed_filtered.ndim == 2 and refined_filtered.ndim == 2
154
162
  and len(observed_filtered) > 0 and len(refined_filtered) > 0):
155
163
  final_observations.append(observed_filtered)
@@ -164,6 +172,12 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
164
172
  final_observations,
165
173
  final_refined,
166
174
  num_bins=num_bins)
175
+ if iteration != iterations - 1:
176
+ center_x_val = cpdis1.data[num_bins//2, num_bins//2]
177
+ center_y_val = cpdis2.data[num_bins//2, num_bins//2]
178
+
179
+ cpdis1.data -= center_x_val
180
+ cpdis2.data -= center_y_val
167
181
 
168
182
  for wcs in current_wcses:
169
183
  wcs.cpdis1 = cpdis1
@@ -177,4 +191,4 @@ def determine_pointing_and_distortion(directory, byte_swap=True, num_stars=20,
177
191
  _, extension = os.path.splitext(fn)
178
192
  hdul = updated_wcs.to_fits() # improve the writing mechanism
179
193
  hdul[0].data = img
180
- hdul.writeto(str(fn).replace(extension, "_repoint.fits"), overwrite=True) # todo make postfix and option
194
+ hdul.writeto(str(fn).replace(extension, "_repoint.fits"), overwrite=True) # todo make postfix an option
@@ -131,7 +131,7 @@ def compute_distortion(
131
131
  img_shape: (int, int),
132
132
  catalog_positions: np.ndarray,
133
133
  found_positions: np.ndarray,
134
- distortion_limit: float =20, num_bins: int =75, blur_sigma: float =4, med_filter_size: int=3
134
+ distortion_limit: float =20, num_bins: int =75, blur_sigma: float = 1, med_filter_size: int = 1
135
135
  ) -> (DistortionLookupTable, DistortionLookupTable):
136
136
  """ Given the derived catalog and actual star positions, determines the distortion
137
137
 
@@ -1,11 +1,11 @@
1
1
  import warnings
2
2
  from typing import Callable
3
3
 
4
+ import astropy.units as u
4
5
  import numpy as np
5
6
  import pandas as pd
6
- import sep_pjw as sep
7
+ import sep
7
8
  from astropy.wcs import WCS, utils
8
- import astropy.units as u
9
9
  from lmfit import Parameters, minimize
10
10
 
11
11
  from thuban.catalog import (filter_for_visible_stars, find_catalog_in_image,
@@ -63,6 +63,7 @@ def _residual(params: Parameters,
63
63
  image_shape: (int, int),
64
64
  edge: int = 100,
65
65
  sigma: float = 3.0,
66
+ max_error: float = 15,
66
67
  mask: Callable = None):
67
68
  """Residual used when optimizing the pointing
68
69
 
@@ -83,18 +84,15 @@ def _residual(params: Parameters,
83
84
  np.ndarray
84
85
  residual
85
86
  """
86
- refined_wcs = WCS(naxis=2)
87
- refined_wcs.wcs.ctype = guess_wcs.wcs.ctype
88
- refined_wcs.wcs.cunit = guess_wcs.wcs.cunit
89
- refined_wcs.wcs.cdelt = guess_wcs.wcs.cdelt
90
- refined_wcs.wcs.crpix = guess_wcs.wcs.crpix
87
+ refined_wcs = guess_wcs.deepcopy()
88
+ refined_wcs.wcs.cdelt = (params['cdelt1'].value, params['cdelt2'].value)
91
89
  refined_wcs.wcs.crval = (params["crval1"].value, params["crval2"].value)
92
- refined_wcs.wcs.pc = calculate_pc_matrix(params["crota"], refined_wcs.wcs.cdelt)
90
+ refined_wcs.wcs.pc = calculate_pc_matrix(params["crota"], (params['cdelt1'], params['cdelt2']))
93
91
  refined_wcs.cpdis1 = guess_wcs.cpdis1
94
92
  refined_wcs.cpdis2 = guess_wcs.cpdis2
95
- refined_wcs.wcs.set_pv(guess_wcs.wcs.get_pv())
93
+ refined_wcs.wcs.set_pv([(2, 1, params['pv'].value)])
96
94
 
97
- reduced_catalog = find_catalog_in_image(catalog, refined_wcs, image_shape=image_shape, mask=mask)
95
+ reduced_catalog = find_catalog_in_image(catalog, refined_wcs, image_shape=image_shape, mask=mask, mode='wcs')
98
96
  refined_coords = np.stack([reduced_catalog['x_pix'], reduced_catalog['y_pix']], axis=-1)
99
97
 
100
98
  image_bounds = (image_shape[0] - edge, image_shape[1] - edge)
@@ -111,20 +109,26 @@ def _residual(params: Parameters,
111
109
  out = np.array([np.min(np.linalg.norm(observed_coords - coord, axis=-1)) for coord in refined_coords[:n]])
112
110
  median, stdev = np.median(out), np.std(out)
113
111
  out[out > median + (sigma * stdev)] = 0.0 # TODO: should this be zero?
112
+ out[out > max_error] = 0.0 # TODO: should this be zero?
114
113
  return out
115
114
 
116
115
 
117
116
  def refine_pointing_wrapper(image, guess_wcs, file_num, observed_coords=None, catalog=None,
118
117
  background_width=16, background_height=16,
119
118
  detection_threshold=5, num_stars=30, max_trials=15, chisqr_threshold=0.1,
120
- dimmest_magnitude=6.0, method='leastsq'):
119
+ dimmest_magnitude=6.0, method='least_squares', ra_tolerance=10, dec_tolerance=5, max_error=15,
120
+ fix_crval=False, fix_cdelt=True, fix_crota=False, fix_pv=True):
121
121
  new_wcs, observed_coords, solution, trial_num = refine_pointing(image,
122
122
  guess_wcs,
123
123
  observed_coords=observed_coords, catalog=catalog,
124
124
  background_width=background_width, background_height=background_height,
125
125
  detection_threshold=detection_threshold, num_stars=num_stars, max_trials=max_trials,
126
126
  chisqr_threshold=chisqr_threshold,
127
- dimmest_magnitude=dimmest_magnitude, method=method)
127
+ dimmest_magnitude=dimmest_magnitude, method=method,
128
+ ra_tolerance=ra_tolerance,
129
+ dec_tolerance=dec_tolerance, max_error=max_error,
130
+ fix_crval=fix_crval, fix_cdelt=fix_cdelt,
131
+ fix_crota=fix_crota, fix_pv=fix_pv)
128
132
  return new_wcs, observed_coords, solution, trial_num, file_num
129
133
 
130
134
 
@@ -137,29 +141,9 @@ def extract_crota_from_wcs(wcs: WCS) -> tuple[float, float]:
137
141
  def refine_pointing(image, guess_wcs, observed_coords=None, catalog=None,
138
142
  background_width=16, background_height=16,
139
143
  detection_threshold=5, num_stars=30, max_trials=15, chisqr_threshold=0.1,
140
- dimmest_magnitude=6.0, method='leastsq', edge=100, sigma=3.0, mask=None):
141
- """ Refine the pointing for an image
142
-
143
- Parameters
144
- ----------
145
- image : np.ndarray
146
- the brightnesses of the image, no preprocessing necessary
147
- guess_wcs : WCS
148
- initial guess for th world coordinate system, must overlap with the true WCS
149
- file_index
150
- observed_coords
151
- catalog
152
- background_width
153
- background_height
154
- detection_threshold
155
- x_lim
156
- y_lim
157
- n
158
-
159
- Returns
160
- -------
161
-
162
- """
144
+ dimmest_magnitude=6.0, method='least_squares', edge=100, sigma=3.0, mask=None,
145
+ ra_tolerance=10, dec_tolerance=5, max_error=15,
146
+ fix_crval=False, fix_cdelt=True, fix_crota=False, fix_pv=True):
163
147
  if catalog is None:
164
148
  catalog = filter_for_visible_stars(load_hipparcos_catalog(), dimmest_magnitude=dimmest_magnitude)
165
149
 
@@ -172,21 +156,54 @@ def refine_pointing(image, guess_wcs, observed_coords=None, catalog=None,
172
156
  observed_coords = np.stack([objects["x"], objects["y"]], axis=-1)
173
157
  if mask is not None:
174
158
  observed_coords = observed_coords[mask(objects['x'], objects['y'])]
159
+
160
+ image_shape = image.shape
161
+ reduced_catalog = find_catalog_in_image(catalog, guess_wcs, image_shape=image_shape, mask=mask)
162
+ refined_coords = np.stack([reduced_catalog['x_pix'], reduced_catalog['y_pix']], axis=-1)
163
+ # print("catalog found", len(refined_coords), refined_coords)
164
+
165
+ image_bounds = (image_shape[0] - edge, image_shape[1] - edge)
166
+ refined_coords = np.array([c for c in refined_coords
167
+ if (c[0] > edge) and (c[1] > edge) and (c[0] < image_bounds[0]) and (
168
+ c[1] < image_bounds[1])])
169
+
170
+ distances = np.array([np.min(np.linalg.norm(refined_coords - coord, axis=-1)) for coord in observed_coords])
171
+ observed_coords = observed_coords[distances < max_error]
175
172
  observed_coords = observed_coords[-3*num_stars:]
176
173
  # set up the optimization
177
174
  params = Parameters()
178
175
  initial_crota = extract_crota_from_wcs(guess_wcs)
179
- params.add("crota", value=initial_crota.to(u.rad).value, min=-np.pi, max=np.pi)
180
- params.add("crval1", value=guess_wcs.wcs.crval[0], min=-180, max=180, vary=True)
181
- params.add("crval2", value=guess_wcs.wcs.crval[1], min=-90, max=90, vary=True)
182
-
176
+ params.add("crota", value=initial_crota.to(u.rad).value,
177
+ min=-np.pi, max=np.pi, vary=not fix_crota)
178
+ params.add("crval1", value=guess_wcs.wcs.crval[0],
179
+ min=guess_wcs.wcs.crval[0]-ra_tolerance,
180
+ max=guess_wcs.wcs.crval[0]+ra_tolerance, vary=not fix_crval)
181
+ params.add("crval2", value=guess_wcs.wcs.crval[1],
182
+ min=guess_wcs.wcs.crval[1]-dec_tolerance,
183
+ max=guess_wcs.wcs.crval[1]+dec_tolerance, vary=not fix_crval)
184
+ params.add("cdelt1", value=guess_wcs.wcs.cdelt[0],
185
+ min=guess_wcs.wcs.cdelt[0]-1,
186
+ max=guess_wcs.wcs.cdelt[0]+1, vary=not fix_cdelt)
187
+ params.add("cdelt2", value=guess_wcs.wcs.cdelt[1],
188
+ min=max(guess_wcs.wcs.cdelt[1]-0.1, 0),
189
+ max=guess_wcs.wcs.cdelt[1]+0.1, vary=not fix_cdelt)
190
+ # if guess_wcs.wcs.get_pv():
191
+ # pv = guess_wcs.wcs.get_pv()[0][-1]
192
+ # print(pv)
193
+ # else:
194
+ # pv = 0.0
195
+ # params.add("pv", value=pv, min=0.0, max=1.0, vary=not fix_pv)
196
+
197
+ # return guess_wcs, observed_coords, None, None
183
198
  # optimize
184
199
  trial_num = 0
185
200
  result_wcses, result_minimizations = [], []
186
201
  while trial_num < max_trials:
187
202
  try:
188
203
  out = minimize(_residual, params, method=method,
189
- args=(observed_coords, catalog, guess_wcs, num_stars, image.shape, edge, sigma, mask))
204
+ args=(observed_coords, catalog, guess_wcs,
205
+ num_stars, image.shape, edge, sigma, max_error, mask),
206
+ max_nfev=500, calc_covar=False)
190
207
  chisqr = out.chisqr
191
208
  result_minimizations.append(out)
192
209
  except IndexError:
@@ -196,16 +213,13 @@ def refine_pointing(image, guess_wcs, observed_coords=None, catalog=None,
196
213
  ConvergenceWarning)
197
214
  else:
198
215
  # construct the result
199
- result_wcs = WCS(naxis=2)
200
- result_wcs.wcs.ctype = guess_wcs.wcs.ctype
201
- result_wcs.wcs.cunit = guess_wcs.wcs.cunit
202
- result_wcs.wcs.cdelt = guess_wcs.wcs.cdelt
203
- result_wcs.wcs.crpix = guess_wcs.wcs.crpix
216
+ result_wcs = guess_wcs.deepcopy()
217
+ result_wcs.wcs.cdelt = (out.params["cdelt1"].value, out.params["cdelt2"].value)
204
218
  result_wcs.wcs.crval = (out.params["crval1"].value, out.params["crval2"].value)
205
219
  result_wcs.wcs.pc = calculate_pc_matrix(out.params["crota"], result_wcs.wcs.cdelt)
206
220
  result_wcs.cpdis1 = guess_wcs.cpdis1 # TODO: what if there is no known distortion
207
221
  result_wcs.cpdis2 = guess_wcs.cpdis2
208
- result_wcs.wcs.set_pv(guess_wcs.wcs.get_pv())
222
+ result_wcs.wcs.set_pv([(2, 1, out.params['pv'].value)])
209
223
  result_wcses.append(result_wcs)
210
224
  trial_num += 1
211
225
  if chisqr < chisqr_threshold:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: thuban
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: High precision and accuracy astrometric image solving from a guess
5
5
  Author-email: "J. Marcus Hughes" <hughes.jmb@gmail.com>
6
6
  Maintainer-email: "J. Marcus Hughes" <hughes.jmb@gmail.com>
@@ -19,7 +19,7 @@ Requires-Dist: numpy
19
19
  Requires-Dist: astropy
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: pandas
22
- Requires-Dist: sep-pjw
22
+ Requires-Dist: sep
23
23
  Requires-Dist: scipy
24
24
  Requires-Dist: scikit-learn
25
25
  Requires-Dist: lmfit
@@ -38,13 +38,15 @@ Requires-Dist: sphinx-automodapi; extra == "docs"
38
38
  Requires-Dist: pydata-sphinx-theme; extra == "docs"
39
39
  Requires-Dist: sphinx-favicon; extra == "docs"
40
40
  Requires-Dist: ipython; extra == "docs"
41
+ Requires-Dist: packaging; extra == "docs"
42
+ Dynamic: license-file
41
43
 
42
44
  # thuban
43
45
 
44
46
  High precision astrometric image solving from a guess.
45
47
 
46
48
  > [!WARNING]
47
- > This package is not yet ready for production usage.
49
+ > This package may still have significant changes until v1.
48
50
 
49
51
 
50
52
  ## To-do
@@ -1,15 +1,17 @@
1
1
  .gitignore
2
2
  .pre-commit-config.yaml
3
3
  .readthedocs.yaml
4
+ CHANGELOG.md
4
5
  CITATION.cff
5
6
  LICENSE
6
7
  MANIFEST.in
7
8
  README.md
8
9
  codecov.yaml
9
10
  pyproject.toml
11
+ .github/ISSUE_TEMPLATE/bug_report.md
12
+ .github/ISSUE_TEMPLATE/feature_request.md
10
13
  .github/workflows/ci.yaml
11
14
  .github/workflows/ci_fixed.yaml
12
- .github/workflows/docs.yaml
13
15
  .github/workflows/publish.yaml
14
16
  docs/Makefile
15
17
  docs/conf.py
@@ -2,7 +2,7 @@ numpy
2
2
  astropy
3
3
  matplotlib
4
4
  pandas
5
- sep-pjw
5
+ sep
6
6
  scipy
7
7
  scikit-learn
8
8
  lmfit
@@ -16,6 +16,7 @@ sphinx-automodapi
16
16
  pydata-sphinx-theme
17
17
  sphinx-favicon
18
18
  ipython
19
+ packaging
19
20
 
20
21
  [test]
21
22
  pytest
@@ -1,36 +0,0 @@
1
- name: deploy-docs
2
-
3
- # Only run this when the master branch changes
4
- on:
5
- push:
6
- branches:
7
- - main
8
-
9
- # This job installs dependencies, builds the book, and pushes it to `gh-pages`
10
- jobs:
11
- deploy-docs:
12
- runs-on: ubuntu-latest
13
- steps:
14
- - uses: actions/checkout@v2
15
-
16
- # Install dependencies
17
- - name: Set up Python 3.10
18
- uses: actions/setup-python@v2
19
- with:
20
- python-version: "3.10"
21
-
22
- - name: Install dependencies
23
- run: |
24
- pip install ".[docs]"
25
-
26
- # Build the book
27
- - name: Sphinx build
28
- run: |
29
- cd ./docs; make html; cd ..
30
-
31
- # Push the book's HTML to github-pages
32
- - name: GitHub Pages action
33
- uses: peaceiris/actions-gh-pages@v3.6.1
34
- with:
35
- github_token: ${{ secrets.GITHUB_TOKEN }}
36
- publish_dir: ./docs/_build/html
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes