Skip to content

Commit f567087

Browse files
authored
Merge pull request #4 from lasp/develop
Minor bug fixes and expanded docs.
2 parents 0c29d17 + 37e887d commit f567087

5 files changed

Lines changed: 177 additions & 4 deletions

File tree

‎README.md‎

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,175 @@
22

33
A library for SPICE extensions and geospatial data product generation.
44

5+
* Github: https://github.com/lasp/curryer
6+
* PyPi: https://pypi.org/project/lasp-curryer/
7+
8+
## Core Features
9+
* Extensions and wrappers for SPICE routines and common data patterns.
10+
* Automation of SPICE kernel creation from JSON definition files and modern data
11+
file formats and third-party data structures.
12+
* Level-1 geospatial data processing routines (e.g., geolocation).
13+
14+
15+
## Install
16+
```shell
17+
pip install lasp-curryer
18+
```
19+
20+
### Data / Binary Files
21+
_NOTE: Data files and precompiled binaries are not currently automated and thus
22+
require manual downloading. This will be addressed in the next major release._
23+
24+
Download from the Curryer repo:
25+
* `data/generic` - Generic spice kernels (e.g., leapsecond kernel)
26+
* Download
27+
* `data/<misssion>` - Mission specific kernels and/or kernel definitions.
28+
* `data/gmted` - Digital Elevation Model (DEMs) with global coverage at
29+
15-arc-second.
30+
* Alternatively, use the script [download_dem.py](bin/download_dem.py) to
31+
download different types and/or resolutions from the USGS.
32+
33+
Define the top-level directory using the environment variable `CURRYER_DATA_DIR`
34+
or pass the path to routines which require data files.
35+
36+
Download Third-party Files:
37+
* SPICE Utilities: https://naif.jpl.nasa.gov/naif/utilities.html
38+
* At minimum: `mkspk`, `msopck`, `brief`, `ckbreif`
39+
* SPICE Generic Kernels (large):
40+
* [de430.bsp](https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/de430.bsp),
41+
place in `data/generic`.
42+
* PyProj Data:
43+
* Data directory: `import pyproj; print(pyproj.datadir.get_user_data_dir())`
44+
* [EGM96 TIFF](https://cdn.proj.org/us_nga_egm96_15.tif)
45+
46+
47+
## Examples
48+
49+
### SPICE Extensions
50+
Time conversion:
51+
```python
52+
from curryer import spicetime
53+
54+
print(spicetime.adapt(0, from_='ugps', to='iso'))
55+
# 1980-01-06 00:00:00.000000
56+
57+
print(spicetime.adapt('2024-11-13', 'iso'))
58+
# 1415491218000000
59+
60+
print(spicetime.adapt(1415491218000000, to='et'))
61+
# 784728069.1827033
62+
63+
import numpy as np
64+
65+
print(repr(spicetime.adapt(np.arange(4) * 60e6 + 1415491218000000, to='dt64')))
66+
# array(['2024-11-13T00:00:00.000000', '2024-11-13T00:01:00.000000',
67+
# '2024-11-13T00:02:00.000000', '2024-11-13T00:03:00.000000'],
68+
# dtype='datetime64[us]')
69+
```
70+
71+
Abstractions:
72+
```python
73+
from curryer import spicierpy
74+
75+
spicierpy.ext.infer_ids('ISS', 25544, from_norad=True)
76+
# {'mission': 'ISS',
77+
# 'spacecraft': -125544,
78+
# 'clock': -125544,
79+
# 'ephemeris': -125544,
80+
# 'attitude': -125544000,
81+
# 'instruments': {}}
82+
83+
earth = spicierpy.obj.Body('Earth')
84+
print(earth, earth.id)
85+
# Body(EARTH) 399
86+
87+
import curryer
88+
89+
mkrn = curryer.meta.MetaKernel.from_json(
90+
'data/tsis1/tsis_v01.kernels.tm.json', sds_dir='data/generic', relative=True
91+
)
92+
print(mkrn)
93+
# MetaKernel(Spacecraft(ISS_SC), Body(ISS_ELC3), Body(ISS_EXPA35), Body(TSIS_TADS),
94+
# Body(TSIS_AZEL), Body(TSIS_TIM), Body(TSIS_TIM_GLINT))
95+
96+
with spicierpy.ext.load_kernel([mkrn.sds_kernels, mkrn.mission_kernels]):
97+
print(spicierpy.ext.instrument_boresight('TSIS_TIM'))
98+
# [0. 0. 1.]
99+
100+
mkrn = curryer.meta.MetaKernel.from_json(
101+
'tests/data/clarreo/cprs_v01.kernels.tm.json', sds_dir='data/generic', relative=True
102+
)
103+
print(mkrn)
104+
# MetaKernel(Spacecraft(ISS_SC), Body(CPRS_BASE), Body(CPRS_PEDE),
105+
# Body(CPRS_AZ), Body(CPRS_YOKE), Body(CPRS_EL), Body(CPRS_HYSICS))
106+
107+
with spicierpy.ext.load_kernel([mkrn.sds_kernels, mkrn.mission_kernels]):
108+
print(curryer.compute.spatial.pixel_vectors('CPRS_HYSICS'))
109+
# (480,
110+
# array([[ 0.00173869, -0.08715574, 0.99619318],
111+
# [ 0.0017315 , -0.08679351, 0.99622482],
112+
# [ 0.00172431, -0.08643127, 0.99625632],
113+
# ...,
114+
# [-0.00171712, 0.08606901, 0.9962877 ],
115+
# [-0.00172431, 0.08643127, 0.99625632],
116+
# [-0.0017315 , 0.08679351, 0.99622482]]))
117+
```
118+
119+
120+
### SPICE Kernel Creation
121+
Create CLARREO Dynamic Kernels:
122+
````python
123+
import curryer
124+
125+
meta_kernel = 'tests/data/clarreo/cprs_v01.kernels.tm.json'
126+
generic_dir = 'data/generic'
127+
kernel_configs = [
128+
'data/clarreo/iss_sc_v01.ephemeris.spk.json',
129+
'data/clarreo/iss_sc_v01.attitude.ck.json',
130+
'data/clarreo/cprs_az_v01.attitude.ck.json',
131+
'data/clarreo/cprs_el_v01.attitude.ck.json',
132+
]
133+
output_dir = '/tmp'
134+
input_file_or_obj = 'tests/data/demo/cprs_geolocation_tlm_20230101_20240430.nc'
135+
136+
# Load meta kernel details. Includes existing static kernels.
137+
mkrn = curryer.meta.MetaKernel.from_json(meta_kernel, relative=True, sds_dir=generic_dir)
138+
139+
# Create the dynamic kernels from the JSONs alone. Note that they
140+
# contain the reference to the input_data netcdf4 file to read.
141+
generated_kernels = []
142+
creator = curryer.kernels.create.KernelCreator(overwrite=False, append=False)
143+
144+
# Generate the kernels from the config and input data (file or object).
145+
for kernel_config in kernel_configs:
146+
generated_kernels.append(creator.write_from_json(
147+
kernel_config, output_kernel=output_dir, input_data=input_file_or_obj,
148+
))
149+
150+
````
151+
152+
153+
### Level-1 Geospatial Processing
154+
Geolocate CLARREO HYSICS Instrument:
155+
```python
156+
import pandas as pd
157+
import curryer
158+
159+
meta_kernel = 'tests/data/clarreo/cprs_v01.kernels.tm.json'
160+
generic_dir = 'data/generic'
161+
162+
time_range = ('2023-01-01', '2023-01-01T00:05:00')
163+
ugps_times = curryer.spicetime.adapt(pd.date_range(*time_range, freq='67ms', inclusive='left'), 'iso')
164+
165+
# Load meta kernel details. Includes existing static kernels.
166+
mkrn = curryer.meta.MetaKernel.from_json(meta_kernel, relative=True, sds_dir=generic_dir)
167+
168+
# Geolocate all the individual pixels and create the L1A data product!
169+
with curryer.spicierpy.ext.load_kernel([mkrn.sds_kernels, mkrn.mission_kernels]):
170+
geoloc_inst = curryer.compute.spatial.Geolocate('CPRS_HYSICS')
171+
l1a_dataset = geoloc_inst(ugps_times)
172+
l1a_dataset.to_netcdf('cprs_geolocation_l1a_20230101.nc')
173+
174+
```
175+
_Assumes dynamic kernels have been created and their file names defined within
176+
the metakernel JSON file._

‎curryer/compute/spatial.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ def ray_intersect_ellipsoid(vector: np.ndarray, position: np.ndarray, geodetic=F
406406
# height is assumed to be zero.
407407
bad_xyz = np.isnan(xyz).any(axis=1)
408408
lla = np.stack([
409-
np.arctan(xyz[:, 1] / xyz[:, 0]),
409+
np.arctan2(xyz[:, 1], xyz[:, 0]),
410410
np.arctan((xyz[:, 2] / (1 - e2)) / np.sqrt(xyz[:, 0] ** 2 + xyz[:, 1] ** 2)),
411411
np.where(bad_xyz, np.nan, 0.0)
412412
], axis=1)

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "lasp-curryer"
3-
version = "0.0.7"
3+
version = "0.1.0"
44
packages = [
55
{ include = "curryer" }
66
]

‎tests/test_compute/test_compute_spatial.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_ellipsoid_intersect_simple(self):
7676

7777
lla = spatial.ray_intersect_ellipsoid(vec, np.array([-7000.0, -7000.0, 0.0]),
7878
geodetic=True, degrees=True)
79-
npt.assert_allclose(lla, np.array([45.0, 0.0, 0.0]))
79+
npt.assert_allclose(lla, np.array([-135.0, 0.0, 0.0]))
8080

8181
# Above (below) the south pole.
8282
xyz = spatial.ray_intersect_ellipsoid(np.array([0.0, 0.0, 1.0]), np.array([0.0, 0.0, -7000.0]))
@@ -96,7 +96,7 @@ def test_ellipsoid_intersect_simple(self):
9696

9797
lla = spatial.ray_intersect_ellipsoid(vectors, positions, geodetic=True, degrees=True)
9898
npt.assert_allclose(lla, np.array([
99-
[0.0, 0.0, 0.0], [90.0, 0.0, 0.0], [0.0, 90.0, 0.0], [-45.0, 0.0, 0.0]]))
99+
[180.0, 0.0, 0.0], [90.0, 0.0, 0.0], [0.0, 90.0, 0.0], [-45.0, 0.0, 0.0]]))
100100

101101
# Handling of non-intersecting vectors:
102102
# * 1st vector is simple nadir.

‎tests/test_tle.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def setUp(self) -> None:
1818
self.__spacetrack_user = os.getenv('SPACETRACK_USER')
1919
self.__spacetrack_pswd = os.getenv('SPACETRACK_PSWD')
2020

21+
@unittest.skip # TODO: API Limits?
2122
def test_ctim_tle_read(self):
2223
accessor = tle.TLERemoteAccessor(self.__spacetrack_user, self.__spacetrack_pswd)
2324
table = accessor.read(self.ctim_norad_id, query_args=[

0 commit comments

Comments
 (0)