Added SPICE C++ wrapper library with google tests and kernels

At this moment only the (by NAIF considered) most cruicial
c_spice API's have been covered, such as getting state vectors
from one reference frame to another, simple time conversions,
load/unload of single and multiple kernels etc. All of which as
described in:

http://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/info/mostused.html

Also added linker flag to except the LIBCMTD.lib as it caused conflicts
on Windows.
This commit is contained in:
Michal Marcinkowski
2014-08-04 20:43:23 +02:00
parent 69fc0e19e2
commit aba319cbba
7 changed files with 1401 additions and 228 deletions

View File

@@ -30,6 +30,8 @@
cmake_minimum_required (VERSION 2.8)
project (OpenSpace)
SET(CMAKE_EXE_LINKER_FLAGS /NODEFAULTLIB:\"LIBCMTD.lib\")
set(OPENSPACE_BASE_DIR "${PROJECT_SOURCE_DIR}")
set(OPENSPACE_EXT_DIR "${OPENSPACE_BASE_DIR}/ext")

View File

@@ -23,9 +23,7 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <ghoul/filesystem/filesystem.h>
#include "gtest/gtest.h"
#include "openspace/util/spicemanager.h"
class SpiceManagerTest : public testing::Test{
@@ -40,12 +38,10 @@ protected:
void reset() {
openspace::SpiceManager::deinitialize();
openspace::SpiceManager::initialize();
// in the reset method, deinitialize it and
// initialize it again to remove all kernels
}
};
//global constants
#define FILLEN 128
#define TYPLEN 32
#define SRCLEN 128
@@ -54,37 +50,42 @@ const int nrMetaKernels = 9;
int which, handle, count = 0;
char file[FILLEN], filtyp[TYPLEN], source[SRCLEN];
int found;
double abs_error = 0.00001;
//TODO: not hardcoded path cpck05Mar2004.tpc
// Shorthand-path definitions
#define LSK absPath("${TESTDIR}/SpiceTest/spicekernels/naif0008.tls")
#define PCK absPath("${TESTDIR}/SpiceTest/spicekernels/cpck05Mar2004.tpc")
#define META absPath("${TESTDIR}/SpiceTest/spicekernels/metaKernel.tm")
// In this testclass only a handset of the testfunctions require a single kernel.
// The remaining methods rely on multiple kernels, loaded as a SPICE 'meta-kernel'.
#define KERNEL(param, name) int kernelID = -1; \
kernelID = openspace::SpiceManager::ref().loadKernel(param, name); \
EXPECT_TRUE(kernelID != -1) << "loadKernel did not return proper id"; \
return kernelID; \
int loadMetaKernel() { KERNEL(META , "METAKERNEL" ); }
int loadLSKKernel() { KERNEL(LSK , "LEAPSECONDS"); }
int loadPCKKernel() { KERNEL(PCK , "CASSINI_PCK"); }
std::string fileType(char type[]){
std::string str(type);
return str;
}
// Try loading single kernel
TEST_F(SpiceManagerTest, loadSingleKernel){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(LSK, "LEAPSECONDS");
EXPECT_TRUE( kernelID == 1 ) << "loadKernel did not return proper id";
loadLSKKernel();
//naif0008.tls is a text file, check if loaded.
kdata_c(0, "text", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_TRUE(found) << "Kernel not loaded";
ASSERT_TRUE(found) << "Kernel not loaded";
unload_c(LSK.c_str());
}
// Try loading multiple kernels via META file
TEST_F(SpiceManagerTest, loadMetaKernel){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(META, "LEAPSECONDS");
EXPECT_TRUE(kernelID != -1) << "loadKernel did not return proper id";
loadMetaKernel();
// typeArr[] has values ordered to match each type of kernel
// as specified in the 'metaKernel.tm' file
std::string typeArr[nrMetaKernels] = { "META", "TEXT", "TEXT",
@@ -97,49 +98,44 @@ TEST_F(SpiceManagerTest, loadMetaKernel){
}
unload_c(META.c_str());
}
// Try unloading kernel using user assigned keyword
TEST_F(SpiceManagerTest, unloadKernelString){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(LSK, "LEAPSECONDS");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadLSKKernel();
//naif0008.tls is a text file, check if loaded.
kdata_c(0, "text", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_TRUE(found);
ASSERT_TRUE(found);
//unload using string keyword
bool unloaded = openspace::SpiceManager::ref().unloadKernel("LEAPSECONDS");
EXPECT_TRUE(unloaded);
kdata_c(0, "text", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_FALSE(found);
}
// Try unloading kernel using integer as ID
TEST_F(SpiceManagerTest, unloadKernelInteger){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(LSK, "LEAPSECONDS");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
int kernelID = loadLSKKernel();
//naif0008.tls is a text file, check if loaded.
kdata_c(0, "text", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_TRUE(found);
ASSERT_TRUE(found);
//unload using unique int ID
bool unloaded = openspace::SpiceManager::ref().unloadKernel(kernelID);
EXPECT_TRUE(unloaded);
EXPECT_TRUE(unloaded) << "Kernel did not unload";
kdata_c(0, "text", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_FALSE(found);
EXPECT_FALSE(found) << "One or more kernels still present in kernel-pool";
}
// Try unloading multiple kernels
TEST_F(SpiceManagerTest, unloadMetaKernel){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(META, "METAKERNEL");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadMetaKernel();
// The metakernel loads these kerneltypes in the exact order as in typeArr
std::string typeArr[nrMetaKernels] = { "META", "TEXT", "TEXT",
"SPK", "SPK", "SPK",
"TEXT", "CK", "TEXT" };
for (int i = 0; i < nrMetaKernels; i++){
// check kernelpool against typeArr
kdata_c(i, "all", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_EQ(fileType(filtyp), typeArr[i]) << "One or more kernels did not load properly";
}
@@ -147,46 +143,42 @@ TEST_F(SpiceManagerTest, unloadMetaKernel){
EXPECT_TRUE(unloaded);
for (int i = 0; i < nrMetaKernels; i++){
// the values should by now be unloaded
kdata_c(i, "all", FILLEN, TYPLEN, SRCLEN, file, filtyp, source, &handle, &found);
EXPECT_FALSE(found) << "Failed unloading kernel";
}
unload_c(META.c_str());
}
// Attempt finding a value in kernelpool
TEST_F(SpiceManagerTest, hasValue){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(PCK, "CASSINI_PCK");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadPCKKernel();
SpiceInt n;
SpiceInt naifId = 399; //Earth
SpiceDouble radii[3];
int n;
int naifId = 399; //Earth
double radii[3];
std::string kernelPoolValue = "RADII";
found = openspace::SpiceManager::ref().hasValue(naifId, kernelPoolValue);
EXPECT_TRUE(found);
unload_c(META.c_str());
ASSERT_TRUE(found) << "Could not find value for specified kernel";
unload_c(PCK.c_str());
}
// Get 1dim value from kernelpool
TEST_F(SpiceManagerTest, getValueFromID_1D){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(PCK, "CASSINI_PCK");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadPCKKernel();
std::string target = "EARTH";
std::string value1D = "MAG_NORTH_POLE_LAT";
double return1D;
openspace::SpiceManager::ref().getValueFromID(target, value1D, return1D);
EXPECT_EQ(return1D, 78.565);
unload_c(META.c_str());
found = openspace::SpiceManager::ref().getValueFromID(target, value1D, return1D);
ASSERT_TRUE(found) << "Could not retrieve value";
EXPECT_EQ(return1D, 78.565) << "Value not found / differs from expected return";
unload_c(PCK.c_str());
}
// Get 2dim value from kernelpool
TEST_F(SpiceManagerTest, getValueFromID_3D){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(PCK, "CASSINI_PCK");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadPCKKernel();
std::string target = "EARTH";
std::string value3D = "RADII";
@@ -194,16 +186,14 @@ TEST_F(SpiceManagerTest, getValueFromID_3D){
glm::dvec3 return3D;
openspace::SpiceManager::ref().getValueFromID(target, value3D, return3D);
EXPECT_EQ(return3D.x, 6378.14);
EXPECT_EQ(return3D.y, 6378.14);
EXPECT_EQ(return3D.z, 6356.75);
unload_c(META.c_str());
EXPECT_EQ(return3D.x, 6378.14) << "Value not found / differs from expected return";
EXPECT_EQ(return3D.y, 6378.14) << "Value not found / differs from expected return";
EXPECT_EQ(return3D.z, 6356.75) << "Value not found / differs from expected return";
unload_c(PCK.c_str());
}
// Get Ndim value from kernelpool
TEST_F(SpiceManagerTest, getValueFromID_ND){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(PCK, "CASSINI_PCK");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadPCKKernel();
std::string target = "SATURN";
std::string valueND = "RING6_A";
@@ -211,7 +201,7 @@ TEST_F(SpiceManagerTest, getValueFromID_ND){
std::vector<double> returnND;
unsigned int nr = 5;
found = openspace::SpiceManager::ref().getValueFromID(target, valueND, returnND, nr);
EXPECT_TRUE(found);
ASSERT_TRUE(found) << "Could not retrieve value for specified kernel";
std::vector<double> controlVec{ 189870.0, 256900.0, 9000.0, 9000.0, 0.000003 };
@@ -220,13 +210,11 @@ TEST_F(SpiceManagerTest, getValueFromID_ND){
for (int i = 0; i < nr; i++){
EXPECT_EQ(controlVec[i], returnND[i]) << "Vector value not equal";
}
unload_c(META.c_str());
unload_c(PCK.c_str());
}
// Try converting string to Ephemeris time
TEST_F(SpiceManagerTest, stringToEphemerisTime){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(LSK, "LEAPSECONDS");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadLSKKernel();
double ephemerisTime;
double control_ephemerisTime;
@@ -235,14 +223,12 @@ TEST_F(SpiceManagerTest, stringToEphemerisTime){
ephemerisTime = openspace::SpiceManager::ref().stringToEphemerisTime(date);
EXPECT_EQ(ephemerisTime, control_ephemerisTime);
unload_c(META.c_str());
EXPECT_EQ(ephemerisTime, control_ephemerisTime) << "Ephemeries times differ / not found";
unload_c(LSK.c_str());
}
// Try getting positional vector of target
TEST_F(SpiceManagerTest, getTargetPosition){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(META, "METAKERNEL");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadMetaKernel();
double et;
double pos[3];
@@ -252,86 +238,304 @@ TEST_F(SpiceManagerTest, getTargetPosition){
str2et_c(utctime, &et);
spkpos_c("EARTH", et, "J2000", "LT+S", "CASSINI", pos, &lt);
glm::dvec3 targetPosition;
double lightTime = 0.0;
found = openspace::SpiceManager::ref().getTargetPosition("EARTH", et, "J2000", "LT+S", "CASSINI",
targetPosition, lightTime);
ASSERT_TRUE(found);
EXPECT_DOUBLE_EQ(pos[0], targetPosition[0]);
EXPECT_DOUBLE_EQ(pos[1], targetPosition[1]);
EXPECT_DOUBLE_EQ(pos[2], targetPosition[2]);
EXPECT_DOUBLE_EQ(pos[0], targetPosition[0]) << "Position not found or differs from expected return";
EXPECT_DOUBLE_EQ(pos[1], targetPosition[1]) << "Position not found or differs from expected return";
EXPECT_DOUBLE_EQ(pos[2], targetPosition[2]) << "Position not found or differs from expected return";
unload_c(META.c_str());
}
// Try getting position & velocity vectors of target
TEST_F(SpiceManagerTest, getTargetState){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(META, "METAKERNEL");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
loadMetaKernel();
SpiceDouble et;
SpiceDouble state[6];
SpiceDouble lt;
SpiceChar utctime[SRCLEN] = "2004 jun 11 19:32:00";
double et;
double state[6];
double lt;
char utctime[SRCLEN] = "2004 jun 11 19:32:00";
str2et_c(utctime, &et);
spkezr_c("EARTH", et, "J2000", "LT+S", "CASSINI", state, &lt);
glm::dvec3 targetPosition;
glm::dvec3 targetVelocity;
double lightTime = 0.0;
found = openspace::SpiceManager::ref().getTargetState("EARTH", et, "J2000", "LT+S", "CASSINI",
targetPosition, targetVelocity, lightTime);
targetPosition, targetVelocity, lightTime);
ASSERT_TRUE(found);
//x,y,z
for (int i = 0; i < 3; i++){
EXPECT_DOUBLE_EQ(state[i], targetPosition[i]) << "Position vector is wrong";
EXPECT_DOUBLE_EQ(state[i+3], targetVelocity[i]) << "Velocity vector is wrong";
EXPECT_DOUBLE_EQ(state[i], targetPosition[i]) << "Position not found or differs from expected return";
EXPECT_DOUBLE_EQ(state[i+3], targetVelocity[i]) << "Velocity not found or differs from expected return";
}
unload_c(META.c_str());
}
// Try getting transformation matrix and transform position and velocity into new reference frame
TEST_F(SpiceManagerTest, getStateTransformMatrix){
loadMetaKernel();
TEST_F(SpiceManagerTest, getPositionTransformMatrix){
int kernelID = -1;
kernelID = openspace::SpiceManager::ref().loadKernel(META, "METAKERNEL");
EXPECT_TRUE(kernelID == 1) << "loadKernel did not return proper id";
SpiceDouble et;
SpiceDouble state[6];
SpiceDouble state_t[6];
SpiceDouble lt;
SpiceDouble referenceMatrix[6][6];
double et;
double state[6];
double state_t[6];
double lt;
double referenceMatrix[6][6];
str2et_c("2004 jun 11 19:32:00", &et);
spkezr_c("PHOEBE", et, "J2000", "LT+S", "CASSINI", state, &lt);
sxform_c("J2000", "IAU_PHOEBE", et, referenceMatrix);
glm::mat3x3 positionMatrix;
glm::mat3x3 velocityMatrix;
glm::dvec3 position(state[0], state[1], state[2]);
glm::dvec3 velocity(state[3], state[4], state[5]);
openspace::mat6x6 stateMatrix;
openspace::transformMatrix stateMatrix(6);
found = openspace::SpiceManager::ref().getStateTransformMatrix("J2000",
"IAU_PHOEBE",
et,
stateMatrix);
stateMatrix);
ASSERT_TRUE(found);
double absolute_range = 0.00000001;
//check for matrix consistency
for (int i = 0; i < 6; i++){
for (int j = 0; j < 6; j++){
ASSERT_NEAR(referenceMatrix[i][j], stateMatrix[i][j], absolute_range)
<< "Assertion failed at [" << i << ", " << j << "]"<< std::endl;
EXPECT_DOUBLE_EQ(referenceMatrix[i][j], stateMatrix(i, j)) << "State-matrix not set or has wrong values";
}
}
mxvg_c(referenceMatrix, state, 6, 6, state_t);
openspace::matrix6 abc;
stateMatrix.transform(position, velocity);
abc.data[0][0] = 10;
for (int i = 0; i < 3; i++){
EXPECT_DOUBLE_EQ(position[i], state_t[i]) << "Position vector differs from its reference";
EXPECT_DOUBLE_EQ(velocity[i], state_t[i + 3]) << "Velocity vector differs from its reference";
}
unload_c(META.c_str());
}
// Try getting transformation matrix and transform the position only into new reference frame
TEST_F(SpiceManagerTest, getPositionTransformMatrix){
loadMetaKernel();
TEST_F(SpiceManagerTest, dontknowyet){
double et;
double lt;
double state[3] = { 1.0, 1.0, 1.0 };
double state_t[3];
double referenceMatrix[3][3];
}
str2et_c("2004 jun 11 19:32:00", &et);
pxform_c("CASSINI_HGA", "J2000", et, referenceMatrix);
openspace::transformMatrix positionMatrix(3);
glm::dvec3 position(state[0], state[1], state[2]);
found = openspace::SpiceManager::ref().getPositionTransformMatrix("CASSINI_HGA",
"J2000",
et,
positionMatrix);
ASSERT_TRUE(found);
//check for matrix consistency
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
EXPECT_DOUBLE_EQ(referenceMatrix[i][j], positionMatrix(i, j)) << "Position-matrix not set or has wrong values";
}
}
//transform reference position into new frame
mxvg_c(referenceMatrix, state, 3, 3, state_t);
positionMatrix.transform(position);
//check transformed values match
for (int i = 0; i < 3; i++){
EXPECT_DOUBLE_EQ(position[i], state_t[i]) << "Position vector differs from its reference";
}
unload_c(META.c_str());
}
// Try to get boresight vector and instrument field of view boundary vectors
TEST_F(SpiceManagerTest, getFieldOfView){
loadMetaKernel();
int n;
int cassini_ID;
double et;
double lt;
double boresight[3];
double bounds_ref[5][3];
char shape_ref[TYPLEN];
char name_ref[FILLEN];
str2et_c("2004 jun 11 19:32:00", &et);
bodn2c_c("CASSINI_ISS_NAC", &cassini_ID, &found);
if (!found){
printf("error cannot locate ID for Cassini \n");
}
getfov_c(cassini_ID, 5, TYPLEN, TYPLEN, shape_ref, name_ref, boresight, &n, bounds_ref);
std::string shape, name;
shape.resize(32);
name.resize(32);
std::vector<glm::dvec3> bounds;
int nrReturned;
found = openspace::SpiceManager::ref().getFieldOfView("CASSINI_ISS_NAC",
shape,
name,
boresight,
bounds,
nrReturned);
ASSERT_TRUE(found);
//check vectors have correct values
for (int i = 0; i < nrReturned; i++){
for (int j = 0; j < 3; j++){
EXPECT_DOUBLE_EQ(bounds_ref[i][j], bounds[i][j]) << "One or more Field of View Boundary vectors \
differ from expected output";
}
}
unload_c(META.c_str());
}
// Try converting rectangular coordinates to latitudal
TEST_F(SpiceManagerTest, rectangularToLatitudal){
loadMetaKernel();
char frame[FILLEN], shape[FILLEN];
double lat, lon;
double bounds[4][3], bsight[3],
obspos[3], point_ref[3];
double dist, et, radius_ref, trgepc;
int n, naifId;
int found;
// First, find an intersection point to convert to rectangular coordinates
str2et_c("2004 jun 11 19:32:00", &et);
bodn2c_c("CASSINI_ISS_NAC", &naifId, &found);
getfov_c(naifId, 4, FILLEN, FILLEN, shape, frame, bsight, &n, bounds);
srfxpt_c("Ellipsoid", "PHOEBE", et, "LT+S", "CASSINI", frame, bsight,
point_ref, &dist, &trgepc, obspos, &found);
reclat_c(point_ref, &radius_ref, &lon, &lat);
glm::dvec3 point(point_ref[0], point_ref[1], point_ref[2]);
double radius, longitude, latitude;
found = openspace::SpiceManager::ref().rectangularToLatitudal(point, radius, longitude, latitude);
ASSERT_TRUE(found);
ASSERT_NEAR(radius, radius_ref, abs_error) << "radius is not set / has incorrect values";
ASSERT_NEAR(longitude, lon, abs_error) << "longitude is not set / has incorrect values";
ASSERT_NEAR(latitude, lat, abs_error) << "latitude is not set / has incorrect values";
unload_c(META.c_str());
}
// Try converting latitudinal coordinates to rectangular
TEST_F(SpiceManagerTest, latitudinalToRectangular){
loadMetaKernel();
char frame[FILLEN], shape[FILLEN];
double lat, lon;
double bounds[4][3], bsight[3],
obspos[3], point_ref[3];
double dist, et, radius_ref, trgepc;
int n, naifId;
// First, find an intersection point to convert to latitudinal coordinates //
str2et_c("2004 jun 11 19:32:00", &et);
bodn2c_c("CASSINI_ISS_NAC", &naifId, &found);
getfov_c(naifId, 4, FILLEN, FILLEN, shape, frame, bsight, &n, bounds);
srfxpt_c("Ellipsoid", "PHOEBE", et, "LT+S", "CASSINI", frame, bsight,
point_ref, &dist, &trgepc, obspos, &found);
reclat_c(point_ref, &radius_ref, &lon, &lat);
lat *= rpd_c();
lon *= rpd_c();
double lat_ref = lat;
double lon_ref = lon;
double rectangular_ref[3];
latrec_c(radius_ref, lon, lat, rectangular_ref);
glm::dvec3 coordinates;
found = openspace::SpiceManager::ref().latidudinalToRectangular(radius_ref, lon, lat, coordinates);
ASSERT_TRUE(found);
ASSERT_NEAR(lon_ref, lon, abs_error) << "longitude is not set / has incorrect values";
ASSERT_NEAR(lat_ref, lat, abs_error) << "latitude is not set / has incorrect values";
unload_c(META.c_str());
}
// Try to convert planetocentric coordinates to rectangular
TEST_F(SpiceManagerTest, planetocentricToRectangular){
loadPCKKernel();
double lat = -35.0; //initial values
double lon = 100.0;
double rectangular_ref[3];
double radius;
int naifId;
bodn2c_c("EARTH", &naifId, &found);
srfrec_c(naifId, lon*rpd_c(), lat*rpd_c(), rectangular_ref);
glm::dvec3 rectangular;
found = openspace::SpiceManager::ref().planetocentricToRectangular("EARTH", lon, lat, rectangular);
for (int i = 0; i < 3; i++){
EXPECT_EQ(rectangular[i], rectangular_ref[i]) << "Rectangular coordinates differ from expected output";
}
unload_c(PCK.c_str());
}
// Try getting sub-observer point
TEST_F(SpiceManagerTest, getSubObserverPoint){
loadMetaKernel();
double et, targetEt_ref, targetEt;
double radii[3], subObserverPoint_ref[3], vectorToSurfacePoint_ref[3];
static SpiceChar * method[2] = { "Intercept: ellipsoid", "Near point: ellipsoid" };
str2et_c("2004 jun 11 19:32:00", &et);
glm::dvec3 subObserverPoint;
glm::dvec3 vectorToSurfacePoint;
for (int i = 0; i < 2; i++){
subpnt_c(method[i], "phoebe", et, "iau_phoebe",
"lt+s", "earth", subObserverPoint_ref, &targetEt_ref, vectorToSurfacePoint_ref);
found = openspace::SpiceManager::ref().getSubObserverPoint(method[i], "phoebe", et, "iau_phoebe",
"lt+s", "earth", subObserverPoint,
targetEt, vectorToSurfacePoint);
ASSERT_TRUE(found);
EXPECT_EQ(targetEt_ref, targetEt);
for (int i = 0; i < 3; i++){
EXPECT_EQ(subObserverPoint_ref[i], subObserverPoint[i])
<< "Sub-observer vector differs from its reference";
EXPECT_EQ(vectorToSurfacePoint_ref[i], vectorToSurfacePoint[i])
<< "Observer to surface point vector differs from its reference";
}
}
unload_c(META.c_str());
}
// Try getting sub-solar point
TEST_F(SpiceManagerTest, getSubSolarPoint){
loadMetaKernel();
double et, targetEt_ref, targetEt;
double radii[3], subSolarPoint_ref[3], vectorToSurfacePoint_ref[3];
static SpiceChar * method[2] = { "Intercept: ellipsoid", "Near point: ellipsoid" };
str2et_c("2004 jun 11 19:32:00", &et);
glm::dvec3 subSolarPoint;
glm::dvec3 vectorToSurfacePoint;
for (int i = 0; i < 2; i++){
subslr_c(method[i], "phoebe", et, "iau_phoebe",
"lt+s", "earth", subSolarPoint_ref, &targetEt_ref, vectorToSurfacePoint_ref);
found = openspace::SpiceManager::ref().getSubSolarPoint(method[i], "phoebe", et, "iau_phoebe",
"lt+s", "earth", subSolarPoint,
targetEt, vectorToSurfacePoint);
ASSERT_TRUE(found);
EXPECT_EQ(targetEt_ref, targetEt);
for (int i = 0; i < 3; i++){
EXPECT_EQ(subSolarPoint_ref[i], subSolarPoint[i])
<< "Sub-solar vector differs from its reference";
EXPECT_EQ(vectorToSurfacePoint_ref[i], vectorToSurfacePoint[i])
<< "Observer to surface point vector differs from its reference";
}
}
unload_c(META.c_str());
}

View File

@@ -21,7 +21,6 @@
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#ifndef __SPICEWRAPPER_H__
#define __SPICEWRAPPER_H__
@@ -33,23 +32,10 @@
#include <map>
namespace openspace{
//just a typedef for now..
typedef double mat6x6[6][6];
class matrix6{
private:
int row, col;
double data[6][6];
public:
matrix6& operator= (const matrix6& lhs){
if (this != &lhs)
}
};
class transformMatrix;
class SpiceManager{
public:
// Initialization
// Initialization ----------------------------------------------------------------------- //
/**
* Static initializer that initializes the static member.
*/
@@ -79,12 +65,13 @@ public:
*
* \param either correspondign unique ID or shorthand with
* which the kernel was loaded and is to be unloaded.
* \return Whether the function succeeded or not
*/
bool unloadKernel(const std::string& shorthand);
bool unloadKernel(int kernelId);
// Acessing Kernel Data - Constants and Ids
// Acessing Kernel Data - Constants and Ids --------------------------------------------- //
/**
* Determine whether values exist for some item for any body in the kernel pool.
@@ -92,6 +79,7 @@ public:
*
* \param naifId ID code of body.
* \param item Item to find ("RADII", "NUT_AMP_RA", etc.).
* \return Whether the function succeeded or not
*/
bool hasValue(int naifId, const std::string& kernelPoolValueName) const;
@@ -100,28 +88,27 @@ public:
* item associated with a body.
* For further details, please refer to 'bodvrd_c' in SPICE Docummentation
*
* \param bodyName Body name.
* \param bodyName Body name.
* \param kernelPoolValueName Item for which values are desired. ("RADII", "NUT_PREC_ANGLES", etc. )
*/
* \return Whether the function succeeded or not
*/
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
double& value) const;
/*
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec2& value) const;
*/
/* Overloaded method for 3dim vectors, see above specification.*/
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec3& value) const;
/* Overloaded method for 4dim vectors, see above specification.*/
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec4& value) const;
/* Overloaded method for Ndim vectors, see above specification.*/
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
std::vector<double>& values, unsigned int num) const;
// Converting between UTC and Ephemeris Time (LSK)
// Converting between UTC and Ephemeris Time (LSK) ------------------------------------- //
/**
* Convert a string representing an epoch to a double precision
@@ -134,7 +121,7 @@ public:
*/
double stringToEphemerisTime(const std::string& epochString) const;
// Computing Positions of Spacecraft and Natural Bodies(SPK)
// Computing Positions of Spacecraft and Natural Bodies(SPK) ---------------------------- //
/**
* Return the position of a target body relative to an observing
@@ -158,7 +145,6 @@ public:
const std::string& observer,
glm::dvec3& targetPosition,
double lightTime) const;
/**
* Return the state (position and velocity) of a target body
* relative to an observing body, optionally corrected for light
@@ -184,7 +170,7 @@ public:
glm::dvec3& targetVelocity,
double lightTime) const;
// Computing Transformations Between Frames (FK)
// Computing Transformations Between Frames (FK) -------------------------------------- //
/**
* Return the state transformation matrix from one frame to
@@ -200,15 +186,7 @@ public:
bool getStateTransformMatrix(const std::string& fromFrame,
const std::string& toFrame,
double ephemerisTime,
mat6x6& stateMatrix) const;
/**
* Multiply the 6x6 stateTransformMatrix and two 3D vector, postion and velocity.
*/
bool multiplyWithStateTransmMat6x6(mat6x6 stateMatrix,
glm::dvec3 position,
glm::dvec3 velocity) const;
transformMatrix& stateMatrix) const;
/**
* Return the matrix that transforms position vectors from one
@@ -224,9 +202,9 @@ public:
bool getPositionTransformMatrix(const std::string& fromFrame,
const std::string& toFrame,
double ephemerisTime,
glm::mat3x3& positionTransformationMatrix) const;
transformMatrix& positionMatrix) const;
// Retrieving Instrument Parameters (IK)
// Retrieving Instrument Parameters (IK) ------------------------------------------ //
/**
* This routine returns the field-of-view (FOV) parameters for a
@@ -239,15 +217,17 @@ public:
* \param boresightVector Boresight vector.
* \param numberOfBoundaryVectors Number of boundary vectors returned.
* \param bounds Field Of View boundary vectors
* \param room Maximum number of vectors that can be returned.
* \return Whether the function succeeded or not
*/
bool getFieldOfView(const std::string& naifInstrumentId,
std::string& instrumentFovShape,
std::string& nameOfFrame,
double boresightVector,
std::vector<glm::vec3>& bounds) const;
std::string& fovShape,
std::string& frameName,
double boresightVector[],
std::vector<glm::dvec3>& bounds,
int& nrReturned) const;
// Computing Planetocentric, Planetodetic, and Planetographic Coordinates
// Computing Planetocentric, Planetodetic, and Planetographic Coordinates ---------- //
/**
* Convert from rectangular coordinates to latitudinal coordinates.
@@ -259,13 +239,13 @@ public:
* \param latitude Latitude of the point in radians. The range is [-pi/2, pi/2].
* \return Whether the function succeeded or not
*/
bool rectangularToLatitudal(const glm::vec3 coordinates,
bool rectangularToLatitudal(const glm::dvec3 coordinates,
double& radius,
double& longitude,
double& latitude) const;
/**
* Convert from latitudinal coordinates to rectangular coordinates.
* For further details, please refer to 'reclat_c ' in SPICE Docummentation
* For further details, please refer to 'latrec_c ' in SPICE Docummentation
*
* \param radius Distance of a point from the origin.
* \param longitude Longitude of point in radians.
@@ -276,7 +256,7 @@ public:
bool latidudinalToRectangular(double radius,
double& longitude,
double& latitude,
glm::vec3& coordinates) const;
glm::dvec3& coordinates) const;
/**
* Convert planetocentric latitude and longitude of a surface
@@ -289,12 +269,12 @@ public:
* \param coordinates Rectangular coordinates of the point.
* \return Whether the function succeeded or not
*/
bool planetocentricToRectangular(int naif_id,
bool planetocentricToRectangular(const std::string& naifName,
double& longitude,
double& latitude,
glm::vec3& coordinates) const;
glm::dvec3& coordinates) const;
// Computing Sub - observer and Sub - solar Points
// Computing Sub - observer and Sub - solar Points --------------------------------- //
/**
* Compute the rectangular coordinates of the sub-observer point on
@@ -319,9 +299,9 @@ public:
std::string bodyFixedFrame,
std::string aberrationCorrection,
std::string observer,
glm::vec3& subObserverPoint,
glm::dvec3& subObserverPoint,
double& targetEpoch,
glm::vec3& vectorToSurfacePoint) const;
glm::dvec3& vectorToSurfacePoint) const;
/**
* Compute the rectangular coordinates of the sub-observer point on
@@ -342,34 +322,102 @@ public:
*/
bool getSubSolarPoint(std::string computationMethod,
std::string target,
double ephemeris,
double ephemeris,
std::string bodyFixedFrame,
std::string aberrationCorrection,
std::string observer,
glm::vec3& subObserverPoint,
double& targetEpoch,
glm::vec3& vectorToSurfacePoint) const;
//TODO: add additional functions for 'mxvg_c'
glm::dvec3& subSolarPoint,
double& targetEpoch,
glm::dvec3& vectorToSurfacePoint) const;
private:
SpiceManager() = default;
~SpiceManager();
SpiceManager(const SpiceManager& c) = delete;
static SpiceManager* _manager;
struct spiceKernel {
std::string path;
std::string name;
int id;
};
std::vector<spiceKernel> _loadedKernels;
unsigned int kernelCount = 0;
unsigned int _kernelCount = 0;
};
}
/**
* SpiceManager helper class, a storage container used to
* transform state vectors from one reference frame to another.
* The client creates an instance of <code>transformMatrix</code>
* and after its been passed to either <code>getStateTransformMatrix</code>
* or <code>getPositionTransformMatrix</code> the instantiated object
* can transform position and velocity to any specified reference frame.
*
* Client-sied example:
* openspace::transformMatrix m(6);
* openspace::SpiceManager::ref().getStateTransformMatrix("J2000",
* "IAU_PHOEBE",
* et,
* stateMatrix);
* stateMatrix.transform(position, velocity);
* (or if transformMatrix is 3x3:)
* stateMatrix.transform(position);
*/
#define COPY(to, from) memcpy(to, from, sizeof(double)* 3);
class transformMatrix{
private:
int N;
double *data;
bool empty;
double* ptr() {
empty = false;
return data;
}
friend class SpiceManager;
public:
/* default constructor */
transformMatrix();
/* default destructor */
~transformMatrix(){ delete[] data; };
/* allocation of memory */
transformMatrix(int n) : N(n){
data = new double[N*N];
empty = true;
}
/** As the spice function mxvg_c requires a 6dim vector
* the two 3dim state vectors are packed into 'state'.
* Transformed values are then copied back from state_t
* to each corresponding statevector.
*
* \param position, positional vector to be expressed in
* the new reference frame.
* \param velocity, (optional) velocity input is only
* transformed in conjunction with a 6x6 matrix, otherwise
* the method ignores its second argument.
*/
void transform(glm::dvec3& position,
glm::dvec3& velocity = glm::dvec3()){
assert(("transformation matrix is empty", !empty));
double *state;
double *state_t;
state = new double[N];
state_t = new double[N];
COPY(state, &position);
if (N == 6) COPY(state + velocity.length(), &velocity);
mxvg_c(data, state, N, N, state_t);
COPY(&position, state_t);
if (N == 6) COPY(&velocity, state_t + velocity.length());
}
/* overloaded operator()
* asserts matrix has been filled
*/
inline double operator()(int i, int j) const{
assert(("transformation matrix is empty", !empty));
return data[j + i*N];
}
};
#undef COPY
}
#endif

View File

@@ -0,0 +1,779 @@
KPL/IK
ISS Instrument Kernel
==============================================================================
This instrument kernel (I-kernel) contains references to the mounting
alignment, internal and FOV geometry for the Cassini Imaging Science
Subsystem (ISS) instruments.
Version and Date
----------------------------------------------------------
The TEXT_KERNEL_ID stores version information of loaded project text
kernels. Each entry associated with the keyword is a string that consists
of four parts: the kernel name, version, entry date, and type. For example,
the ISS I-kernel might have an entry as follows:
TEXT_KERNEL_ID += 'CASSINI_ISS V0.0.0 29-SEPTEMBER-1999 IK'
| | | |
| | | |
KERNEL NAME <-------+ | | |
| | V
VERSION <-------+ | KERNEL TYPE
|
V
ENTRY DATE
ISS I-Kernel Version:
\begindata
TEXT_KERNEL_ID += 'CASSINI_ISS V0.9.0 23-JANUARY-2004 IK'
\begintext
Version 0.9 -- January 23, 2004 -- Josh Riley and Lee Elson
-- Updated focal lengths using inflight calibration results.
Version 0.8 -- April 23, 2001 -- Scott Turner
-- Updated kernel to utilize new FOV ANGLES specification.
Version 0.7 -- September 27, 2000 -- Scott Turner
-- The value of IFOV for the NAC recorded in previous kernels as
60 should have been 6. The value associated with
INS-82360_IFOV and the table in the documentation have been
updated to reflect this correction.
Version 0.6 -- August 15, 2000 -- Scott Turner
-- Recalculated the FOV definitions to enhance precision.
Version 0.5 -- June 7, 2000 -- Scott Turner
-- Changed the INS[#]_FOV_CENTER_PIXEL keyword to reflect
changes in the I-kernel SIS.
Version 0.4 -- March 27, 2000 -- Scott Turner
-- Included sample code in FORTRAN and C for computing the
angular extents of the ISS fields of view.
-- Added the TEXT_KERNEL_ID keyword.
-- Minor cosmetic alterations to the structure of the kernel to
improve readability.
Version 0.3 -- March 17, 2000 -- Scott Turner
-- This I-kernel reflects changes discussed at the SPICE team
meeting on 3/16/2000.
-- All of the INS[ID]_SEQ_[WORD] keywords were eliminated.
-- INS[ID]_SEQ_FOV_CENTER is now keyed to
INS[ID]_FOV_CENTER_PIXEL.
Version 0.2 -- March 6, 2000 -- Scott Turner
-- Fixed a few errors in the comment text describing the ISS and
this kernel's intended usage.
-- Removed 'NAC' and 'WAC' from keywords. These values were
necessary when both the NAC and WAC shared a single
instrument ID code.
-- Added 'SEQ_' to the FOV keywords that are to be used by PDT
and possibly other sequence tools.
-- Added name to ID code mappings for 'CASSINI_ISS_NAC' and
'CASSINI_ISS_WAC'
-- Added the keywords 'INS-82360_SEQ_PIXEL_SIZE',
'INS-82361_SEQ_PIXEL_SIZE', 'INS-82360_FOV_NAME',
'INS-82361_FOV_NAME', 'INS-82360_BORESIGHT_ID',
'INS-82361_BORESIGHT_ID', 'INS-82360_BORESIGHT_NAME',
'INS-82361_BORESIGHT_NAME'
Version 0.1 -- October 8, 1999 -- Scott Turner
-- Added an additional set of keywords that describe the FOVs of
the instruments to maintain compatibility with previously
discussed standards. These keywords may change or be
eliminated when the kernel evolves. Placeholders for the NAC
and WAC radiators were also placed into keywords conforming
to this standard.
-- Altered the NAC/WAC FOV definition parameters to conform with
GETFOV's expectations. Added documentation describing the
parameters.
-- Changed NAC and WAC ID codes from -82010 and -82020 to -82360
and -82361 respectively.
-- Altered reference No. 4 to include proper document title and
project document number.
-- Added a section for data to store instrument mode timing. The
values present are just place holders until the actual ones
are uncovered.
Version 0.0 -- June 21, 1999 -- Scott Turner
-- Initial Prototype Release for Review.
References
----------------------------------------------------------
1. ``Cassini Science Instruments and Investigations'', Revised
Second Printing. Stephen J. Edberg.
2. Cassini Spacecraft Frames Definition Kernel.
3. JPL Cassini Project Web Page describing the instruments.
4. Cassini Document No. 699-416 Imaging Science Subsystem
Calibration Report
5. Email from Jeff Boyer regarding necessary data for footprint
calculations.
6. Email discussion with Vance Haemmerle regarding the location
of the (0,0) pixel on the CCD and it's correlation to the NAC
and WAC FOV definition.
7. Cassini/NAIF SPICE Workshop, November 8-9, 1999.
8. Email from Vance Haemmerle regarding an incorrect value
recorded for the NAC IFOV parameter.
9. Memo from P. Thomas "Geometric calibration of the ISS NAC and
WAC", December 18, 2002.
Contact Information
----------------------------------------------------------
Direct questions, comments or concerns about the contents of this kernel
to:
Lee Elson, NAIF/JPL, (818)-354-4223, Lee.Elson@jpl.nasa.gov
Implementation Notes
----------------------------------------------------------
This file is used by the SPICE system as follows: programs that make use of
this instrument kernel must ``load'' the kernel, normally during program
initialization. Loading the kernel associates data items with their names
in a data structure called the ``kernel pool''. The SPICELIB routine FURNSH
and CSPICE routine furnsh_c load SPICE kernels as shown below:
FORTRAN (SPICELIB)
CALL FURNSH ( 'kernel_name' )
C (CSPICE)
furnsh_c ( "kernel_name" )
In order for a program or subroutine to extract data from the pool, the
SPICELIB routines GDPOOL and GIPOOL are used. See [2] for details.
This file was created and may be updated with a text editor or word
processor.
Naming Conventions
----------------------------------------------------------
All names referencing values in this I-kernel start with the characters
`INS' followed by the NAIF Cassini spacecraft ID number (-82) followed by a
NAIF three digit ID code for the ISS instruments. (NAC = 360, WAC = 361).
The remainder of the name is an underscore character followed by the unique
name of the data item. For example, the ISS NAC boresight direction in the
ISS NAC optics frame (``CASSINI_ISS_NAC'' -- see [2]) is specified by:
INS-82360_BORESIGHT
The upper bound on the length of the name of any data item is 32
characters.
If the same item is included in more than one file, or if the same item
appears more than once within a single file, the latest value supersedes
any earlier values.
ISS description
----------------------------------------------------------
From [3]:
``IMAGING SCIENCE SUBSYSTEM (ISS)
The Cassini orbiter imaging experiments will encompass a wide variety of
targets (Saturn, the rings, Titan, the icy satellites, and star fields) and
a wide range of observing distances for various scientific purposes. The
science objectives include studying the atmospheres of Saturn and Titan,
the rings of Saturn and their interactions with the planet's satellites,
and the surface characteristics of the satellites, including Titan. Because
of these multiple objectives, the Imaging Science Subsystem (ISS) has two
separate camera designs. The first is a narrow-angle camera (NAC) design
that will obtain high-resolution images of the target of interest. The
second is a wide-angle camera (WAC) design that provides a different scale
of image resolution and more complete coverage spatially. The spacecraft
will carry one NAC and one WAC. The NAC is also used to obtain optical
navigation images for the mission with the WAC acting as a functionally
redundant backup unit for this purpose.
The cameras are charge-coupled device (CCD) imagers. A CCD is essentially a
large-scale integrated circuit (IC) that has a two-dimensional array of
hundreds or thousands of charge-isolated wells, each representing a picture
element or "pixel." Light falling on a well is absorbed by a
photoconductive substrate, such as silicon, which releases a quantity of
electrons proportional to the intensity of the light. The CCD detects and
stores an accumulated electrical charge representing the light level on
each well. These charges are subsequently read out for conversion to
digital data. CCDs are much more sensitive to light of a wider spectrum
than vidicon tube-type imagers, and they are less massive, require less
energy, and interface more easily with digital circuitry.
The Cassini imagers differ primarily in the design of the optics. The NAC
has a focal length of 2000 mm, and the WAC , which uses optics inherited
from the Voyager mission, has a focal length of 200 mm. The cameras each
have a focal plane shutter of the same type as used on both Voyager and
Galileo, and they have a two-wheel filter-changing mechanism derived from
the Hubble Space Telescope Wide Field/Planetary Camera (WF/PC) design. The
CCD detector is cooled to suppress dark current (residual current in the
CCD beyond that released by incident light), which is dependent upon
temperature. It is also shielded from ionizing radiation.
The CCD detector design is a square array of 1024x1024 pixels, each pixel
12 micrometers on a side. The IC chip will use three-phase,
front-side-illuminated architecture, with a coating of lumogen phosphor to
provide ultraviolet response. The detector is passively cooled by a
radiator to approximately 10 degrees C below its nominal operating
temperature (approximately minus 90 degrees C), and then it is controlled
to the operating temperature by a proportional control heater. To minimize
radiator size and heater power, the detector/radiator combination is
thermally isolated from the rest of the camera head assembly (CHA).
The entire NAC is thermally isolated from the remote sensing pallet (RSP)
on which it is mounted in order to minimize the effects of RSP thermal
variations on NAC image quality. The WAC, being an inherited design with
less stringent imaging requirements, is not thermally isolated.
The electronics for each camera are identical. All ISS command and
telemetry functions will be handled by the electronics, including receipt
of commands from the Command and Data Subsystem, expansion of commands, and
collection and transmission of imaging data and telemetry to the CDS.
The ISS controls the amount of power it draws from the spacecraft during
operations. To accomplish this, the profile of ISS command timing is
structured to reduce the power the ISS requires for certain internal
functions (e.g., shutter or filter wheel movement). When the filter is
moving, the power from the optical heater (if present) in the active camera
is turned off. When the movement is complete, the optical heater is turned
on (if needed). In addition, simultaneous filter positioning within a
single camera, either the WAC or NAC, is not permitted.
During the cruise phase of the mission, the cameras will periodically be
turned on for maintenance, calibration, and monitoring of instrument health
and performance. Other than these specified times, the ISS will be off and
replacement heaters will be on. In addition, decontamination/radiation
heater 1 will be on throughout most of the cruise.
Upon arrival at the Saturnian system, the cameras will be on most of the
time. Spacecraft power limitations will be the controlling parameter
determining whether the ISS will be turned off or put into a low-power
state. During the Saturn tour, high-activity periods for Saturn and its
rings will be clustered around periapsis (the point in the orbit closest to
the planet); for the satellites, the high-activity periods will be when the
spacecraft is closest to them. At these times, high-resolution images of
all targets will be acquired through various camera filters, and the data
will be stored in the spacecraft solid-state recorder (SSR). During lower
activity periods (i.e., when the spacecraft is orbiting farther from the
targets), long-term atmospheric and ring monitoring will take place, and
ISS calibrations will be performed.''
ISS First Order Optical Parameters
----------------------------------------------------------
The first order optical parameters for the two cameras that constitute the
ISS detectors:
-- Narrow Angle Camera
-- Wide Angle Camera
as provided by [4 and 9] for the CL filter combinations compiled into the
following table:
------------------------------ ----------- -----------
parameter NAC WAC
------------------------------ ----------- -----------
Effective Focal Length, mm 2003.44 200.77
Estimated Uncertainty, mm 0.03 0.01
Spectral Band, nm 200-1100 400-1000
F/number 10.5 3.44
------------------------------ ----------- -----------
These values are given in the keywords below in the same units as the table
above:
Narrow Angle Camera (NAC):
\begindata
INS-82360_FOCAL_LENGTH = ( 2003.44 )
INS-82360_FL_UNCERTAINTY = ( 0.03 )
INS-82360_WAVELENGTH_RANGE = ( 200, 1100 )
INS-82360_F/NUMBER = ( 10.5 )
\begintext
Wide Angle Camera (WAC):
\begindata
INS-82361_FOCAL_LENGTH = ( 200.77 )
INS-82361_FL_UNCERTAINTY = ( 0.01 )
INS-82361_WAVELENGTH_RANGE = ( 400, 1000 )
INS-82361_F/NUMBER = ( 3.44 )
\begintext
ISS Field of View Parameters
----------------------------------------------------------
FOV Sizes (in degrees)
Spacecraft Frame:
Xsc
^
|
|
o----->
Ysc Zsc
^
Samples | Ycm
(0,0) + + + > |
+ \_____________________________________________________ ___
+ | | |
+ | Samples | |
V | (0,0) + + + > | |
Lines | + \_____________________________ ___ | |
| + | | | | |
| + | | | | |
| V | | | | |
| Lines | | | | |
| | | | | |
<--- | | x | 0.35 | 3.4
Xcm | | Zcm | | | |
| | | | | |
| | | | | |
| | | | | |
| |_____________________________| _|_ | |
| NAC | |
| |------------0.35-------------| | |
| | |
|_____________________________________________________| _|_
WAC
|------------------------3.48-------------------------|
Note that although the above diagram suggests that the NAC and WAC
boresights are co-aligned, this is not the case. As [1] and [2] point out,
the NAC and WAC are both mounted to the Remote Sensing Palette in different
locations. From [6], the CCD samples and lines increase with decreasing X
and Y in the NAC and WAC frames. Thus the (0,0) corners of the CCDs are as
illustrated on the diagram above.
The FOVs of the ISS detectors have the following angular sizes (from [4]):
------------ ---------------- ----------------
Detector Horizontal Vertical
------------ ---------------- ----------------
NAC 0.35 degrees 0.35 degrees
WAC 3.48 degrees 3.48 degrees
------------ ---------------- ----------------
The CCD geometry parameters as presented in [1] and [4] are provided below:
------------------------------ ----------- -----------
parameter NAC WAC
------------------------------ ----------- -----------
Detector Array Size 1024x1024 1024x1024
Pixel Size, microns 12x12 12x12
FOV Angular Size, degrees 0.35x0.35 3.48x3.48
IFOV, microradian/pixel 6 60
------------------------------ ----------- -----------
With the keywords and their values:
Narrow Angle Camera (NAC):
\begindata
INS-82360_PIXEL_SAMPLES = ( 1024 )
INS-82360_PIXEL_LINES = ( 1024 )
INS-82360_PIXEL_SIZE = ( 12 )
INS-82360_CCD_CENTER = ( 512.5, 512.5 )
INS-82360_IFOV = ( 6 )
\begintext
Wide Angle Camera (WAC):
\begindata
INS-82361_PIXEL_SAMPLES = ( 1024 )
INS-82361_PIXEL_LINES = ( 1024 )
INS-82361_PIXEL_SIZE = ( 12 )
INS-82361_CCD_CENTER = ( 512.5, 512.5 )
INS-82361_IFOV = ( 60 )
\begintext
The keywords INS[ID]_FOV_FRAME, INS[ID]_FOV_SHAPE, INS[ID]_BORESIGHT, and
FOV ANGLES specification keywords defined below are used to describe the
instrument field of view. Since both the NAC and WAC have square fields of
view, the INS[ID]_FOV_SHAPE keyword will always be 'RECTANGLE', and GETFOV
will return the four vectors in the instrument frame that describe the
edges of the FOV cone. Both the NAC and WAC boresights lie along the
Z-axis.
Narrow Angle Camera (NAC) FOV Definition
Since the NAC's angular separation is 0.35 degrees, looking up the Y-axis
in the CASSINI_ISS_NAC frame we have: (Note we are arbitrarily choosing
vectors that terminate in the Z=1 plane.)
^ X
| ins
|
| /|
| / |
| / |
| / o |
|/ 0.175 |
o--------------->
Y \ | Z
ins \ | ins
\ |
\ |
\|
|-- 1.0 --|
Plane Y = 0
Now from here we see that the X components of the boundary corners are:
X Component = +/- 1.0 * tan ( 0.175 degrees ) = +/- 0.003054335689
Since the field of view is square this holds for the Y components as well.
These FOV values as well as the values from the preceding table are given
in the keywords below in the same units as listed above:
Narrow Angle Camera (NAC):
\begindata
INS-82360_FOV_FRAME = 'CASSINI_ISS_NAC'
INS-82360_FOV_SHAPE = 'RECTANGLE'
INS-82360_BORESIGHT = (
0.0000000000000000 0.0000000000000000 +1.0000000000000000
)
INS-82360_FOV_CLASS_SPEC = 'ANGLES'
INS-82360_FOV_REF_VECTOR = (
+1.0000000000000000 0.0000000000000000 0.0000000000000000
)
INS-82360_FOV_REF_ANGLE = ( 0.175 )
INS-82360_FOV_CROSS_ANGLE = ( 0.175 )
INS-82360_FOV_ANGLE_UNITS = 'DEGREES'
\begintext
Wide Angle Camera (WAC) FOV Definition
Since the WAC is also a square field of view, similar calculations as to
those made for the NAC hold. The half angle of interest is 1.74 degrees as
opposed to 0.175. Looking up the Y-axis in the CASSINI_ISS_WAC frame we
have: (Note we are arbitrarily choosing vectors that terminate in the Z=1
plane.)
^ X
| ins
|
| /|
| / |
| / |
| / o |
|/ 1.740 |
o--------------->
Y \ | Z
ins \ | ins
\ |
\ |
\|
|-- 1.0 --|
Plane Y = 0
Now from here we see that the X components of the boundary corners are:
X Component = +/- 1.0 * tan ( 1.74 degrees ) = +/- 0.030378068382
Since the field of view is square this holds for the Y components as well.
Again since the field of view is square this computation holds for the Y
components as well. All of these values are collected in the FOV keywords
defined below. Utilizing the ANGLES FOV specification:
Wide Angle Camera (WAC):
\begindata
INS-82361_FOV_FRAME = 'CASSINI_ISS_WAC'
INS-82361_FOV_SHAPE = 'RECTANGLE'
INS-82361_BORESIGHT = (
0.0000000000000000 0.0000000000000000 +1.0000000000000000
)
INS-82361_FOV_CLASS_SPEC = 'ANGLES'
INS-82361_FOV_REF_VECTOR = (
+1.0000000000000000 0.0000000000000000 0.0000000000000000
)
INS-82361_FOV_REF_ANGLE = ( 1.74 )
INS-82361_FOV_CROSS_ANGLE = ( 1.74 )
INS-82361_FOV_ANGLE_UNITS = 'DEGREES'
\begintext
ISS Radiator FOV Definitions
The FOV values for the ISS radiators are place holders until a time when
real values are provided.
Narrow Angle Camera Radiator (NAC_RAD):
\begindata
INS-82368_FOV_FRAME = 'CASSINI_ISS_NAC_RAD'
INS-82368_FOV_SHAPE = 'CIRCLE'
INS-82368_BORESIGHT = (
0.0000000000000000 0.0000000000000000 +1.0000000000000000
)
INS-82368_FOV_CLASS_SPEC = 'ANGLES'
INS-82368_FOV_REF_VECTOR = (
0.0000000000000000 +1.0000000000000000 +0.0000000000000001
)
INS-82368_FOV_REF_ANGLE = ( 90.0 )
INS-82368_FOV_ANGLE_UNITS = 'DEGREES'
\begintext
Wide Angle Camera Radiator (WAC_RAD):
\begindata
INS-82369_FOV_FRAME = 'CASSINI_ISS_WAC_RAD'
INS-82369_FOV_SHAPE = 'CIRCLE'
INS-82369_BORESIGHT = (
0.0000000000000000 0.0000000000000000 +1.0000000000000000
)
INS-82369_FOV_CLASS_SPEC = 'ANGLES'
INS-82369_FOV_REF_VECTOR = (
0.0000000000000000 +1.0000000000000000 +0.0000000000000001
)
INS-82369_FOV_REF_ANGLE = ( 90.0 )
INS-82369_FOV_ANGLE_UNITS = 'DEGREES'
\begintext
Pixel Parameters
----------------------------------------------------------
These parameters describe the pixel structure associated with the
instruments and their fields of views. In some cases this is a
generalization of the notion of pixel, in that instead of representing
pixels on a CCD they may represent a collection of individual detectors.
The FOV_CENTER_PIXEL keyword is precisely the same as the CCD_CENTER
defined in the CCD geometry keywords above.
Narrow Angle Camera (NAC)
\begindata
INS-82360_FOV_CENTER_PIXEL = ( 511.5, 511.5 )
\begintext
Wide Angle Camera (WAC)
\begindata
INS-82361_FOV_CENTER_PIXEL = ( 511.5, 511.5 )
\begintext
Narrow Angle Camera Radiator (NAC_RAD)
\begindata
INS-82368_FOV_CENTER_PIXEL = ( 0, 0 )
INS-82368_PIXEL_SAMPLES = ( 1 )
INS-82368_PIXEL_LINES = ( 1 )
\begintext
Wide Angle Camera Radiator (WAC_RAD)
\begindata
INS-82369_FOV_CENTER_PIXEL = ( 0, 0 )
INS-82369_PIXEL_SAMPLES = ( 1 )
INS-82369_PIXEL_LINES = ( 1 )
\begintext
Instrument Mode Timing
----------------------------------------------------------
The following values were provided as samples in [5]. The values are
defined in [5] as follows:
``The initial values for the following keywords are given
per instrument number:
INS[instrument number]_[instrument acronym]_MODE_NAME
INS[instrument number]_[instrument acronym]_TRIGGER_OFFSET
INS[instrument number]_[instrument acronym]_CYCLE_DURATION
INS..._MODE_NAME contains the name of the instrument mode for
the INS..._TRIGGER_OFFSET and INS..._CYCLE_DURATION
keywords.
INS..._TRIGGER_OFFSET specifies the reference time of the
first instrument frame (to be calculated for a footprint)
relative to the time of transacting the corresponding TRIGGER
command. The units are SFOC duration.
INS..._CYCLE_DURATION specifies the duration between successive
instrument frames (from the first one) for the INS..._MODE_NAME.''
NAC Mode Timing
The following values define the instrument modes and timing for the ISS
NAC.
\begindata
INS-82360_MODE_NAME = 'NOMINAL'
INS-82360_TRIGGER_OFFSET = '0:01:00.0'
INS-82360_CYCLE_DURATION = '0:01:00.0'
\begintext
WAC Mode Timing
The following values define the instrument modes and timing for the ISS
WAC.
\begindata
INS-82361_MODE_NAME = 'NOMINAL'
INS-82361_TRIGGER_OFFSET = '0:01:00.0'
INS-82361_CYCLE_DURATION = '0:01:00.0'
\begintext
NAIF ID Code to Name Mapping
----------------------------------------------------------
The following keywords define names for the corresponding ID Codes. See
[10] for details.
\begindata
NAIF_BODY_NAME += ( 'CASSINI_ISS_NAC' )
NAIF_BODY_CODE += ( -82360 )
NAIF_BODY_NAME += ( 'CASSINI_ISS_WAC' )
NAIF_BODY_CODE += ( -82361 )
NAIF_BODY_NAME += ( 'CASSINI_ISS_NAC_RAD' )
NAIF_BODY_CODE += ( -82368 )
NAIF_BODY_NAME += ( 'CASSINI_ISS_WAC_RAD' )
NAIF_BODY_CODE += ( -82369 )
\begintext
Platform ID
----------------------------------------------------------
The ISS instrument is mounted on the Remote Sensing Palette, which is
connected to the Cassini Spacecraft body. Therefore the value in the
keywords below is -82000.
\begindata
INS-82360_PLATFORM_ID = ( -82000 )
INS-82361_PLATFORM_ID = ( -82000 )
INS-82368_PLATFORM_ID = ( -82000 )
INS-82369_PLATFORM_ID = ( -82000 )
\begintext

View File

@@ -9,5 +9,6 @@ KPL/MK
'030201AP_SK_SM546_T45.bsp'
'cas_v37.tf',
'04135_04171pc_psiv2.bc',
'cpck05Mar2004.tpc')
'cpck05Mar2004.tpc',
'cas_iss_v09.ti')
\begintext

View File

@@ -32,9 +32,14 @@
#include <ghoul/lua/ghoul_lua.h>
#include <openspace/tests/test_common.inl>
#include <openspace/tests/test_scenegraph.inl>
#include <openspace/tests/test_powerscalecoordinates.inl>
#include <openspace/tests/test_spicemanager.inl>
//#include <openspace/tests/test_scenegraph.inl>
//#include <openspace/tests/test_powerscalecoordinates.inl>
#include <openspace/engine/openspaceengine.h>
#include <openspace/util/constants.h>
#include <openspace/util/factorymanager.h>
#include <openspace/util/spice.h>
#include <openspace/util/time.h>
#include <iostream>
@@ -57,6 +62,7 @@ int main(int argc, char** argv) {
LFATAL("Could not find OpenSpace configuration file!");
assert(false);
}
LINFO("Configuration file found: " << FileSys.absolutePath(configurationFilePath));
LDEBUG("registering base path");
if( ! openspace::OpenSpaceEngine::registerBasePathFromConfigurationFile(configurationFilePath)) {
@@ -66,16 +72,16 @@ int main(int argc, char** argv) {
ghoul::Dictionary configuration;
ghoul::lua::loadDictionaryFromFile(configurationFilePath, configuration);
if(configuration.hasKey("paths")) {
if (configuration.hasKey(openspace::constants::openspaceengine::keyPaths)) {
ghoul::Dictionary pathsDictionary;
if(configuration.getValue("paths", pathsDictionary)) {
if (configuration.getValue(openspace::constants::openspaceengine::keyPaths, pathsDictionary)) {
openspace::OpenSpaceEngine::registerPathsFromDictionary(pathsDictionary);
}
}
openspace::Time::init();
/*openspace::Time::init();
openspace::Spice::init();
openspace::Spice::ref().loadDefaultKernels();
openspace::Spice::ref().loadDefaultKernels();*/
openspace::FactoryManager::initialize();
testing::InitGoogleTest(&argc, argv);

View File

@@ -32,8 +32,6 @@
#include "openspace/util/spicemanager.h"
#include "ghoul/filesystem/filesystem.h"
#define BUFSIZE 64
namespace {
const std::string _loggerCat = "SpiceManager";
}
@@ -63,8 +61,7 @@ SpiceManager& SpiceManager::ref() {
}
int SpiceManager::loadKernel(const std::string& fullPath, const std::string& shorthand){
unsigned int kernelId = ++kernelCount;
unsigned int kernelId = ++_kernelCount;
assert(kernelId > 0);
std::string currentDirectory = FileSys.currentDirectory();
@@ -77,8 +74,8 @@ int SpiceManager::loadKernel(const std::string& fullPath, const std::string& sho
FileSys.setCurrentDirectory(currentDirectory);
spiceKernel current = { fullPath,
shorthand,
kernelId };
shorthand,
kernelId };
_loadedKernels.push_back(current);
@@ -118,8 +115,8 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValue,
double& value) const{
int n;
SpiceInt code;
SpiceBoolean found;
int code;
int found;
bodn2c_c(bodyname.c_str(), &code, &found);
if (!found) return false;
@@ -133,9 +130,9 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec2& value) const{
int n;
SpiceDouble val[2];
SpiceInt code;
SpiceBoolean found;
double val[2];
int code;
int found;
bodn2c_c(bodyname.c_str(), &code, &found);
if (!found) return false;
@@ -153,17 +150,15 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec3& value) const{
int n;
SpiceDouble val[3];
SpiceInt code;
SpiceBoolean found;
double val[3];
int code;
int found;
bodn2c_c(bodyname.c_str(), &code, &found);
if (!found) return false;
bodvrd_c(bodyname.c_str(), kernelPoolValueName.c_str(), 3, &n, val);
value[0] = val[0];
value[1] = val[1];
value[2] = val[2];
memcpy(&value, val, sizeof(double)* 3);
return true;
}
@@ -173,9 +168,9 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
glm::dvec4& value) const{
int n;
SpiceDouble val[4];
SpiceInt code;
SpiceBoolean found;
double val[4];
int code;
int found;
bodn2c_c(bodyname.c_str(), &code, &found);
if (!found) return false;
@@ -192,12 +187,13 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
// ND
bool SpiceManager::getValueFromID(const std::string& bodyname,
const std::string& kernelPoolValueName,
std::vector<double>& values, unsigned int num) const{
SpiceInt n;
SpiceDouble *val;
val = (SpiceDouble*)malloc(num*sizeof(SpiceDouble));
SpiceInt code;
SpiceBoolean found;
std::vector<double>& values,
unsigned int num) const{
int n;
double *val;
val = (double*)malloc(num*sizeof(double));
int code;
int found;
bodn2c_c(bodyname.c_str(), &code, &found);
if (!found) return false;
@@ -211,7 +207,7 @@ bool SpiceManager::getValueFromID(const std::string& bodyname,
}
double SpiceManager::stringToEphemerisTime(const std::string& epochString) const{
SpiceDouble et;
double et;
str2et_c(epochString.c_str(), &et);
return et;
}
@@ -224,17 +220,14 @@ bool SpiceManager::getTargetPosition(const std::string& target,
glm::dvec3& targetPosition,
double lightTime) const{
double pos[3] = { NULL, NULL, NULL };
//method to put error out...
spkpos_c(target.c_str(), ephemerisTime, referenceFrame.c_str(),
aberrationCorrection.c_str(), observer.c_str(), pos, &lightTime);
if (pos[0] == NULL ||
pos[1] == NULL ||
pos[2] == NULL)
if (pos[0] == NULL || pos[1] == NULL || pos[2] == NULL)
return false;
targetPosition[0] = pos[0];
targetPosition[1] = pos[1];
targetPosition[2] = pos[2];
memcpy(&targetPosition, pos, sizeof(double)* 3);
return true;
}
@@ -250,14 +243,14 @@ bool SpiceManager::getTargetState(const std::string& target,
std::fill_n(state, 6, NULL);
spkezr_c(target.c_str(), ephemerisTime, referenceFrame.c_str(),
aberrationCorrection.c_str(), observer.c_str(), state, &lightTime);
aberrationCorrection.c_str(), observer.c_str(), state, &lightTime);
for (int i = 0; i < 3; i++){
if (state[i] == NULL || state[i + 3] == NULL){
return false;
}
targetPosition[i] = state[i];
targetVelocity[i] = state[i+3];
memcpy(&targetPosition, state , sizeof(double)* 3);
memcpy(&targetVelocity, state +3, sizeof(double)* 3);
}
return true;
}
@@ -265,12 +258,152 @@ bool SpiceManager::getTargetState(const std::string& target,
bool SpiceManager::getStateTransformMatrix(const std::string& fromFrame,
const std::string& toFrame,
double ephemerisTime,
mat6x6& stateMatrix) const{
transformMatrix& stateMatrix) const{
sxform_c(fromFrame.c_str(), toFrame.c_str(),
ephemerisTime, (double(*)[6])stateMatrix.ptr());
return true;
}
bool SpiceManager::getPositionTransformMatrix(const std::string& fromFrame,
const std::string& toFrame,
double ephemerisTime,
transformMatrix& positionMatrix) const{
pxform_c(fromFrame.c_str(), toFrame.c_str(),
ephemerisTime, (double(*)[3])positionMatrix.ptr());
sxform_c(fromFrame.c_str(), toFrame.c_str(), ephemerisTime, stateMatrix);
//error handling?
return true;
}
bool SpiceManager::getFieldOfView(const std::string& naifInstrumentId,
std::string& fovShape,
std::string& frameName,
double boresightVector[],
std::vector<glm::dvec3>& bounds,
int& nrReturned) const{
int n;
int found;
int naifId;
int maxVectors = 12;
double *boundsArr = new double[maxVectors * 3];
for (int i = 0; i < maxVectors; i++){
for (int j = 0; j < 3; j++){
boundsArr[j + i*3] = NULL;
}
}
bodn2c_c(naifInstrumentId.c_str(), &naifId, &found);
if (!found) return false;
if (fovShape.size() != 0 && frameName.size() != 0){
getfov_c(naifId,
maxVectors,
fovShape.size(),
frameName.size(),
const_cast<char*>(fovShape.c_str()),
const_cast<char*>(frameName.c_str()),
boresightVector,
&nrReturned,
(double(*)[3])boundsArr);
}else{
std::cout << "Frame name and FOV shape \
need to be preallocated" << std::endl;
return false;
}
for (int i = 0; i < nrReturned; i++){
glm::dvec3 tmp;
for (int j = 0; j < 3; j++){
tmp[j] = boundsArr[j + i*3];
}
bounds.push_back(tmp);
}
return true;
}
bool SpiceManager::rectangularToLatitudal(const glm::dvec3 coordinates,
double& radius,
double& longitude,
double& latitude) const{
double point[3] = { coordinates.x, coordinates.y, coordinates.z };
reclat_c(point, &radius, &longitude, &latitude);
//check if returns values
return (radius && longitude && latitude);
}
bool SpiceManager::latidudinalToRectangular(double radius,
double& longitude,
double& latitude,
glm::dvec3& coordinates) const{
double point[3] = { coordinates.x, coordinates.y, coordinates.z };
latrec_c(radius, longitude, latitude, point);
//check if returns values
return (radius && longitude && latitude);
}
bool SpiceManager::planetocentricToRectangular(const std::string& naifName,
double& longitude,
double& latitude,
glm::dvec3& coordinates) const{
int naifId;
int found;
double rectangular[3];
bodn2c_c(naifName.c_str(), &naifId, &found);
if (!found) return false;
srfrec_c(naifId, longitude*rpd_c(), latitude*rpd_c(), rectangular);
memcpy(&coordinates, rectangular, sizeof(double) * 3);
return true;
}
bool SpiceManager::getSubObserverPoint(std::string computationMethod,
std::string target,
double ephemeris,
std::string bodyFixedFrame,
std::string aberrationCorrection,
std::string observer,
glm::dvec3& subObserverPoint,
double& targetEpoch,
glm::dvec3& vectorToSurfacePoint) const{
double subPoint[3], vecToSurf[3];
subpnt_c(computationMethod.c_str(),
target.c_str(),
ephemeris,
bodyFixedFrame.c_str(),
aberrationCorrection.c_str(),
observer.c_str(), subPoint, &targetEpoch, vecToSurf);
memcpy(&subObserverPoint , subPoint , sizeof(double) * 3);
memcpy(&vectorToSurfacePoint, vecToSurf, sizeof(double) * 3);
return true;
}
bool SpiceManager::getSubSolarPoint(std::string computationMethod,
std::string target,
double ephemeris,
std::string bodyFixedFrame,
std::string aberrationCorrection,
std::string observer,
glm::dvec3& subSolarPoint,
double& targetEpoch,
glm::dvec3& vectorToSurfacePoint) const{
double subPoint[3], vecToSurf[3];
subslr_c(computationMethod.c_str(),
target.c_str(),
ephemeris,
bodyFixedFrame.c_str(),
aberrationCorrection.c_str(),
observer.c_str(), subPoint, &targetEpoch, vecToSurf);
memcpy(&subSolarPoint, subPoint, sizeof(double)* 3);
memcpy(&vectorToSurfacePoint, vecToSurf, sizeof(double)* 3);
return true;
}
}