fitscube 2.3.1__tar.gz → 2.3.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.
- {fitscube-2.3.1 → fitscube-2.3.2}/PKG-INFO +1 -1
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/_version.py +2 -2
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/combine_fits.py +38 -3
- {fitscube-2.3.1 → fitscube-2.3.2}/pyproject.toml +1 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/test_frequencies.py +14 -1
- {fitscube-2.3.1 → fitscube-2.3.2}/.github/CONTRIBUTING.md +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.github/dependabot.yml +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.github/release.yml +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.github/workflows/cd.yml +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.github/workflows/ci.yml +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.gitignore +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/.pre-commit-config.yaml +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/CHANGELOG.md +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/LICENSE +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/README.md +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/__init__.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/asyncio.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/bounding_box.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/cli.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/exceptions.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/extract.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/logging.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/fitscube/version.pyi +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/noxfile.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/__init__.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/conftest.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/data/cube.zip +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/data/images.zip +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/data/time_images.zip +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/data/timecube.zip +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/test_bb.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/test_extract.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/test_package.py +0 -0
- {fitscube-2.3.1 → fitscube-2.3.2}/tests/test_times.py +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 3,
|
|
21
|
+
__version__ = version = '2.3.2'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 3, 2)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -132,6 +132,41 @@ def isin_close(
|
|
|
132
132
|
return np.isclose(element[:, None], test_element, atol, rtol).any(1)
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
def grid_step(diffs: ArrayLike) -> float:
|
|
136
|
+
"""Recover the fundamental channel step from the observed diffs.
|
|
137
|
+
|
|
138
|
+
On a regular grid with missing channels every diff is an integer multiple
|
|
139
|
+
of the channel width, so the gcd of the diffs recovers that width even when
|
|
140
|
+
no two surviving channels are adjacent -- the case where ``min(diffs)``
|
|
141
|
+
overestimates the step (e.g. present channels 0, 3, 5 give ``min == 2`` but
|
|
142
|
+
the true step is 1). Falls back to ``min(diffs)`` if the gcd is numerically
|
|
143
|
+
unstable or does not divide every diff, so the common case is unchanged (any
|
|
144
|
+
surviving adjacent pair makes the gcd equal the minimum diff exactly).
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
diffs (ArrayLike): Differences between consecutive spec values.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
float: Estimated regular grid step.
|
|
151
|
+
"""
|
|
152
|
+
diffs = np.abs(np.asarray(diffs, dtype=np.longdouble))
|
|
153
|
+
min_diff = np.min(diffs)
|
|
154
|
+
tol = np.max(diffs) * 1e-6
|
|
155
|
+
|
|
156
|
+
g = diffs[0]
|
|
157
|
+
for d in diffs[1:]:
|
|
158
|
+
a, b = (g, d) if g >= d else (d, g)
|
|
159
|
+
# Tolerant Euclid: stop once the remainder is within the noise floor.
|
|
160
|
+
while b > tol:
|
|
161
|
+
a, b = b, a % b
|
|
162
|
+
g = a
|
|
163
|
+
|
|
164
|
+
divides_all = np.all(np.abs(np.round(diffs / g) - diffs / g) <= 1e-3)
|
|
165
|
+
if g <= tol or not divides_all:
|
|
166
|
+
return float(min_diff)
|
|
167
|
+
return float(g)
|
|
168
|
+
|
|
169
|
+
|
|
135
170
|
def even_spacing(specs: u.Quantity, time_domain_mode: bool = False) -> SpequencyInfo:
|
|
136
171
|
"""Make the frequencies or times evenly spaced.
|
|
137
172
|
|
|
@@ -143,9 +178,9 @@ def even_spacing(specs: u.Quantity, time_domain_mode: bool = False) -> Spequency
|
|
|
143
178
|
"""
|
|
144
179
|
specs_arr = specs.value.astype(np.longdouble)
|
|
145
180
|
diffs = np.diff(specs_arr)
|
|
146
|
-
|
|
147
|
-
# Create a new array with the
|
|
148
|
-
new_specs = np_arange_fix(specs_arr[0], specs_arr[-1],
|
|
181
|
+
step = grid_step(diffs)
|
|
182
|
+
# Create a new array with the fundamental grid step
|
|
183
|
+
new_specs = np_arange_fix(specs_arr[0], specs_arr[-1], step)
|
|
149
184
|
missing_chan_idx = np.logical_not(
|
|
150
185
|
isin_close(new_specs, specs_arr, time_domain_mode)
|
|
151
186
|
)
|
|
@@ -6,7 +6,7 @@ import astropy.units as u
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pytest
|
|
8
8
|
from astropy.io import fits
|
|
9
|
-
from fitscube.combine_fits import combine_fits, parse_specs
|
|
9
|
+
from fitscube.combine_fits import combine_fits, even_spacing, parse_specs
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@pytest.fixture
|
|
@@ -108,3 +108,16 @@ def test_uneven_combine(
|
|
|
108
108
|
assert chan in (1, 2)
|
|
109
109
|
continue
|
|
110
110
|
assert np.allclose(plane, image)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_even_spacing_non_adjacent_gaps():
|
|
114
|
+
# Present channels 0, 3, 5 GHz on a 1 GHz grid (1, 2, 4 missing). No two
|
|
115
|
+
# surviving channels are adjacent, so min(diffs) == 2 GHz would mis-grid the
|
|
116
|
+
# axis to [0, 2, 4] and drop the real channels. The gcd of the diffs
|
|
117
|
+
# [3, 2] GHz recovers the true 1 GHz step.
|
|
118
|
+
specs = np.array([0.0, 3.0, 5.0]) * u.GHz
|
|
119
|
+
new_specs, missing_chan_idx = even_spacing(specs)
|
|
120
|
+
assert len(new_specs) == 6, "should rebuild the full 0..5 GHz grid"
|
|
121
|
+
assert missing_chan_idx.sum() == 3
|
|
122
|
+
expected_missing = np.array([False, True, True, False, True, False])
|
|
123
|
+
assert np.array_equal(missing_chan_idx, expected_missing)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|