From b330f0df3ce510901120a61b3cd4d3c070e1595f Mon Sep 17 00:00:00 2001 From: tariqksoliman Date: Thu, 7 Mar 2024 12:22:27 -0800 Subject: [PATCH] #504 Update docs for SPICE conf --- Missions/spice-kernels-conf.example.json | 27 +++++ docs/pages/Configure/SPICE/SPICE.md | 80 ++++++++++++++ docs/pages/Setup/ENVs/ENVs.md | 8 ++ private/api/BandsToProfile.py | 1 + sample.env | 8 +- scripts/server.js | 5 +- spice/getKernels.js | 135 +++++++++++++---------- src/essence/Tools/Shade/ShadeTool.js | 57 ++++++---- src/essence/Tools/Shade/config.json | 6 +- 9 files changed, 243 insertions(+), 84 deletions(-) create mode 100644 docs/pages/Configure/SPICE/SPICE.md diff --git a/Missions/spice-kernels-conf.example.json b/Missions/spice-kernels-conf.example.json index dbb4bb23..063ebbc2 100644 --- a/Missions/spice-kernels-conf.example.json +++ b/Missions/spice-kernels-conf.example.json @@ -50,6 +50,33 @@ ] } } + }, + "MOON": { + "description": "MOON", + "kernels": [ + "https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/pck00011.tpc" + ], + "targets": { + "LRO": { + "description": "LRO - Lunar Reconnaissance Orbiter", + "kernels": [ + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/lsk/naif0012.tls", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/sclk/lro_clkcor_2023354_v00.tsc", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/pck/pck00010.tpc", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/pck/moon_pa_de421_1900_2050.bpc", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/fk/lro_dlre_frames_2010132_v04.tf", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/fk/lro_frames_2012255_v02.tf", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/fk/moon_assoc_me.tf", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/fk/moon_080317.tf", + "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data/spk/de421.bsp", + { + "url": "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/extras/mk/lro_2023_v04.tm", + "mkRoot": "https://naif.jpl.nasa.gov/pub/naif/pds/data/lro-l-spice-6-v1.0/lrosp_1000/data", + "mkRegex": ".*/spk/lrorg.*.bsp" + } + ] + } + } } } } diff --git a/docs/pages/Configure/SPICE/SPICE.md b/docs/pages/Configure/SPICE/SPICE.md new file mode 100644 index 00000000..cd7ec24f --- /dev/null +++ b/docs/pages/Configure/SPICE/SPICE.md @@ -0,0 +1,80 @@ +--- +layout: page +title: SPICE +permalink: /configure/spice +parent: Configure +nav_order: 3 +--- + +# SPICE + +> SPICE (Spacecraft Planet Instrument C-matrix Events) is a NASA ancillary information system used to compute geometric information used in planning and analyzing science observations obtained from robotic spacecraft. It is also used in planning missions and conducting numerous engineering functions needed to carry out those missions. + +[Link to NAIF SPICE](https://naif.jpl.nasa.gov/naif/) + +MMGIS utilizes SPICE through [spiceypy](https://github.com/AndrewAnnex/SpiceyPy). Some of MMGIS' tools may need to be configured properly with regards to SPICE. The following tools require relevant SPICE kernels (mission-specific files that SPICE needs to perform certain computations and transformations) to be set: + +- ShadeTool + +## Configuring + +MMGIS has a SPICE kernel download scheduler that, when configured, periodically downloads all the latest specified kernels (because they are regularly updated). + +- To begin, set the ENV `SPICE_SCHEDULED_KERNEL_DOWNLOAD=true`. +- Next there is a sample SPICE kernel configuration file under the `/Missions/` directory called `spice-kernels-conf.example.json`. +- Copy `spice-kernels-conf.example.json` to `spice-kernels-conf.json` in the same directory. + +### ENVs + +#### `SPICE_SCHEDULED_KERNEL_DOWNLOAD=` + +If true, then at every other midnight, MMGIS will read /Missions/spice-kernels-conf.json and re/download all the specified kernels. See /Missions/spice-kernels-conf.example.json | boolean | default `false` + +#### `SPICE_SCHEDULED_KERNEL_DOWNLOAD_ON_START=` + +If true, then also triggers the kernel download when MMGIS starts | boolean | default `false` + +#### `SPICE_SCHEDULED_KERNEL_CRON_EXPR=` + +A cron schedule expression for use in the [node-schedule npm library](https://www.npmjs.com/package/node-schedule) | string | default `"0 0 */2 * *"` (every other day) + +### spice-kernels-conf.json + +- If the MMGIS ENV 'SPICE_SCHEDULED_KERNEL_DOWNLOAD=true' MMGIS will read /Missions/spice-kernels-conf.json and re/download all the specified kernels to `/spice/kernels`. + +- 'body' names and 'targets' names must be valid NAIF SPICE names/ids. Meta-kernels (.tm) can also be set for download by using an object instead of a string. + +The schema works as follows: + +```json +{ + "body": { + "{NAIF_BODY_NAME_OR_ID}": { + "desciption": "If a function uses this BODY, it will furnsh() all these direct kernels.", + "kernels": [ + "Full URL to kernel to download", + "Or an object that indicates a meta-kernel (a kernel that lists out other kernels to download):", + { + "url": "Full URL to a meta-kernel to download (file extension .tm)", + "mkRoot": "Root path to replace the meta-kernel's '$KERNELS' prefixes with.", + "mkRegex": "A JavaScript regular expression to filter down which meta-kernel kernels to download. For instance '.*/spk/.*.bsp'" + } + ], + "targets": { + "{NAIF_TARGET_NAME_OR_ID}": { + "description": "If a function uses this BODY and TARGET, it will also furnsh() all these kernels.", + "kernels": [ + "Full URL to kernel to download", + "Or an object that indicates a meta-kernel (a kernel that lists out other kernels to download):", + { + "url": "Full URL to a meta-kernel to download (file extension .tm)", + "mkRoot": "Root path to replace the meta-kernel's '$KERNELS' prefixes with.", + "mkRegex": "A JavaScript regular expression to filter down which meta-kernel kernels to download. For instance '.*/spk/.*.bsp'" + } + ] + } + } + } + } +} +``` diff --git a/docs/pages/Setup/ENVs/ENVs.md b/docs/pages/Setup/ENVs/ENVs.md index 35ba4833..885bbd7c 100644 --- a/docs/pages/Setup/ENVs/ENVs.md +++ b/docs/pages/Setup/ENVs/ENVs.md @@ -143,3 +143,11 @@ If true at build-time, JavaScript source maps will also be built | boolean | def #### `SPICE_SCHEDULED_KERNEL_DOWNLOAD=` If true, then at every other midnight, MMGIS will read /Missions/spice-kernels-conf.json and re/download all the specified kernels. See /Missions/spice-kernels-conf.example.json | boolean | default `false` + +#### `SPICE_SCHEDULED_KERNEL_DOWNLOAD_ON_START=` + +If true, then also triggers the kernel download when MMGIS starts | boolean | default `false` + +#### `SPICE_SCHEDULED_KERNEL_CRON_EXPR=` + +A cron schedule expression for use in the [node-schedule npm library](https://www.npmjs.com/package/node-schedule) | string | default `"0 0 */2 * *"` (every other day) diff --git a/private/api/BandsToProfile.py b/private/api/BandsToProfile.py index 7c87a604..f0d78cd9 100755 --- a/private/api/BandsToProfile.py +++ b/private/api/BandsToProfile.py @@ -117,6 +117,7 @@ def latLonsToPixel(latLonPairs): type = str(sys.argv[4]) # xyorll bands = ast.literal_eval(unquote(sys.argv[5])) # bands + latLonPair = [[lat, lon]] # Open the image diff --git a/sample.env b/sample.env index 194c65e8..616b8f10 100644 --- a/sample.env +++ b/sample.env @@ -81,5 +81,9 @@ SKIP_CLIENT_INITIAL_LOGIN= # If true at build-time, JavaScript source maps will also be built GENERATE_SOURCEMAP=false -# If true, then at every other midnight, MMGIS will read /Missions/spice-kernels-conf.json and re/download all the specified kernels to /spice/kernels. -SPICE_SCHEDULED_KERNEL_DOWNLOAD=false \ No newline at end of file +# If true, then at every other midnight, MMGIS will read /Missions/spice-kernels-conf.json and re/download all the specified kernels. +SPICE_SCHEDULED_KERNEL_DOWNLOAD=false +# If true, then also triggers the kernel download when MMGIS starts +SPICE_SCHEDULED_KERNEL_DOWNLOAD_ON_START=false +# A cron schedule expression for use in the node-schedule npm library. Defaults to "0 0 */2 * *" which is every other day. +SPICE_SCHEDULED_KERNEL_CRON_EXPR= \ No newline at end of file diff --git a/scripts/server.js b/scripts/server.js index aacc0d27..0f2207ed 100644 --- a/scripts/server.js +++ b/scripts/server.js @@ -121,7 +121,10 @@ app.use( ); if (process.env.SPICE_SCHEDULED_KERNEL_DOWNLOAD === "true") - setSPICEKernelDownloadSchedule(); + setSPICEKernelDownloadSchedule( + process.env.SPICE_SCHEDULED_KERNEL_DOWNLOAD_ON_START, + process.env.SPICE_SCHEDULED_KERNEL_CRON_EXPR + ); /////////////////////////// diff --git a/spice/getKernels.js b/spice/getKernels.js index 490e0d89..18091289 100644 --- a/spice/getKernels.js +++ b/spice/getKernels.js @@ -3,15 +3,17 @@ const fs = require("fs"); const path = require("path"); const schedule = require("node-schedule"); +const logger = require("../API/logger"); + const OUTPUT_DIR = "./kernels"; const SHOULD_LOG = false; -function setSPICEKernelDownloadSchedule() { +function setSPICEKernelDownloadSchedule(runImmeditately, cronExpression) { // Run immediately - getKernelsFromConf(); + if (runImmeditately === "true") getKernelsFromConf(); schedule.scheduleJob( - "0 0 */2 * *", // Every other day + cronExpression || "0 0 */2 * *", // Every other day function () { getKernelsFromConf(); } @@ -19,32 +21,38 @@ function setSPICEKernelDownloadSchedule() { } async function getKernelsFromConf() { - //Look at latest kernels.json file - const kernelsFile = path.join( - __dirname, - `../Missions/spice-kernels-conf.json` - ); - const kernels = JSON.parse(fs.readFileSync(kernelsFile, "utf8")); + logger(`info`, `Starting scheduled download of SPICE kernels.`); - Object.keys(kernels.body).forEach(async (b) => { - const body = kernels.body[b]; - if (body.kernels) { - await getKernels(body.kernels, `${OUTPUT_DIR}/${b}`, true, SHOULD_LOG); - } - if (body.targets) { - Object.keys(body.targets).forEach(async (t) => { - const target = kernels.body[b].targets[t]; - if (target.kernels) { - await getKernels( - target.kernels, - `${OUTPUT_DIR}/${b}/${t}`, - true, - SHOULD_LOG - ); - } - }); - } - }); + try { + //Look at latest kernels.json file + const kernelsFile = path.join( + __dirname, + `../Missions/spice-kernels-conf.json` + ); + const kernels = JSON.parse(fs.readFileSync(kernelsFile, "utf8")); + + Object.keys(kernels.body).forEach(async (b) => { + const body = kernels.body[b]; + if (body.kernels) { + await getKernels(body.kernels, `${OUTPUT_DIR}/${b}`, true, SHOULD_LOG); + } + if (body.targets) { + Object.keys(body.targets).forEach(async (t) => { + const target = kernels.body[b].targets[t]; + if (target.kernels) { + await getKernels( + target.kernels, + `${OUTPUT_DIR}/${b}/${t}`, + true, + SHOULD_LOG + ); + } + }); + } + }); + } catch (err) { + logger(`error`, `getKernelsFromConf: ${err}`); + } } /* @@ -63,8 +71,6 @@ async function getKernels( includeMetaKernels, shouldLog ) { - if (shouldLog) - console.log(`Starting downloaded of ${kernelUrls.length} kernels.`); const outputPath = path.join(__dirname, `/${outputDir}`); // Make outputPath directories if they don't already exist @@ -78,43 +84,52 @@ async function getKernels( }); const fileMap = {}; + // Get all immediate kernels - kernelUrls.map((file) => { + for (let i = 0; i < kernelUrls.length; i++) { + const file = kernelUrls[i]; + const filename = file.url || file; const basename = path.basename(filename); + if (shouldLog) + logger(`info`, `Starting downloaded of SPICE kernel: ${filename}`); + // Ignore duplicates - if (fileMap[basename] != null) return true; + if (fileMap[basename] != null) continue; fileMap[basename] = file; - return fetch(filename).then( - async (response) => - new Promise((resolve, reject) => { - const ws = fs.createWriteStream(`${outputPath}/${basename}`); - response.body.pipe(ws); - response.body.on("close", () => { - if (shouldLog) console.log(`Successfully downloaded ${filename}`); - loaded[basename] = true; - proceed(); - resolve(); - }); - ws.on("error", (err) => { - if (shouldLog) - console.log(`WARN: Failed to downloaded ${filename}`, err); - loaded[basename] = true; - proceed(); - reject(); - }); - }) - ); - }); + await fetch(filename) + .then( + async (response) => + new Promise((resolve, reject) => { + const ws = fs.createWriteStream(`${outputPath}/${basename}`); + response.body.pipe(ws); + response.body.on("close", () => { + if (shouldLog) + logger(`success`, `Successfully downloaded ${filename}`); + loaded[basename] = true; + proceed(); + resolve(); + }); + ws.on("error", (err) => { + if (shouldLog) + logger(`warn`, `Failed to downloaded ${filename}`, err); + loaded[basename] = true; + proceed(); + reject(); + }); + }) + ) + .catch((err) => { + logger(`warn`, `Failed to download kernel: ${filename} - ${err}`); + }); + } function proceed() { if (Object.values(loaded).every((item) => item === true)) { try { if (includeMetaKernels === true) { - if (shouldLog) - console.log("Finished downloading all first-level kernels."); const regex = /'\$KERNELS\/.*\..*'\n/g; // Next any meta-kernels (mk) (.tm) need to be read and have their inner-kernels downloaded setTimeout(() => { @@ -143,10 +158,16 @@ async function getKernels( }); }, 1000); } else { - if (shouldLog) console.log("Finished downloading all kernels."); + logger( + `success`, + `Finished downloading immediate kernels (meta-kernels may still be downloading).` + ); } } catch (err) { - console.error(err); + logger( + `warn`, + `Failed to proceed through meta-kernel download: ${err}` + ); } } } diff --git a/src/essence/Tools/Shade/ShadeTool.js b/src/essence/Tools/Shade/ShadeTool.js index 48291f93..43cc821b 100644 --- a/src/essence/Tools/Shade/ShadeTool.js +++ b/src/essence/Tools/Shade/ShadeTool.js @@ -312,16 +312,18 @@ let ShadeTool = { ShadeTool.vars.observers.length > 0 ) { allObservers = ShadeTool.vars.observers - .map( - (c, i) => - "' - ) + .map((c, i) => { + if (c.value != null && c.name != null) + return ( + "' + ) + }) .join('\n') } @@ -345,19 +347,21 @@ let ShadeTool = { "", "", `
Observer
`, - "
", - `
Entity
`, - "", - "
", - "
", - `
Time
`, - "
", - `
`, - ``, + allObservers.length != 0 ? [ + "
", + `
Entity
`, + "", "
", - "
", + "
", + `
Time
`, + "
", + `
`, + ``, + "
", + "
", + ].join('\n') : null, "
", "
Height
", "
", @@ -847,6 +851,8 @@ let ShadeTool = { } } + if (body == null || options.observer == null) return + calls.api( 'chronice', { @@ -901,6 +907,8 @@ let ShadeTool = { } } + if (body == null || options.observer == null) return + calls.api( 'chronice', { @@ -1042,6 +1050,11 @@ let ShadeTool = { if ((ShadeTool.vars.observers[i].value = options.observer)) { obsRefFrame = ShadeTool.vars.observers[i].frame obsBody = ShadeTool.vars.observers[i].body + } else if (options.observer == null) { + // If no observer, pick first + obsRefFrame = ShadeTool.vars.observers[0].frame + obsBody = ShadeTool.vars.observers[0].body + break } } } diff --git a/src/essence/Tools/Shade/config.json b/src/essence/Tools/Shade/config.json index 89975922..c46fa721 100644 --- a/src/essence/Tools/Shade/config.json +++ b/src/essence/Tools/Shade/config.json @@ -25,8 +25,10 @@ ], "observers": [ { - "name": "User-friendly name for observer for spacecraft time computations", - "value": "SPICE Spacecraft name or ID" + "name": "User-friendly name for observer for spacecraft time computations. Set to null to turn off time converting.", + "value": "SPICE Spacecraft name or ID", + "frame": "SPICE reference frame. i.e. IAU_MARS", + "body": "SPICE planet body. i.e. MARS" } ], "defaultHeight": 0,