timetag-explorer 1.0.1__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.
- timetag_explorer-1.0.1/LICENSE +29 -0
- timetag_explorer-1.0.1/PKG-INFO +129 -0
- timetag_explorer-1.0.1/README.md +95 -0
- timetag_explorer-1.0.1/pyproject.toml +48 -0
- timetag_explorer-1.0.1/setup.cfg +4 -0
- timetag_explorer-1.0.1/tests/test_dataset.py +55 -0
- timetag_explorer-1.0.1/tests/test_frames.py +28 -0
- timetag_explorer-1.0.1/tests/test_gdcorr.py +26 -0
- timetag_explorer-1.0.1/tests/test_package.py +43 -0
- timetag_explorer-1.0.1/tests/test_planet.py +71 -0
- timetag_explorer-1.0.1/tests/test_regions.py +38 -0
- timetag_explorer-1.0.1/tests/test_selection_script.py +36 -0
- timetag_explorer-1.0.1/timetag_explorer/__init__.py +21 -0
- timetag_explorer-1.0.1/timetag_explorer/analysis.py +93 -0
- timetag_explorer-1.0.1/timetag_explorer/config.py +25 -0
- timetag_explorer-1.0.1/timetag_explorer/constants.py +57 -0
- timetag_explorer-1.0.1/timetag_explorer/dataset.py +459 -0
- timetag_explorer-1.0.1/timetag_explorer/frames.py +282 -0
- timetag_explorer-1.0.1/timetag_explorer/gdcorr.py +41 -0
- timetag_explorer-1.0.1/timetag_explorer/horizons.py +166 -0
- timetag_explorer-1.0.1/timetag_explorer/log.py +20 -0
- timetag_explorer-1.0.1/timetag_explorer/planet.py +246 -0
- timetag_explorer-1.0.1/timetag_explorer/regions.py +67 -0
- timetag_explorer-1.0.1/timetag_explorer/selection_script.py +494 -0
- timetag_explorer-1.0.1/timetag_explorer/transforms.py +578 -0
- timetag_explorer-1.0.1/timetag_explorer.egg-info/PKG-INFO +129 -0
- timetag_explorer-1.0.1/timetag_explorer.egg-info/SOURCES.txt +28 -0
- timetag_explorer-1.0.1/timetag_explorer.egg-info/dependency_links.txt +1 -0
- timetag_explorer-1.0.1/timetag_explorer.egg-info/requires.txt +8 -0
- timetag_explorer-1.0.1/timetag_explorer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Sean Lockwood
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timetag-explorer
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: The science code behind TIME-TAG Explorer: HST/STIS FUV TIME-TAG processing, reference frames, planet maps and selection scripts
|
|
5
|
+
Author: Sean Lockwood
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://explorer.planetarylightshow.com
|
|
8
|
+
Project-URL: Documentation, https://timetag-explorer.readthedocs.io
|
|
9
|
+
Project-URL: Source, https://github.com/sean-lockwood/timetag-explorer
|
|
10
|
+
Project-URL: Issues, https://github.com/sean-lockwood/timetag-explorer/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/sean-lockwood/timetag-explorer/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: astronomy,HST,STIS,TIME-TAG,FUV,aurora
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: numpy>=2.0
|
|
27
|
+
Requires-Dist: astropy>=7.0
|
|
28
|
+
Requires-Dist: matplotlib>=3.9
|
|
29
|
+
Requires-Dist: astroquery>=0.4.10
|
|
30
|
+
Provides-Extra: docs
|
|
31
|
+
Requires-Dist: sphinx>=7.2; extra == "docs"
|
|
32
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# timetag-explorer
|
|
36
|
+
|
|
37
|
+
[](https://github.com/sean-lockwood/timetag-explorer/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/timetag-explorer/)
|
|
39
|
+
[](https://timetag-explorer.readthedocs.io)
|
|
40
|
+
|
|
41
|
+
The science code behind [TIME-TAG Explorer](https://explorer.planetarylightshow.com), a web app for exploring
|
|
42
|
+
HST/STIS FUV TIME-TAG observations (aurorae on Jupiter and Saturn, and other targets that move or vary).
|
|
43
|
+
|
|
44
|
+
This repository holds **only the calculations**: reading a TIME-TAG file, the processing steps, the transformations
|
|
45
|
+
between reference frames and planet maps, the exposure-time maps, and the generator of the standalone "selection
|
|
46
|
+
scripts". The web server around it (sign-in, pages, plots and mouse selection) is a separate, private project, and
|
|
47
|
+
this package knows nothing about it. Everything the app computes about the data comes from here, so this is the part
|
|
48
|
+
that scientific review should look at.
|
|
49
|
+
|
|
50
|
+
> **Status: first release, not yet validated.** [VALIDATION.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/VALIDATION.md) lists each step with its reference and
|
|
51
|
+
> its review status. The geometric distortion correction is **disabled** in this release: `gdcorr.py` holds
|
|
52
|
+
> placeholder (zero) coefficients until values from a public reference file replace them.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
Python 3.11 or newer.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
pip install timetag-explorer
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
To work on the code, clone the repository and `pip install -e .` in it. Documentation:
|
|
63
|
+
<https://timetag-explorer.readthedocs.io>.
|
|
64
|
+
|
|
65
|
+
## A first look
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from timetag_explorer import load_dataset, Corrections, Box, describe_selection
|
|
69
|
+
|
|
70
|
+
ds = load_dataset('od8k57itq_tag.fits') # a TIME-TAG file from MAST; its _spt.fits file, if next to it, is used too
|
|
71
|
+
corr = Corrections(randcorr=True, flatcorr=False, timecorr=True) # which processing steps are on
|
|
72
|
+
|
|
73
|
+
edges, rate = ds.light_curve(3.0) # count rate in 3 s bins
|
|
74
|
+
image = ds.image(time_sel=(100.0, 250.0), frame='image', corr=corr) # counts/s per 4x4 hi-res-pixel bin
|
|
75
|
+
region = Box(900, 1100, 900, 1100) # detector pixels
|
|
76
|
+
edges, region_rate = ds.light_curve(3.0, region=region, corr=corr)
|
|
77
|
+
print(describe_selection(ds, corr, 'image', (100.0, 250.0), region))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Rest frames and planet maps (`frame='rest'`, `'planet'`, `'map_rect'`, ...) need the exposure's SPT file next to the
|
|
81
|
+
TIME-TAG file; the planet ones also ask JPL Horizons for positions (through `astroquery`) and keep the answers in
|
|
82
|
+
`config.HORIZONS_DIR`. The flat field is optional: put `combined_flat.fits`
|
|
83
|
+
([download](https://www.planetarylightshow.com/static/data/combined_flat.fits);
|
|
84
|
+
[how it was made](https://www.planetarylightshow.com/aurora_processing.html#Flat-Fielding)) where `config.FLAT_FILE` points,
|
|
85
|
+
or set the environment variables `TAG_EXPLORER_FLAT` and `TAG_EXPLORER_HORIZONS`.
|
|
86
|
+
|
|
87
|
+
`build_selection_script(ds, time_sel, region, frame, corr)` returns a standalone Python script that reproduces a
|
|
88
|
+
selection on the original TIME-TAG file and writes flag columns into a copy of it.
|
|
89
|
+
|
|
90
|
+
## What is where
|
|
91
|
+
|
|
92
|
+
| Module | Contents |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `regions` | `Box`, `Circle`, `Polygon`: which detector pixels a region holds |
|
|
95
|
+
| `frames` | the processing switches (`Corrections`), reference frames, header parsing (pointing, slew, WCS) |
|
|
96
|
+
| `horizons` | planet and moon positions from JPL Horizons, and the local cache of them |
|
|
97
|
+
| `planet` | Jupiter and Saturn orientation (IAU rotation model, light time) and the sky / surface / map projections |
|
|
98
|
+
| `transforms` | detector pixels to frames and maps, exposure-time maps, the distortion lookup table |
|
|
99
|
+
| `dataset` | `TagDataset`: events, slicing in time, binning, light curves, count-rate images |
|
|
100
|
+
| `analysis` | areas, mean brightness and the exposure for a selection |
|
|
101
|
+
| `selection_script` | the generator of selection scripts (and its version, `SELVER`) |
|
|
102
|
+
| `gdcorr` | the geometric distortion polynomial (placeholder coefficients for now) |
|
|
103
|
+
| `constants`, `config`, `log` | shared numbers; file locations; logging |
|
|
104
|
+
|
|
105
|
+
## Tests and validation
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pip install -e .
|
|
109
|
+
for t in tests/test_*.py; do python "$t"; done # quick: synthetic data, no network
|
|
110
|
+
python validation/check_selection_scripts.py OFAP44G5Q # slow: runs generated scripts on a real exposure (downloads it)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The first set runs on every pull request. The second runs every week on GitHub Actions. See
|
|
114
|
+
[VALIDATION.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/VALIDATION.md) for what each step is checked against.
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Questions and concerns about the science are welcome as [issues](https://github.com/sean-lockwood/timetag-explorer/issues); proposed changes as pull requests.
|
|
119
|
+
Please read [CONTRIBUTING.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/CONTRIBUTING.md) first. Requests for access to the web app go to the address on the
|
|
120
|
+
[app's front page](https://explorer.planetarylightshow.com/about), not here.
|
|
121
|
+
|
|
122
|
+
## Citing
|
|
123
|
+
|
|
124
|
+
Use the "Cite this repository" button on GitHub, or [CITATION.cff](https://github.com/sean-lockwood/timetag-explorer/blob/main/CITATION.cff). A DOI will be added once the first
|
|
125
|
+
release is archived.
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
BSD 3-Clause. See [LICENSE](https://github.com/sean-lockwood/timetag-explorer/blob/main/LICENSE).
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# timetag-explorer
|
|
2
|
+
|
|
3
|
+
[](https://github.com/sean-lockwood/timetag-explorer/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/timetag-explorer/)
|
|
5
|
+
[](https://timetag-explorer.readthedocs.io)
|
|
6
|
+
|
|
7
|
+
The science code behind [TIME-TAG Explorer](https://explorer.planetarylightshow.com), a web app for exploring
|
|
8
|
+
HST/STIS FUV TIME-TAG observations (aurorae on Jupiter and Saturn, and other targets that move or vary).
|
|
9
|
+
|
|
10
|
+
This repository holds **only the calculations**: reading a TIME-TAG file, the processing steps, the transformations
|
|
11
|
+
between reference frames and planet maps, the exposure-time maps, and the generator of the standalone "selection
|
|
12
|
+
scripts". The web server around it (sign-in, pages, plots and mouse selection) is a separate, private project, and
|
|
13
|
+
this package knows nothing about it. Everything the app computes about the data comes from here, so this is the part
|
|
14
|
+
that scientific review should look at.
|
|
15
|
+
|
|
16
|
+
> **Status: first release, not yet validated.** [VALIDATION.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/VALIDATION.md) lists each step with its reference and
|
|
17
|
+
> its review status. The geometric distortion correction is **disabled** in this release: `gdcorr.py` holds
|
|
18
|
+
> placeholder (zero) coefficients until values from a public reference file replace them.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
Python 3.11 or newer.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install timetag-explorer
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
To work on the code, clone the repository and `pip install -e .` in it. Documentation:
|
|
29
|
+
<https://timetag-explorer.readthedocs.io>.
|
|
30
|
+
|
|
31
|
+
## A first look
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from timetag_explorer import load_dataset, Corrections, Box, describe_selection
|
|
35
|
+
|
|
36
|
+
ds = load_dataset('od8k57itq_tag.fits') # a TIME-TAG file from MAST; its _spt.fits file, if next to it, is used too
|
|
37
|
+
corr = Corrections(randcorr=True, flatcorr=False, timecorr=True) # which processing steps are on
|
|
38
|
+
|
|
39
|
+
edges, rate = ds.light_curve(3.0) # count rate in 3 s bins
|
|
40
|
+
image = ds.image(time_sel=(100.0, 250.0), frame='image', corr=corr) # counts/s per 4x4 hi-res-pixel bin
|
|
41
|
+
region = Box(900, 1100, 900, 1100) # detector pixels
|
|
42
|
+
edges, region_rate = ds.light_curve(3.0, region=region, corr=corr)
|
|
43
|
+
print(describe_selection(ds, corr, 'image', (100.0, 250.0), region))
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Rest frames and planet maps (`frame='rest'`, `'planet'`, `'map_rect'`, ...) need the exposure's SPT file next to the
|
|
47
|
+
TIME-TAG file; the planet ones also ask JPL Horizons for positions (through `astroquery`) and keep the answers in
|
|
48
|
+
`config.HORIZONS_DIR`. The flat field is optional: put `combined_flat.fits`
|
|
49
|
+
([download](https://www.planetarylightshow.com/static/data/combined_flat.fits);
|
|
50
|
+
[how it was made](https://www.planetarylightshow.com/aurora_processing.html#Flat-Fielding)) where `config.FLAT_FILE` points,
|
|
51
|
+
or set the environment variables `TAG_EXPLORER_FLAT` and `TAG_EXPLORER_HORIZONS`.
|
|
52
|
+
|
|
53
|
+
`build_selection_script(ds, time_sel, region, frame, corr)` returns a standalone Python script that reproduces a
|
|
54
|
+
selection on the original TIME-TAG file and writes flag columns into a copy of it.
|
|
55
|
+
|
|
56
|
+
## What is where
|
|
57
|
+
|
|
58
|
+
| Module | Contents |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `regions` | `Box`, `Circle`, `Polygon`: which detector pixels a region holds |
|
|
61
|
+
| `frames` | the processing switches (`Corrections`), reference frames, header parsing (pointing, slew, WCS) |
|
|
62
|
+
| `horizons` | planet and moon positions from JPL Horizons, and the local cache of them |
|
|
63
|
+
| `planet` | Jupiter and Saturn orientation (IAU rotation model, light time) and the sky / surface / map projections |
|
|
64
|
+
| `transforms` | detector pixels to frames and maps, exposure-time maps, the distortion lookup table |
|
|
65
|
+
| `dataset` | `TagDataset`: events, slicing in time, binning, light curves, count-rate images |
|
|
66
|
+
| `analysis` | areas, mean brightness and the exposure for a selection |
|
|
67
|
+
| `selection_script` | the generator of selection scripts (and its version, `SELVER`) |
|
|
68
|
+
| `gdcorr` | the geometric distortion polynomial (placeholder coefficients for now) |
|
|
69
|
+
| `constants`, `config`, `log` | shared numbers; file locations; logging |
|
|
70
|
+
|
|
71
|
+
## Tests and validation
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
pip install -e .
|
|
75
|
+
for t in tests/test_*.py; do python "$t"; done # quick: synthetic data, no network
|
|
76
|
+
python validation/check_selection_scripts.py OFAP44G5Q # slow: runs generated scripts on a real exposure (downloads it)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The first set runs on every pull request. The second runs every week on GitHub Actions. See
|
|
80
|
+
[VALIDATION.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/VALIDATION.md) for what each step is checked against.
|
|
81
|
+
|
|
82
|
+
## Contributing
|
|
83
|
+
|
|
84
|
+
Questions and concerns about the science are welcome as [issues](https://github.com/sean-lockwood/timetag-explorer/issues); proposed changes as pull requests.
|
|
85
|
+
Please read [CONTRIBUTING.md](https://github.com/sean-lockwood/timetag-explorer/blob/main/CONTRIBUTING.md) first. Requests for access to the web app go to the address on the
|
|
86
|
+
[app's front page](https://explorer.planetarylightshow.com/about), not here.
|
|
87
|
+
|
|
88
|
+
## Citing
|
|
89
|
+
|
|
90
|
+
Use the "Cite this repository" button on GitHub, or [CITATION.cff](https://github.com/sean-lockwood/timetag-explorer/blob/main/CITATION.cff). A DOI will be added once the first
|
|
91
|
+
release is archived.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
BSD 3-Clause. See [LICENSE](https://github.com/sean-lockwood/timetag-explorer/blob/main/LICENSE).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "timetag-explorer"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "The science code behind TIME-TAG Explorer: HST/STIS FUV TIME-TAG processing, reference frames, planet maps and selection scripts"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Sean Lockwood" }]
|
|
14
|
+
keywords = ["astronomy", "HST", "STIS", "TIME-TAG", "FUV", "aurora"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"numpy>=2.0",
|
|
29
|
+
"astropy>=7.0",
|
|
30
|
+
"matplotlib>=3.9", # only for its point-in-polygon test
|
|
31
|
+
"astroquery>=0.4.10", # JPL Horizons, for the planet frames and maps
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
docs = ["sphinx>=7.2", "sphinx-rtd-theme>=2.0"]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://explorer.planetarylightshow.com"
|
|
39
|
+
Documentation = "https://timetag-explorer.readthedocs.io"
|
|
40
|
+
Source = "https://github.com/sean-lockwood/timetag-explorer"
|
|
41
|
+
Issues = "https://github.com/sean-lockwood/timetag-explorer/issues"
|
|
42
|
+
Changelog = "https://github.com/sean-lockwood/timetag-explorer/blob/main/CHANGELOG.md"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools.dynamic]
|
|
45
|
+
version = { attr = "timetag_explorer.__version__" }
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
include = ["timetag_explorer*"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""TagDataset on small synthetic exposures: counts, rates, exposure, regions and the processing steps."""
|
|
2
|
+
import support # noqa: F401
|
|
3
|
+
import numpy as np
|
|
4
|
+
from support import OFF, dataset
|
|
5
|
+
|
|
6
|
+
from timetag_explorer.constants import EDGE_TRIM_PIXELS, SPATIAL_BIN
|
|
7
|
+
from timetag_explorer.frames import Corrections
|
|
8
|
+
from timetag_explorer.regions import Box
|
|
9
|
+
|
|
10
|
+
# 1000 events, 10 per second for 100 s, all in one pixel
|
|
11
|
+
n = 1000
|
|
12
|
+
t = np.linspace(0, 99.9, n)
|
|
13
|
+
ds = dataset(t, np.full(n, 100), np.full(n, 100))
|
|
14
|
+
assert ds.exposure() == 100.0 and abs(ds.tmax - 99.9) < 1e-3
|
|
15
|
+
img = ds.image(None, 'image', OFF)
|
|
16
|
+
assert abs(img.sum() - 10.0) < 1e-9, img.sum() # counts per second
|
|
17
|
+
img10 = ds.image((0.0, 10.0), 'image', OFF)
|
|
18
|
+
assert abs(img10.sum() - 10.0) < 1e-9 # a time window gives the rate in that window
|
|
19
|
+
edges, rate = ds.light_curve(10.0, None, 'image', OFF)
|
|
20
|
+
assert np.allclose(rate, 10.0) and len(rate) == 10
|
|
21
|
+
|
|
22
|
+
# a gap in the good-time intervals: its bins have no exposure (NaN) and the others are still right
|
|
23
|
+
gap = dataset(t[(t < 40) | (t >= 60)], np.full(800, 100), np.full(800, 100), gti=((0, 40), (60, 100)))
|
|
24
|
+
assert gap.exposure() == 80.0 and abs(gap.exposure(30, 70) - 20.0) < 1e-9
|
|
25
|
+
_, r = gap.light_curve(10.0, None, 'image', OFF)
|
|
26
|
+
assert np.isnan(r[4]) and np.isnan(r[5]) and np.allclose(r[[0, 1, 2, 3, 6, 7, 8, 9]], 10.0)
|
|
27
|
+
|
|
28
|
+
# a region: counts inside it only
|
|
29
|
+
x = np.where(np.arange(n) % 2 == 0, 100, 300) # every other event in each of two places
|
|
30
|
+
two = dataset(t, x, np.full(n, 100))
|
|
31
|
+
inside = two.light_curve(10.0, Box(90, 110, 90, 110), 'image', OFF)[1]
|
|
32
|
+
assert np.allclose(inside, 5.0)
|
|
33
|
+
assert abs(two.image(None, 'image', OFF).sum() - 10.0) < 1e-9
|
|
34
|
+
mask = two.region_mask(Box(90, 110, 90, 110), 'image', OFF)
|
|
35
|
+
assert mask.sum() == 500 and mask.dtype == bool
|
|
36
|
+
|
|
37
|
+
# flat-field weights multiply the counts
|
|
38
|
+
weighted = dataset(t, np.full(n, 100), np.full(n, 100), weight=np.full(n, 2.0))
|
|
39
|
+
assert abs(weighted.image(None, 'image', Corrections(randcorr=False, geocorr=False, flatcorr=True, timecorr=False)).sum() - 20.0) < 1e-6
|
|
40
|
+
assert abs(weighted.image(None, 'image', OFF).sum() - 10.0) < 1e-9 # switched off: unweighted
|
|
41
|
+
|
|
42
|
+
# the vignetted border is discarded only with the exposure-time correction
|
|
43
|
+
edge = dataset(t, np.full(n, EDGE_TRIM_PIXELS - 20), np.full(n, 100))
|
|
44
|
+
assert abs(edge.image(None, 'image', OFF).sum() - 10.0) < 1e-9
|
|
45
|
+
assert edge.image(None, 'image', Corrections(randcorr=False, geocorr=False, flatcorr=False, timecorr=True)).sum() == 0.0
|
|
46
|
+
|
|
47
|
+
# the random sub-pixel offset moves events by less than a pixel, keeps the total, and repeats exactly
|
|
48
|
+
rand = Corrections(randcorr=True, geocorr=False, flatcorr=False, timecorr=False)
|
|
49
|
+
a = dataset(t, np.full(n, 100), np.full(n, 100)).image(None, 'image', rand)
|
|
50
|
+
b = dataset(t, np.full(n, 100), np.full(n, 100)).image(None, 'image', rand)
|
|
51
|
+
assert np.array_equal(a, b) and abs(a.sum() - 10.0) < 1e-9 and a.shape[0] * SPATIAL_BIN <= 2048
|
|
52
|
+
|
|
53
|
+
# a window outside the data
|
|
54
|
+
assert ds.image((200.0, 300.0), 'image', OFF).sum() == 0.0
|
|
55
|
+
print('dataset ok')
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Header parsing and the small rules around frames and light-curve bins."""
|
|
2
|
+
import support # noqa: F401
|
|
3
|
+
|
|
4
|
+
from timetag_explorer.constants import DEFAULT_TIME_BIN, MAX_LIGHT_CURVE_BINS, MIN_TIME_BIN
|
|
5
|
+
from timetag_explorer.frames import (FRAMES, FrameInfo, clamp_binsize, is_map, is_north, is_planet, names_from_levels,
|
|
6
|
+
parse_mt_levels)
|
|
7
|
+
|
|
8
|
+
# Motion-tracking levels: parts of one level are joined in order
|
|
9
|
+
header = {'MT_LV1_1': 'TYPE=STD,NAME=JUPI', 'MT_LV1_2': 'TER', 'MT_LV2_1': 'TYPE=POS_ANGLE,ANG=44.0,R_RAD=0.5',
|
|
10
|
+
'OTHER': 'x'}
|
|
11
|
+
levels = parse_mt_levels(header)
|
|
12
|
+
assert levels[0] == {'TYPE': 'STD', 'NAME': 'JUPITER'}
|
|
13
|
+
assert levels[1] == {'TYPE': 'POS_ANGLE', 'ANG': '44.0', 'R_RAD': '0.5'}
|
|
14
|
+
assert parse_mt_levels({}) == []
|
|
15
|
+
assert isinstance(names_from_levels(levels), tuple)
|
|
16
|
+
|
|
17
|
+
# Light-curve bins are always usable, whatever was typed
|
|
18
|
+
assert clamp_binsize(3) == 3.0
|
|
19
|
+
assert clamp_binsize('nan') == DEFAULT_TIME_BIN and clamp_binsize(None) == DEFAULT_TIME_BIN
|
|
20
|
+
assert clamp_binsize(0) == MIN_TIME_BIN and clamp_binsize(-5) == MIN_TIME_BIN
|
|
21
|
+
assert clamp_binsize(1e-9, tmax=1000.0) == 1000.0 / MAX_LIGHT_CURVE_BINS # not billions of bins
|
|
22
|
+
|
|
23
|
+
# Frame names
|
|
24
|
+
assert is_map('map_rect') and not is_map('planet') and is_planet('planet_north') and is_north('rest_north')
|
|
25
|
+
assert 'image' in FRAMES and all(isinstance(v, str) for v in FRAMES.values())
|
|
26
|
+
info = FrameInfo(orientat=0.0, slew_angle=0.0, slew_rate=0.0)
|
|
27
|
+
assert info.unavailable_reason('image') is None and info.unavailable_reason('map_rect') is not None
|
|
28
|
+
print('frames ok')
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""The geometric-distortion polynomial, and the placeholder state of this release."""
|
|
2
|
+
import support # noqa: F401
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from timetag_explorer import gdcorr
|
|
6
|
+
from timetag_explorer.frames import DEFAULT_CORRECTIONS, geocorr_available
|
|
7
|
+
|
|
8
|
+
# The polynomial itself: each coefficient multiplies one monomial.
|
|
9
|
+
x, y = 3.0, 5.0
|
|
10
|
+
monomials = [y, x, y * y, x * y, x * x, y ** 3, y * y * x, y * x * x, x ** 3, y ** 4, y ** 3 * x, y * y * x * x,
|
|
11
|
+
y * x ** 3, x ** 4]
|
|
12
|
+
for k, expected in enumerate(monomials):
|
|
13
|
+
c = [0.0] * 14
|
|
14
|
+
c[k] = 1.0
|
|
15
|
+
assert gdcorr.polyd4(x, y, c) == expected, k
|
|
16
|
+
|
|
17
|
+
# Placeholder coefficients: nothing is corrected, and the Explorer knows it.
|
|
18
|
+
assert gdcorr.PLACEHOLDER == (not (any(gdcorr.a) or any(gdcorr.b)))
|
|
19
|
+
if gdcorr.PLACEHOLDER:
|
|
20
|
+
gx, gy = np.meshgrid(np.arange(0, 2048, 64, dtype=float), np.arange(0, 2048, 64, dtype=float))
|
|
21
|
+
xc, yc = gdcorr.gdcorr(gx, gy)
|
|
22
|
+
assert np.array_equal(xc, gx) and np.array_equal(yc, gy)
|
|
23
|
+
assert not geocorr_available() and not DEFAULT_CORRECTIONS.geocorr
|
|
24
|
+
else: # real coefficients: the switch is available and on by default
|
|
25
|
+
assert geocorr_available()
|
|
26
|
+
print('gdcorr ok')
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""The package stays free of web-server code, and its version numbers agree."""
|
|
2
|
+
import ast
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import support # noqa: F401
|
|
9
|
+
|
|
10
|
+
root = Path(__file__).resolve().parent.parent
|
|
11
|
+
FORBIDDEN = {'nicegui', 'starlette', 'fastapi', 'uvicorn', 'httpx', 'authlib', 's3fs'}
|
|
12
|
+
|
|
13
|
+
# 1. No module imports a web framework (or anything else the server alone needs).
|
|
14
|
+
for path in sorted((root / 'timetag_explorer').glob('*.py')):
|
|
15
|
+
for node in ast.walk(ast.parse(path.read_text())):
|
|
16
|
+
names = [a.name for a in node.names] if isinstance(node, ast.Import) else (
|
|
17
|
+
[node.module or ''] if isinstance(node, ast.ImportFrom) and node.level == 0 else [])
|
|
18
|
+
for name in names:
|
|
19
|
+
assert name.split('.')[0] not in FORBIDDEN, f'{path.name} imports {name}'
|
|
20
|
+
|
|
21
|
+
# 2. Importing the package does not pull in a web framework or pyplot, and nothing runs at import time.
|
|
22
|
+
out = subprocess.run([sys.executable, '-c',
|
|
23
|
+
'import sys, timetag_explorer\n'
|
|
24
|
+
'bad = [m for m in sys.modules if m.split(".")[0] in %r or m == "matplotlib.pyplot"]\n'
|
|
25
|
+
'print(",".join(bad))' % sorted(FORBIDDEN)],
|
|
26
|
+
capture_output=True, text=True, cwd=root)
|
|
27
|
+
assert out.returncode == 0, out.stderr
|
|
28
|
+
assert out.stdout.strip() == '', out.stdout
|
|
29
|
+
|
|
30
|
+
# 3. One version number, everywhere.
|
|
31
|
+
import timetag_explorer # noqa: E402
|
|
32
|
+
|
|
33
|
+
version = timetag_explorer.__version__
|
|
34
|
+
assert re.fullmatch(r'\d+\.\d+\.\d+', version), version
|
|
35
|
+
cff = (root / 'CITATION.cff').read_text()
|
|
36
|
+
assert re.search(rf'^version: "?{re.escape(version)}"?\s*$', cff, re.M), 'CITATION.cff version differs from __version__'
|
|
37
|
+
assert f'## [{version}]' in (root / 'CHANGELOG.md').read_text(), 'CHANGELOG.md has no entry for this version'
|
|
38
|
+
|
|
39
|
+
# 4. No private data in the package: no personal paths or addresses.
|
|
40
|
+
for path in sorted((root / 'timetag_explorer').glob('*.py')):
|
|
41
|
+
text = path.read_text()
|
|
42
|
+
assert '/home/' not in text and '@gmail.com' not in text, path.name
|
|
43
|
+
print('package ok')
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Planet geometry against answers that can be derived independently: round trips, areas of spheres and caps,
|
|
2
|
+
the rotation rate, and the leap seconds."""
|
|
3
|
+
import support # noqa: F401
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from timetag_explorer.planet import (AU_KM, BODIES, LIGHT_TIME_PER_AU, map_bin_area, map_plane_coordinates,
|
|
7
|
+
map_plane_to_surface, limb_outline, planet_orientation, screen_to_surface,
|
|
8
|
+
spheroid_radii, surface_to_screen, tdb_minus_utc)
|
|
9
|
+
|
|
10
|
+
jup = BODIES['JUPITER']
|
|
11
|
+
assert (jup.radius_eq_km, jup.radius_pol_km) == (71492.0, 66854.0) # IAU 2015 radii
|
|
12
|
+
assert spheroid_radii(jup, 0.0) == (1.0, 66854.0 / 71492.0)
|
|
13
|
+
|
|
14
|
+
# Leap seconds: TAI-UTC was 36 s until the end of 2016 and 37 s from 1 January 2017 (MJD 57754); TDB = TT + ~0.
|
|
15
|
+
assert tdb_minus_utc(57753) == 36 + 32.184 and tdb_minus_utc(57754) == 37 + 32.184
|
|
16
|
+
assert tdb_minus_utc(50082) == 29 + 32.184 or tdb_minus_utc(50082) == 30 + 32.184 # before the table: first entry
|
|
17
|
+
|
|
18
|
+
# The sub-observer point is at the centre of the disc, looking straight at it.
|
|
19
|
+
x, y, mu = surface_to_screen(100.0, 0.0, 0.0, 100.0, 1.0, 1.0)
|
|
20
|
+
assert abs(x) < 1e-12 and abs(y) < 1e-12 and abs(mu - 1.0) < 1e-12
|
|
21
|
+
# ... and the limb of a sphere seen from its equator is a circle of radius 1.
|
|
22
|
+
lx, ly = limb_outline(0.0, 1.0, 1.0)
|
|
23
|
+
assert np.allclose(np.hypot(lx, ly), 1.0)
|
|
24
|
+
|
|
25
|
+
# Round trip, surface -> screen -> surface, over the visible hemisphere of an oblate planet seen from 3 N.
|
|
26
|
+
a, b = spheroid_radii(jup, 400.0)
|
|
27
|
+
rng = np.random.default_rng(0)
|
|
28
|
+
lon = rng.uniform(0, 360, 2000)
|
|
29
|
+
lat = rng.uniform(-85, 85, 2000)
|
|
30
|
+
sx, sy, smu = surface_to_screen(lon, lat, 3.0, 120.0, a, b)
|
|
31
|
+
vis = smu > 0.05
|
|
32
|
+
lon2, lat2, mu2, on = screen_to_surface(sx[vis], sy[vis], 3.0, 120.0, a, b)
|
|
33
|
+
assert on.all() and np.allclose(mu2, smu[vis], atol=1e-9)
|
|
34
|
+
assert np.allclose(((lon2 - lon[vis] + 180) % 360) - 180, 0, atol=1e-7) and np.allclose(lat2, lat[vis], atol=1e-7)
|
|
35
|
+
# a point outside the disc is not on the planet
|
|
36
|
+
assert not screen_to_surface(np.array([2.0]), np.array([0.0]), 3.0, 120.0, a, b)[3][0]
|
|
37
|
+
|
|
38
|
+
# Map planes: round trips, and the area of a whole sphere and of a polar cap.
|
|
39
|
+
for frame in ('map_rect', 'map_north', 'map_south'):
|
|
40
|
+
lo = rng.uniform(0, 360, 200)
|
|
41
|
+
la = rng.uniform(45, 85, 200) * (-1 if frame == 'map_south' else 1) if frame != 'map_rect' else rng.uniform(-80, 80, 200)
|
|
42
|
+
u, v, ok = map_plane_coordinates(lo, la, frame, 180.0)
|
|
43
|
+
lo2, la2 = map_plane_to_surface(u, v, frame)
|
|
44
|
+
assert np.allclose(((lo2 - lo + 180) % 360) - 180, 0, atol=1e-7) and np.allclose(la2, la, atol=1e-7), frame
|
|
45
|
+
R = 71492.0
|
|
46
|
+
step = 0.5
|
|
47
|
+
cu = np.arange(0, 360, step) + step / 2
|
|
48
|
+
cv = np.arange(-90, 90, step) + step / 2
|
|
49
|
+
gu, gv = np.meshgrid(cu, cv)
|
|
50
|
+
area = map_bin_area(gu, gv, 'map_rect', step, R, R).sum()
|
|
51
|
+
assert abs(area / (4 * np.pi * R ** 2) - 1) < 1e-4, area
|
|
52
|
+
# north polar map out to 50 degrees of colatitude: a cap of area 2 pi R^2 (1 - cos 50 deg)
|
|
53
|
+
step = 0.25
|
|
54
|
+
c = np.arange(-50, 50, step) + step / 2
|
|
55
|
+
gu, gv = np.meshgrid(c, c)
|
|
56
|
+
inside = np.hypot(gu, gv) < 50.0
|
|
57
|
+
area = map_bin_area(gu[inside], gv[inside], 'map_north', step, R, R).sum()
|
|
58
|
+
cap = 2 * np.pi * R ** 2 * (1 - np.cos(np.radians(50.0)))
|
|
59
|
+
assert abs(area / cap - 1) < 2e-3, (area, cap)
|
|
60
|
+
|
|
61
|
+
# Orientation: Jupiter 4.5 AU away is 71492 km / 4.5 AU across, and System III turns 870.5366 deg per day.
|
|
62
|
+
n = 5
|
|
63
|
+
jd = 2460000.5 + np.arange(n) / 24.0
|
|
64
|
+
positions = np.column_stack([jd, np.full(n, 150.0), np.full(n, 15.0), np.full(n, 4.5)])
|
|
65
|
+
orient = planet_orientation(positions, jup, jd[0] - 2400000.5)
|
|
66
|
+
assert np.allclose(orient.radius_arcsec, 71492.0 / (4.5 * AU_KM) * 206264.80624709636)
|
|
67
|
+
rate = np.diff(orient.lon_obs) / np.diff(orient.t) # deg per second, west longitude
|
|
68
|
+
assert np.allclose(np.abs(rate) * 86400, 870.5366420, rtol=1e-3), rate * 86400
|
|
69
|
+
assert abs(LIGHT_TIME_PER_AU - 499.0048) < 1e-3
|
|
70
|
+
assert np.all(np.abs(orient.lat_obs_c) < 90) and np.all(np.abs(orient.lat_obs_g) < 90)
|
|
71
|
+
print('planet ok')
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Region shapes: which pixels they contain, and how they describe themselves."""
|
|
2
|
+
import support # noqa: F401
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from timetag_explorer.regions import Box, Circle, Polygon
|
|
6
|
+
|
|
7
|
+
x, y = np.meshgrid(np.arange(0, 10, dtype=float), np.arange(0, 10, dtype=float))
|
|
8
|
+
|
|
9
|
+
# Box: half-open, so adjacent boxes never count a pixel twice
|
|
10
|
+
box = Box(2, 5, 3, 6)
|
|
11
|
+
m = box.contains(x, y)
|
|
12
|
+
assert m.sum() == 9 and m[3:6, 2:5].all()
|
|
13
|
+
assert box.contains(np.array([2.0]), np.array([3.0]))[0] and not box.contains(np.array([5.0]), np.array([3.0]))[0]
|
|
14
|
+
assert Box(2, 5, 3, 6).describe() == 'box x 2-5, y 3-6'
|
|
15
|
+
|
|
16
|
+
# Circle: the boundary counts as inside
|
|
17
|
+
c = Circle(5, 5, 2)
|
|
18
|
+
assert c.contains(np.array([7.0]), np.array([5.0]))[0] and not c.contains(np.array([7.1]), np.array([5.0]))[0]
|
|
19
|
+
assert abs(c.contains(x, y).sum() - np.pi * 4) <= 3 # a pixel grid approximates pi r^2
|
|
20
|
+
assert Circle(5, 5, 2).describe().startswith('circle centre (5, 5), r = 2')
|
|
21
|
+
|
|
22
|
+
# Polygon: a square given as vertices, and a triangle
|
|
23
|
+
sq = Polygon(((1, 1), (4, 1), (4, 4), (1, 4)))
|
|
24
|
+
inside = sq.contains(np.array([2.5, 0.5, 5.0]), np.array([2.5, 2.5, 2.5]))
|
|
25
|
+
assert list(inside) == [True, False, False]
|
|
26
|
+
tri = Polygon(((0, 0), (8, 0), (0, 8)))
|
|
27
|
+
assert tri.contains(np.array([1.0]), np.array([1.0]))[0] and not tri.contains(np.array([7.0]), np.array([7.0]))[0]
|
|
28
|
+
assert not Polygon(((0, 0), (1, 1))).contains(x, y).any() # fewer than three vertices contain nothing
|
|
29
|
+
assert 'free-form, 4 vertices' in sq.describe()
|
|
30
|
+
|
|
31
|
+
# hashable (they are used in cache keys) and immutable
|
|
32
|
+
assert len({Box(0, 1, 0, 1), Box(0, 1, 0, 1), Circle(0, 0, 1)}) == 2
|
|
33
|
+
try:
|
|
34
|
+
box.x0 = 9
|
|
35
|
+
raise SystemExit('a Box must be immutable')
|
|
36
|
+
except AttributeError:
|
|
37
|
+
pass
|
|
38
|
+
print('regions ok')
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""The selection-script generator: the script it writes is valid Python, carries its version, and says what it does.
|
|
2
|
+
(Running the scripts on real data is done by validation/check_selection_scripts.py.)"""
|
|
3
|
+
import support # noqa: F401
|
|
4
|
+
import numpy as np
|
|
5
|
+
from support import OFF, dataset
|
|
6
|
+
|
|
7
|
+
import timetag_explorer as tte
|
|
8
|
+
from timetag_explorer.frames import Corrections
|
|
9
|
+
from timetag_explorer.regions import Box, Circle, Polygon
|
|
10
|
+
|
|
11
|
+
n = 200
|
|
12
|
+
ds = dataset(np.linspace(0, 99, n), np.full(n, 600), np.full(n, 700))
|
|
13
|
+
cases = [((10.0, 20.0), None, 'image', OFF),
|
|
14
|
+
(None, Box(500, 700, 600, 800), 'image', OFF),
|
|
15
|
+
((10.0, 20.0), Circle(600, 700, 50), 'rest', Corrections(randcorr=True, geocorr=False, timecorr=True)),
|
|
16
|
+
(None, Polygon(((500, 500), (700, 500), (600, 700))), 'image', Corrections(randcorr=False, geocorr=True))]
|
|
17
|
+
for time_sel, region, frame, corr in cases:
|
|
18
|
+
try:
|
|
19
|
+
s = tte.build_selection_script(ds, time_sel, region, frame, corr)
|
|
20
|
+
except ValueError as exc: # a rest frame needs the SPT file's pointing information
|
|
21
|
+
assert frame != 'image', exc
|
|
22
|
+
continue
|
|
23
|
+
assert not s.warnings or frame != 'image', s.warnings
|
|
24
|
+
compile(s.code, s.filename, 'exec') # valid Python
|
|
25
|
+
assert tte.SELECTION_SCRIPT_VERSION in s.code and 'SELVER' in s.code
|
|
26
|
+
assert s.filename.endswith('.py') and ds.rootname in s.filename
|
|
27
|
+
assert isinstance(s.packages, (list, tuple)) and s.notes
|
|
28
|
+
assert f"SCRIPT_VERSION = '{tte.SELECTION_SCRIPT_VERSION}'" in s.code
|
|
29
|
+
if time_sel is not None:
|
|
30
|
+
assert f'TIME_WINDOW = ({time_sel[0]}, {time_sel[1]})' in s.code
|
|
31
|
+
if isinstance(region, Box):
|
|
32
|
+
assert f'X0, X1 = {float(region.x0)}, {float(region.x1)}' in s.code
|
|
33
|
+
if region is not None:
|
|
34
|
+
assert region.describe() in s.code # the region is written out in words, too
|
|
35
|
+
assert tte.SELECTION_SCRIPT_VERSION == '1.0'
|
|
36
|
+
print('selection script ok')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""TIME-TAG Explorer science code: the calculations behind the web app for HST/STIS FUV TIME-TAG data.
|
|
2
|
+
|
|
3
|
+
This package has no web-server code. It reads a TIME-TAG (and SPT) file, applies the processing steps, transforms
|
|
4
|
+
positions between reference frames and planet maps, and writes the standalone selection scripts.
|
|
5
|
+
|
|
6
|
+
from timetag_explorer import TagDataset, Corrections, Box
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
__version__ = '1.0.1'
|
|
11
|
+
|
|
12
|
+
from .analysis import describe_selection
|
|
13
|
+
from .constants import SELECTION_SCRIPT_VERSION
|
|
14
|
+
from .dataset import TagDataset, load_dataset
|
|
15
|
+
from .frames import DEFAULT_CORRECTIONS, FRAMES, Corrections, FrameInfo
|
|
16
|
+
from .regions import Box, Circle, Polygon, Region
|
|
17
|
+
from .selection_script import SelectionScript, build_selection_script
|
|
18
|
+
|
|
19
|
+
__all__ = ['__version__', 'SELECTION_SCRIPT_VERSION', 'TagDataset', 'load_dataset', 'describe_selection',
|
|
20
|
+
'Corrections', 'DEFAULT_CORRECTIONS', 'FRAMES', 'FrameInfo', 'Box', 'Circle', 'Polygon', 'Region',
|
|
21
|
+
'SelectionScript', 'build_selection_script']
|