|
2 | 2 |
|
3 | 3 | A library for SPICE extensions and geospatial data product generation. |
4 | 4 |
|
| 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._ |
0 commit comments