ruststartracker 0.2.2__tar.gz → 0.2.3__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.
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/PKG-INFO +30 -6
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/README.md +29 -5
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/build_script.py +6 -1
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/pyproject.toml +2 -2
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/libruststartracker.pyi +38 -7
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/star.py +30 -5
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/test_backend.py +17 -6
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/test_catalog.py +14 -0
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/test_integration.py +2 -0
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/test_star.py +47 -12
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/LICENSE +0 -0
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/__init__.py +0 -0
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/catalog.py +0 -0
- {ruststartracker-0.2.2 → ruststartracker-0.2.3}/ruststartracker/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ruststartracker
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Lightweight Python Star Tracker With Rust Backend
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Nicolas Tobler
|
|
@@ -34,6 +34,35 @@ Features:
|
|
|
34
34
|
|
|
35
35
|
## Example
|
|
36
36
|
|
|
37
|
+
### Rust
|
|
38
|
+
|
|
39
|
+
See [examples/basic.rs](examples/basic.rs)
|
|
40
|
+
|
|
41
|
+
```rust
|
|
42
|
+
// Get catalog positions
|
|
43
|
+
let catalog: StarCatalog = StarCatalog::from_gaia(max_magnitude: ...).unwrap();
|
|
44
|
+
let stars_xyz: Vec<[f32; 3]> = catalog.normalized_positions(epoch: ..., observer_position: ...);
|
|
45
|
+
let stars_mag: Vec<f32> = catalog.magnitudes();
|
|
46
|
+
|
|
47
|
+
// Create StarTracker instance (reuse this)
|
|
48
|
+
let star_matcher = StarMatcher::new(
|
|
49
|
+
stars_xyz,
|
|
50
|
+
stars_mag,
|
|
51
|
+
max_lookup_magnitude: ...
|
|
52
|
+
max_inter_star_angle: ...,
|
|
53
|
+
inter_star_angle_tolerance: ...,
|
|
54
|
+
min_matches: ...,
|
|
55
|
+
timeout: ...
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Normalized observation in the camera frame
|
|
59
|
+
let obs_xyz_camera: Vec<[f32; 3]> = ...
|
|
60
|
+
|
|
61
|
+
let result = star_matcher.find(&obs_xyz_camera);
|
|
62
|
+
println!("Result: {:?}", result);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Python
|
|
37
66
|
```python
|
|
38
67
|
import ruststartracker
|
|
39
68
|
|
|
@@ -72,11 +101,6 @@ print(result)
|
|
|
72
101
|
|
|
73
102
|
- Install with `pip install ruststartracker` (Currently only ARM/x86 Linux wheels available).
|
|
74
103
|
|
|
75
|
-
## TODOs
|
|
76
|
-
|
|
77
|
-
- Improve error messages.
|
|
78
|
-
- Return more diagnostic data.
|
|
79
|
-
|
|
80
104
|
## Attributions
|
|
81
105
|
|
|
82
106
|
### Gaia Data
|
|
@@ -13,6 +13,35 @@ Features:
|
|
|
13
13
|
|
|
14
14
|
## Example
|
|
15
15
|
|
|
16
|
+
### Rust
|
|
17
|
+
|
|
18
|
+
See [examples/basic.rs](examples/basic.rs)
|
|
19
|
+
|
|
20
|
+
```rust
|
|
21
|
+
// Get catalog positions
|
|
22
|
+
let catalog: StarCatalog = StarCatalog::from_gaia(max_magnitude: ...).unwrap();
|
|
23
|
+
let stars_xyz: Vec<[f32; 3]> = catalog.normalized_positions(epoch: ..., observer_position: ...);
|
|
24
|
+
let stars_mag: Vec<f32> = catalog.magnitudes();
|
|
25
|
+
|
|
26
|
+
// Create StarTracker instance (reuse this)
|
|
27
|
+
let star_matcher = StarMatcher::new(
|
|
28
|
+
stars_xyz,
|
|
29
|
+
stars_mag,
|
|
30
|
+
max_lookup_magnitude: ...
|
|
31
|
+
max_inter_star_angle: ...,
|
|
32
|
+
inter_star_angle_tolerance: ...,
|
|
33
|
+
min_matches: ...,
|
|
34
|
+
timeout: ...
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Normalized observation in the camera frame
|
|
38
|
+
let obs_xyz_camera: Vec<[f32; 3]> = ...
|
|
39
|
+
|
|
40
|
+
let result = star_matcher.find(&obs_xyz_camera);
|
|
41
|
+
println!("Result: {:?}", result);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Python
|
|
16
45
|
```python
|
|
17
46
|
import ruststartracker
|
|
18
47
|
|
|
@@ -51,11 +80,6 @@ print(result)
|
|
|
51
80
|
|
|
52
81
|
- Install with `pip install ruststartracker` (Currently only ARM/x86 Linux wheels available).
|
|
53
82
|
|
|
54
|
-
## TODOs
|
|
55
|
-
|
|
56
|
-
- Improve error messages.
|
|
57
|
-
- Return more diagnostic data.
|
|
58
|
-
|
|
59
83
|
## Attributions
|
|
60
84
|
|
|
61
85
|
### Gaia Data
|
|
@@ -50,7 +50,12 @@ def build_script() -> None:
|
|
|
50
50
|
if not gaia_file.exists():
|
|
51
51
|
download_gaia_data(gaia_file)
|
|
52
52
|
|
|
53
|
-
subprocess.check_call(
|
|
53
|
+
subprocess.check_call( # noqa: S603
|
|
54
|
+
["cargo", "build", "--release", "--features", "improc,gaia"], # noqa: S607
|
|
55
|
+
cwd=cwd,
|
|
56
|
+
stdout=None,
|
|
57
|
+
stderr=None,
|
|
58
|
+
)
|
|
54
59
|
shutil.copy(
|
|
55
60
|
cwd / "target/release/libruststartracker.so", cwd / "ruststartracker/libruststartracker.so"
|
|
56
61
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "ruststartracker"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "Lightweight Python Star Tracker With Rust Backend"
|
|
5
5
|
authors = ["Nicolas Tobler <nitobler@gmail.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -42,7 +42,7 @@ requires = ["poetry-core", "astroquery>=0.4.10"]
|
|
|
42
42
|
build-backend = "poetry.core.masonry.api"
|
|
43
43
|
|
|
44
44
|
[tool.bumpversion]
|
|
45
|
-
current_version = "0.2.
|
|
45
|
+
current_version = "0.2.3"
|
|
46
46
|
commit = true
|
|
47
47
|
tag = true
|
|
48
48
|
tag_name = "v{new_version}"
|
|
@@ -2,12 +2,15 @@ from collections.abc import Iterator
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import numpy.typing as npt
|
|
5
|
+
from typing_extensions import Self
|
|
5
6
|
|
|
6
7
|
class StarMatcher:
|
|
7
8
|
def __init__(
|
|
8
9
|
self,
|
|
9
10
|
stars_xyz: npt.NDArray[np.float32],
|
|
11
|
+
stars_mag: npt.NDArray[np.float32],
|
|
10
12
|
max_inter_star_angle: float,
|
|
13
|
+
max_lookup_magnitude: float,
|
|
11
14
|
inter_star_angle_tolerance: float,
|
|
12
15
|
n_minimum_matches: int,
|
|
13
16
|
timeout_secs: float,
|
|
@@ -19,7 +22,7 @@ class StarMatcher:
|
|
|
19
22
|
npt.NDArray[np.uint32],
|
|
20
23
|
npt.NDArray[np.uint32],
|
|
21
24
|
int,
|
|
22
|
-
|
|
25
|
+
npt.NDArray[np.float32],
|
|
23
26
|
float,
|
|
24
27
|
]: ...
|
|
25
28
|
|
|
@@ -44,15 +47,43 @@ class IterTriangleFinder:
|
|
|
44
47
|
class UnitVectorLookup:
|
|
45
48
|
def __init__(self, vec: npt.NDArray[np.float32]) -> None: ...
|
|
46
49
|
def lookup_nearest(self, key: npt.NDArray[np.float32]) -> int: ...
|
|
47
|
-
def get_inter_star_index_numpy(
|
|
48
|
-
self, vec: npt.NDArray[np.float32], angle_threshold: float
|
|
49
|
-
) -> tuple[list[list[int]], list[float], list[float]]: ...
|
|
50
50
|
def get_inter_star_index(
|
|
51
|
-
self,
|
|
51
|
+
self,
|
|
52
|
+
stars: npt.NDArray[np.float32],
|
|
53
|
+
magnitudes: npt.NDArray[np.float32],
|
|
54
|
+
max_angle_rad: float,
|
|
55
|
+
max_magnitude: float,
|
|
52
56
|
) -> tuple[list[list[int]], list[float], list[float]]: ...
|
|
53
57
|
def look_up_close_angles(
|
|
54
|
-
self,
|
|
58
|
+
self,
|
|
59
|
+
vectors: npt.NDArray[np.float32],
|
|
60
|
+
magnitudes: npt.NDArray[np.float32],
|
|
61
|
+
max_angle_rad: float,
|
|
62
|
+
max_magnitude: float,
|
|
55
63
|
) -> list[tuple[list[float], float]]: ...
|
|
56
64
|
def look_up_close_angles_naive(
|
|
57
|
-
self,
|
|
65
|
+
self,
|
|
66
|
+
vectors: npt.NDArray[np.float32],
|
|
67
|
+
magnitudes: npt.NDArray[np.float32],
|
|
68
|
+
max_angle_rad: float,
|
|
69
|
+
max_magnitude: float,
|
|
58
70
|
) -> list[tuple[list[float], float]]: ...
|
|
71
|
+
|
|
72
|
+
def get_threshold_from_histogram(
|
|
73
|
+
img: npt.NDArray[np.uint8],
|
|
74
|
+
*,
|
|
75
|
+
fraction: float,
|
|
76
|
+
) -> int: ...
|
|
77
|
+
def extract_observations(
|
|
78
|
+
img: npt.NDArray[np.uint8],
|
|
79
|
+
threshold: int,
|
|
80
|
+
min_star_area: int,
|
|
81
|
+
max_star_area: int,
|
|
82
|
+
) -> tuple[npt.NDArray[np.float32], npt.NDArray[np.float32]]: ...
|
|
83
|
+
|
|
84
|
+
class StarCatalog:
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_gaia(cls, *, max_magnitude: float | None) -> Self: ...
|
|
87
|
+
def normalized_positions(
|
|
88
|
+
self, *, epoch: float | None, observer_position: np.ndarray | None
|
|
89
|
+
) -> npt.NDArray[np.float32]: ...
|
|
@@ -87,8 +87,10 @@ class StarTracker:
|
|
|
87
87
|
def __init__(
|
|
88
88
|
self,
|
|
89
89
|
stars_xyz: npt.NDArray[np.float32],
|
|
90
|
+
stars_mag: npt.NDArray[np.float32],
|
|
90
91
|
camera_params: CameraParameters,
|
|
91
92
|
*,
|
|
93
|
+
max_lookup_magnitude: float | None = None,
|
|
92
94
|
max_inter_star_angle: float | None = None,
|
|
93
95
|
inter_star_angle_tolerance: float = 0.0008,
|
|
94
96
|
n_minimum_matches: int = 10,
|
|
@@ -98,11 +100,15 @@ class StarTracker:
|
|
|
98
100
|
|
|
99
101
|
Args:
|
|
100
102
|
stars_xyz: Positions of catalog stars
|
|
103
|
+
stars_mag: Magnitudes of catalog stars
|
|
101
104
|
camera_params: Calibrated camera parameters
|
|
105
|
+
max_lookup_magnitude: Maximum magnitude of stars used in the triangulation. Reducing
|
|
106
|
+
this number means only bright stars are used for triangulation. This results in
|
|
107
|
+
faster lookup performance.
|
|
102
108
|
max_inter_star_angle: Maximum angle between stars that should be indexed.
|
|
103
109
|
Calculating large inter star angles is expensive. If None, the angle is
|
|
104
110
|
calculated from the camera field of view.
|
|
105
|
-
inter_star_angle_tolerance: Tolerance for inter star angle matching.
|
|
111
|
+
inter_star_angle_tolerance: Tolerance for inter star angle matching in rad.
|
|
106
112
|
n_minimum_matches: Minimum amount of required matches for a successful
|
|
107
113
|
attitude estimation
|
|
108
114
|
timeout_secs: Maximum allowed search time in seconds. A StarTrackerError is raised
|
|
@@ -127,8 +133,13 @@ class StarTracker:
|
|
|
127
133
|
dot_products = (corner_coords_xyz * np.array([0, 0, 1], dtype=np.float32)).sum(axis=-1)
|
|
128
134
|
max_inter_star_angle = float(np.arccos(dot_products.max())) * 2
|
|
129
135
|
|
|
136
|
+
if max_lookup_magnitude is None:
|
|
137
|
+
max_lookup_magnitude = 100.0 # A very faint star. Almost infinity
|
|
138
|
+
|
|
130
139
|
self._star_matcher = ruststartracker.libruststartracker.StarMatcher(
|
|
131
140
|
np.ascontiguousarray(stars_xyz, dtype=np.float32),
|
|
141
|
+
np.ascontiguousarray(stars_mag, dtype=np.float32),
|
|
142
|
+
float(max_lookup_magnitude),
|
|
132
143
|
float(max_inter_star_angle),
|
|
133
144
|
float(inter_star_angle_tolerance),
|
|
134
145
|
int(n_minimum_matches),
|
|
@@ -178,6 +189,20 @@ class StarTracker:
|
|
|
178
189
|
max_star_area=max_star_area,
|
|
179
190
|
)
|
|
180
191
|
|
|
192
|
+
if threshold is None:
|
|
193
|
+
threshold = ruststartracker.libruststartracker.get_threshold_from_histogram(
|
|
194
|
+
img, fraction=0.99
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
centroids, intensities = ruststartracker.libruststartracker.extract_observations(
|
|
198
|
+
img,
|
|
199
|
+
threshold,
|
|
200
|
+
min_star_area,
|
|
201
|
+
max_star_area,
|
|
202
|
+
)
|
|
203
|
+
centroids = np.array(centroids, dtype=np.float32)
|
|
204
|
+
intensities = np.array(intensities, dtype=np.float32)
|
|
205
|
+
|
|
181
206
|
# At least 3 observations are required (one triangle)
|
|
182
207
|
if len(centroids) < 3:
|
|
183
208
|
raise StarTrackerError("Found too few star candidates (< 3) to continue.")
|
|
@@ -238,12 +263,12 @@ class StarTracker:
|
|
|
238
263
|
quat, match_ids, obs_indices, n_matches, matched_obs, duration_s = result
|
|
239
264
|
|
|
240
265
|
return StarTrackerResult(
|
|
241
|
-
quat=
|
|
242
|
-
match_ids=
|
|
266
|
+
quat=quat,
|
|
267
|
+
match_ids=match_ids,
|
|
243
268
|
n_matches=n_matches,
|
|
244
269
|
duration_s=duration_s,
|
|
245
|
-
mached_obs_x=
|
|
246
|
-
obs_indices=
|
|
270
|
+
mached_obs_x=matched_obs,
|
|
271
|
+
obs_indices=obs_indices,
|
|
247
272
|
)
|
|
248
273
|
|
|
249
274
|
|
|
@@ -54,9 +54,12 @@ def test_unit_vector_lookup():
|
|
|
54
54
|
close_indices_gt = np.concatenate(results, axis=-1).T[args]
|
|
55
55
|
angles_gt = angles[args]
|
|
56
56
|
|
|
57
|
-
close_indices, angles, poly = uvl.
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
close_indices, angles, poly = uvl.get_inter_star_index(
|
|
58
|
+
np.array(vec[:, :3], dtype=np.float32),
|
|
59
|
+
np.ones(len(vec), dtype=np.float32),
|
|
60
|
+
angle_threshold,
|
|
61
|
+
10,
|
|
62
|
+
)
|
|
60
63
|
close_indices = np.array(close_indices)
|
|
61
64
|
angles = np.array(angles)
|
|
62
65
|
poly = np.array(poly)
|
|
@@ -101,6 +104,8 @@ def test_star_matcher():
|
|
|
101
104
|
vec = rng.normal(size=[n_cat_stars, 3]).astype(np.float32)
|
|
102
105
|
vec /= np.linalg.norm(vec, axis=-1, keepdims=True)
|
|
103
106
|
|
|
107
|
+
magnitudes = rng.uniform(0, 10, size=vec.shape[:1]).astype(np.float32)
|
|
108
|
+
|
|
104
109
|
key = rng.normal(size=[3]).astype(np.float32)
|
|
105
110
|
key /= np.linalg.norm(key, axis=-1, keepdims=True)
|
|
106
111
|
|
|
@@ -113,10 +118,16 @@ def test_star_matcher():
|
|
|
113
118
|
|
|
114
119
|
rot = scipy.spatial.transform.Rotation.from_rotvec([1, 1, 1])
|
|
115
120
|
|
|
116
|
-
obs_rotated = rot.apply(obs)
|
|
121
|
+
obs_rotated = rot.apply(obs).astype(np.float32)
|
|
117
122
|
|
|
118
123
|
index = libruststartracker.StarMatcher(
|
|
119
|
-
vec,
|
|
124
|
+
vec,
|
|
125
|
+
magnitudes,
|
|
126
|
+
10,
|
|
127
|
+
np.radians(10).item(),
|
|
128
|
+
np.radians(0.1).item(),
|
|
129
|
+
4,
|
|
130
|
+
999.0,
|
|
120
131
|
)
|
|
121
132
|
|
|
122
133
|
res = index.find(obs_rotated)
|
|
@@ -124,7 +135,7 @@ def test_star_matcher():
|
|
|
124
135
|
assert res is not None
|
|
125
136
|
|
|
126
137
|
quat, match_ids, obs_indices, n_matches, matched_obs, time_s = res
|
|
127
|
-
np.testing.assert_allclose(quat, rot.inv().as_quat())
|
|
138
|
+
np.testing.assert_allclose(quat, rot.inv().as_quat(), rtol=1e-6)
|
|
128
139
|
assert n_matches >= 4
|
|
129
140
|
assert len(obs_index) == len(match_ids)
|
|
130
141
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import time
|
|
2
3
|
|
|
3
4
|
import astropy.time # type: ignore[import]
|
|
4
5
|
import numpy as np
|
|
5
6
|
import pytest
|
|
6
7
|
|
|
7
8
|
import ruststartracker.catalog
|
|
9
|
+
import ruststartracker.libruststartracker
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def test_time_to_epoch():
|
|
@@ -43,5 +45,17 @@ def test_extract_observations():
|
|
|
43
45
|
np.testing.assert_allclose(np.linalg.norm(positions, axis=-1), 1.0, rtol=1e-5)
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
def test_python_rust():
|
|
49
|
+
t0 = time.monotonic()
|
|
50
|
+
positions = ruststartracker.catalog.StarCatalog().normalized_positions(epoch=2025.0)
|
|
51
|
+
print(f"Python catalog took {time.monotonic() - t0:.3f} seconds")
|
|
52
|
+
t0 = time.monotonic()
|
|
53
|
+
positions2 = ruststartracker.libruststartracker.StarCatalog.from_gaia(
|
|
54
|
+
max_magnitude=6.0
|
|
55
|
+
).normalized_positions(epoch=2025.0, observer_position=None)
|
|
56
|
+
print(f"Rust catalog took {time.monotonic() - t0:.3f} seconds")
|
|
57
|
+
np.testing.assert_allclose(positions, positions2, rtol=1e-5, atol=1e-5)
|
|
58
|
+
|
|
59
|
+
|
|
46
60
|
if __name__ == "__main__":
|
|
47
61
|
pytest.main([__file__])
|
|
@@ -26,6 +26,7 @@ def prepare() -> tuple[ruststartracker.StarTracker, np.ndarray]:
|
|
|
26
26
|
|
|
27
27
|
catalog = ruststartracker.StarCatalog()
|
|
28
28
|
star_catalog_vecs = catalog.normalized_positions(epoch=2024)
|
|
29
|
+
star_catalog_magnitudes = catalog.magnitude
|
|
29
30
|
|
|
30
31
|
camera_params = ruststartracker.CameraParameters(
|
|
31
32
|
camera_matrix=camera_matrix,
|
|
@@ -35,6 +36,7 @@ def prepare() -> tuple[ruststartracker.StarTracker, np.ndarray]:
|
|
|
35
36
|
|
|
36
37
|
st = ruststartracker.StarTracker(
|
|
37
38
|
star_catalog_vecs,
|
|
39
|
+
star_catalog_magnitudes,
|
|
38
40
|
camera_params,
|
|
39
41
|
inter_star_angle_tolerance=np.radians(0.05).item(),
|
|
40
42
|
n_minimum_matches=5,
|
|
@@ -5,16 +5,31 @@ import pytest
|
|
|
5
5
|
import scipy.spatial
|
|
6
6
|
|
|
7
7
|
import ruststartracker
|
|
8
|
+
import ruststartracker.libruststartracker
|
|
8
9
|
import ruststartracker.star
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
@pytest.mark.parametrize("impl", ["python", "rust"])
|
|
13
|
+
def test_extract_observations(impl: str):
|
|
14
|
+
size_x, size_y = (960, 480)
|
|
13
15
|
img = np.zeros((size_y, size_x), np.uint8)
|
|
14
|
-
points = np.array([(3, 5), (23, 13)])
|
|
16
|
+
points = np.array([(3, 5), (23, 13), (30, 50), (230, 130)])
|
|
15
17
|
for x, y in points:
|
|
16
18
|
img[y - 1 : y + 3, x - 1 : x + 3] = 50
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
t0 = time.monotonic()
|
|
21
|
+
if impl == "python":
|
|
22
|
+
centers, intensities = ruststartracker.star._extract_observations(img, threshold=30)
|
|
23
|
+
elif impl == "rust":
|
|
24
|
+
centers, intensities = ruststartracker.libruststartracker.extract_observations(
|
|
25
|
+
img, 30, 3, 300
|
|
26
|
+
)
|
|
27
|
+
else:
|
|
28
|
+
raise AssertionError
|
|
29
|
+
print(f"Extracting observations took {time.monotonic() - t0:.5f} seconds")
|
|
30
|
+
|
|
31
|
+
assert isinstance(centers, np.ndarray)
|
|
32
|
+
assert isinstance(intensities, np.ndarray)
|
|
18
33
|
np.testing.assert_almost_equal(centers, points + 0.5)
|
|
19
34
|
np.testing.assert_almost_equal(intensities, 50 * 16)
|
|
20
35
|
|
|
@@ -28,6 +43,8 @@ def setup():
|
|
|
28
43
|
vec = rng.normal(size=[n_cat_stars, 3]).astype(np.float32)
|
|
29
44
|
vec /= np.linalg.norm(vec, axis=-1, keepdims=True)
|
|
30
45
|
|
|
46
|
+
mag = rng.uniform(0, 10, size=vec.shape[:1]).astype(np.float32)
|
|
47
|
+
|
|
31
48
|
angle_threshold = np.radians(10)
|
|
32
49
|
dotp = np.sum([0, 0, 1] * vec, axis=-1)
|
|
33
50
|
threshold = np.cos(angle_threshold).item()
|
|
@@ -55,17 +72,18 @@ def setup():
|
|
|
55
72
|
image_patch = img[y - 1 : y + 2, x - 1 : x + 2]
|
|
56
73
|
image_patch[:] = 50
|
|
57
74
|
|
|
58
|
-
return img, vec, camera_params
|
|
75
|
+
return img, vec, mag, pixel_in_frame, camera_params
|
|
59
76
|
|
|
60
77
|
|
|
61
78
|
def test_star_matcher_success(setup):
|
|
62
|
-
img, vec, camera_params = setup
|
|
79
|
+
img, vec, mag, _, camera_params = setup
|
|
63
80
|
|
|
64
81
|
rot = scipy.spatial.transform.Rotation.from_rotvec([1, 1, 1])
|
|
65
82
|
vec = rot.inv().apply(vec)
|
|
66
83
|
|
|
67
84
|
st = ruststartracker.StarTracker(
|
|
68
85
|
vec,
|
|
86
|
+
mag,
|
|
69
87
|
camera_params,
|
|
70
88
|
inter_star_angle_tolerance=np.radians(0.1).item(),
|
|
71
89
|
n_minimum_matches=6,
|
|
@@ -78,34 +96,51 @@ def test_star_matcher_success(setup):
|
|
|
78
96
|
|
|
79
97
|
|
|
80
98
|
def test_star_matcher_exhaust(setup):
|
|
81
|
-
img, vec, camera_params = setup
|
|
99
|
+
img, vec, mag, _, camera_params = setup
|
|
82
100
|
st = ruststartracker.StarTracker(
|
|
83
101
|
vec,
|
|
102
|
+
mag,
|
|
84
103
|
camera_params,
|
|
85
104
|
inter_star_angle_tolerance=np.radians(0.001).item(),
|
|
86
105
|
n_minimum_matches=500,
|
|
87
106
|
timeout_secs=999.0,
|
|
88
107
|
)
|
|
89
|
-
with pytest.raises(ruststartracker.StarTrackerError, match="
|
|
108
|
+
with pytest.raises(ruststartracker.StarTrackerError, match="SearchExhausted"):
|
|
90
109
|
st.process_image(img)
|
|
91
110
|
|
|
92
111
|
|
|
93
112
|
def test_star_matcher_timout(setup):
|
|
94
|
-
img, vec, camera_params = setup
|
|
95
|
-
timeout = 0.
|
|
113
|
+
img, vec, mag, _, camera_params = setup
|
|
114
|
+
timeout = 0.0002
|
|
96
115
|
st = ruststartracker.StarTracker(
|
|
97
116
|
vec,
|
|
117
|
+
mag,
|
|
98
118
|
camera_params,
|
|
99
119
|
inter_star_angle_tolerance=np.radians(0.1).item(),
|
|
100
120
|
n_minimum_matches=500,
|
|
101
121
|
timeout_secs=timeout,
|
|
102
122
|
)
|
|
103
123
|
t = time.monotonic()
|
|
104
|
-
with pytest.raises(ruststartracker.StarTrackerError, match="Timeout
|
|
124
|
+
with pytest.raises(ruststartracker.StarTrackerError, match="Timeout"):
|
|
105
125
|
st.process_image(img)
|
|
106
126
|
passed_time = time.monotonic() - t
|
|
107
127
|
assert passed_time > timeout
|
|
108
128
|
|
|
109
129
|
|
|
130
|
+
def test_star_matcher_not_enough_stars(setup):
|
|
131
|
+
_, vec, mag, pixel_in_frame, camera_params = setup
|
|
132
|
+
timeout = 0.2
|
|
133
|
+
st = ruststartracker.StarTracker(
|
|
134
|
+
vec,
|
|
135
|
+
mag,
|
|
136
|
+
camera_params,
|
|
137
|
+
inter_star_angle_tolerance=np.radians(0.1).item(),
|
|
138
|
+
n_minimum_matches=500,
|
|
139
|
+
timeout_secs=timeout,
|
|
140
|
+
)
|
|
141
|
+
with pytest.raises(ruststartracker.StarTrackerError, match="NotEnoughStars"):
|
|
142
|
+
st.process_image_coordiantes(pixel_in_frame[:2])
|
|
143
|
+
|
|
144
|
+
|
|
110
145
|
if __name__ == "__main__":
|
|
111
|
-
pytest.main([__file__])
|
|
146
|
+
pytest.main([__file__, "--capture=no"])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|