CSPICE Toolkit
January 4, 2021 ยท View on GitHub
This is unofficial copy of NASA/JPL SPICE Toolkit for C patched for cross-platform compatibility (Linux, macOS, Windows and Web).
Please refer to the original SPICE Toolkit for C documentation.

Build
Linux
cd src
./mk_linux.csh
The resulting library path is: <repository_root>/lib/libcspice.a
macOS
cd src
./mk_mac.csh
The resulting library path is: <repository_root>/lib/libcspice.a
Windows
Dynamic-Link Library (.dll)
From Visual Studio command line:
cd <repository_root>\src
mk_dll.bat
The resulting library files are:
<repository_root>\bin\cspice.dll<repository_root>\lib\cspice_dll.lib<repository_root>\lib\cspice_dll.exp
Static Library (.lib)
From Visual Studio command line:
cd <repository_root>\src
mk_static.bat
The resulting library file is: <repository_root>\lib\cspice.lib
WebAssembly
-
Install Emscripten
-
Build the static library:
cd src ./mk_wasm.cshThe resulting library path is:
<repository_root>/lib/libcspice_wasm.a -
Build
cspice.wasmand JavaScript glue code for use in Web Workers (cspice.worker.js) and in Node.js (cspice.node.js)cd wasm ./build_wasm.shModify
EXPORTED_FUNCTIONSinbuild_wasm.shto include all required SPICE functions into.wasm/.jsfiles.Modify
kernels/wasm.tmto select the SPICE kernels you want to load.Note that
wasm.tmanditrf93.tfkernels are embedded into the JavaScript glue code.
How to use CSpice WebAssembly in React app
It is recommended to use Create React App (and don't eject it) since it's greatly simplifies the app maintenance.
-
Copy
cspice.worker.jstosrc/spice/cspice.js(Development and Production) -
Copy
cspice.node.jstosrc/spice/__mocks__/cspice.js(Testing with Jest) -
Copy (or symlink)
cspice.wasmtopublic/spiceandsrc/spice/__mocks__folders -
Copy (or symlink) all SPICE kernels you want to use to the respective folders:
public/spice/kernels/(fk|lsk|pck|spk) -
Create file system initialization code, e.g.:
src/spice/filesystem.jsexport function mountFileSystem(instance) { const fs = instance.FS; fs.mkdir('/kernels'); fs.mount(instance.IDBFS, {}, '/kernels'); // initialize file system with data from persistent source fs.syncfs(true, (err) => { if (err) return; // link missing kernels try { fs.lookupPath('/kernels/leapseconds.tls', {}); } catch (err) { fs.createLazyFile('/kernels', 'leapseconds.tls', '/spice/kernels/lsk/leapseconds.tls', true, false)); } try { fs.lookupPath('/kernels/de440s.bsp', {}); } catch (err) { fs.createLazyFile('/kernels', 'de440s.bsp', '/spice/kernels/spk/de440s.bsp', true, false)); } // save file system to persistent source fs.syncfs(() => null); }); }src/spice/__mocks__/filesystem.jsimport { readFileSync } from 'fs'; function copyFile(fileSystem, dest, src) { const stream = fileSystem.open(dest, 'w'); const data = readFileSync(src); fileSystem.write(stream, data, 0, data.length, 0); fileSystem.close(stream); } export function mountFileSystem(instance) { instance.FS.mkdir('/kernels'); copyFile(instance.FS, '/kernels/leapseconds.tls', '/spice/kernels/lsk/leapseconds.tls'); copyFile(instance.FS, '/kernels/de440s.bsp', '/spice/kernels/spk/de440s.bsp'); }Make sure you created/copied all the required kernel files in both Web Worker and Node.js (mocked) code.
-
Create Web Worker and its wrapper for Jest:
src/workers/spice.worker.jsimport cSpice from '../spice/cspice'; import { mountFileSystem } from '../spice/filesystem'; const instance = await cSpice(); mountFileSystem(instance); export tkvrsn_c = async (str) => instance.ccall('tkvrsn_c', 'string', ['string'], [str]);src/workers/__mocks__/spice.worker.jsimport * as SpiceWorker from '../spice.worker'; const spiceWorker = () => SpiceWorker; export default spiceWorker; -
Install workerize-loader
-
Add Jest config
moduleNameMapperoption topackage.json:"jest": { "moduleNameMapper": { "^.*/spice/(.*)": "<rootDir>/src/spice/__mocks__/\$1", "^workerize-loader!(.*)/workers/(.*)": "<rootDir>/src/workers/__mocks__/\$2" } } -
Create React hook and use it in functional components:
src/hooks/useSpiceWorker.jsimport createWorker from 'workerize-loader!../workers/spice.worker'; const spiceWorker = createWorker(); export const useSpiceWorker = () => spiceWorker;src/components/Component.jsximport React, { useState, useEffect } from 'react'; import { useSpiceWorker } from '../hooks/useSpiceWorker'; export const Component = () => { const { tkvrsn_c } = useSpiceWorker(); const [ver, setVer] = useState(); useEffect(() => { tkvrsn_c('TOOLKIT').then(setVer).catch(({ message }) => setVer(message)); }, [tkvrsn_c]); return <div>{ver || 'Loading...'}</div>; }; -
If you use Storybook:
Set
globalObjectto'this'in Webpack config, e.g. in.storybook/main.js:module.exports = { webpackFinal: (config) => { config.output.globalObject = 'this'; return config; } };