15 #include <getSpkAbCorrState.hpp> 
   19 #include <nlohmann/json.hpp> 
   20 using json = nlohmann::json;
 
   23 #include "Constants.h" 
   25 #include "EllipsoidShape.h" 
   26 #include "EndianSwapper.h" 
   28 #include "IException.h" 
   31 #include "Longitude.h" 
   32 #include "LightTimeCorrectionState.h" 
   33 #include "NaifStatus.h" 
   34 #include "ShapeModel.h" 
   35 #include "SpacecraftPosition.h" 
   66     if (cube.
hasBlob(
"CSMState", 
"String")) {
 
   71       bool hasTables = (kernels[
"TargetPosition"][0] == 
"Table");
 
   73       init(lab, !hasTables);
 
   84   Spice::Spice(
Pvl &lab, json isd) {
 
   97   void Spice::csmInit(
Cube &cube, 
Pvl label) {
 
  100     NaifStatus::CheckErrors();
 
  107   void Spice::defaultInit() {
 
  113     m_startTime = 
new iTime;
 
  114     m_endTime = 
new iTime;
 
  115     m_cacheSize = 
new SpiceDouble;
 
  118     m_startTimePadding = 
new SpiceDouble;
 
  119     *m_startTimePadding = 0;
 
  120     m_endTimePadding = 
new SpiceDouble;
 
  121     *m_endTimePadding = 0;
 
  123     m_instrumentPosition = 
nullptr;
 
  124     m_instrumentRotation = 
nullptr;
 
  126     m_sunPosition = 
nullptr;
 
  127     m_bodyRotation = 
nullptr;
 
  129     m_allowDownsizing = 
false;
 
  134     m_sclkCode = 
nullptr;
 
  135     m_spkBodyCode = 
nullptr;
 
  136     m_bodyFrameCode = 
nullptr;
 
  154   void Spice::init(
Pvl &lab, 
bool noTables, json isd) {
 
  155     NaifStatus::CheckErrors();
 
  159     m_spkCode = 
new SpiceInt;
 
  160     m_ckCode = 
new SpiceInt;
 
  161     m_ikCode = 
new SpiceInt;
 
  162     m_sclkCode = 
new SpiceInt;
 
  163     m_spkBodyCode = 
new SpiceInt;
 
  164     m_bodyFrameCode = 
new SpiceInt;
 
  166     m_naifKeywords = 
new PvlObject(
"NaifKeywords");
 
  174       *m_startTimePadding = 
toDouble(kernels[
"StartPadding"][0]);
 
  177       *m_startTimePadding = 0.0;
 
  181       *m_endTimePadding  = 
toDouble(kernels[
"EndPadding"][0]);
 
  184       *m_endTimePadding = 0.0;
 
  188     m_usingNaif = !lab.
hasObject(
"NaifKeywords") || noTables;
 
  199         if (kernels[
"InstrumentPointing"][0].toUpper() == 
"NADIR") {
 
  200           QString msg = 
"Falling back to ISIS generation of nadir pointing";
 
  201           throw IException(IException::Programmer, msg, _FILEINFO_);
 
  206           std::ostringstream kernel_pvl;
 
  207           kernel_pvl << kernels;
 
  210           props[
"kernels"] = kernel_pvl.str();
 
  212           isd = ale::load(lab.
fileName().toStdString(), props.dump(), 
"ale");
 
  215         json aleNaifKeywords = isd[
"naif_keywords"];
 
  216         m_naifKeywords = 
new PvlObject(
"NaifKeywords", aleNaifKeywords);
 
  219         load(kernels[
"LeapSecond"], noTables);
 
  221           load(kernels[
"SpacecraftClock"], noTables);
 
  228           load(kernels[
"TargetPosition"], noTables);
 
  229           load(kernels[
"InstrumentPosition"], noTables);
 
  230           load(kernels[
"InstrumentPointing"], noTables);
 
  234           load(kernels[
"Frame"], noTables);
 
  237         load(kernels[
"TargetAttitudeShape"], noTables);
 
  239           load(kernels[
"Instrument"], noTables);
 
  242         if (kernels.
hasKeyword(
"InstrumentAddendum")) {
 
  243           load(kernels[
"InstrumentAddendum"], noTables);
 
  247         load(kernels[
"LeapSecond"], noTables);
 
  249           load(kernels[
"SpacecraftClock"], noTables);
 
  255           load(kernels[
"Extra"], noTables);
 
  263       m_target = 
new Target(
this, lab);
 
  270       if (m_target->name().toUpper() == 
"SATURN" && m_target->shape()->name().toUpper() == 
"PLANE") {
 
  272         load(ringPck, noTables);
 
  276       *m_naifKeywords = lab.
findObject(
"NaifKeywords");
 
  283       m_target = 
new Target(
this, lab);
 
  301     QString trykey = 
"NaifIkCode";
 
  302     if (kernels.
hasKeyword(
"NaifFrameCode")) trykey = 
"NaifFrameCode";
 
  303     *m_ikCode = 
toInt(kernels[trykey][0]);
 
  305     *m_spkCode  = *m_ikCode / 1000;
 
  306     *m_sclkCode = *m_spkCode;
 
  307     *m_ckCode   = *m_ikCode;
 
  309     if (!m_target->isSky()) {
 
  313       QString radiiKey = 
"BODY" + 
Isis::toString(m_target->naifBodyCode()) + 
"_RADII";
 
  314       QVariant result = m_target->naifBodyCode();
 
  315       storeValue(
"BODY_CODE", 0, SpiceIntType, result);
 
  316       std::vector<Distance> radii(3,
Distance());
 
  317       radii[0] = 
Distance(getDouble(radiiKey, 0), Distance::Kilometers);
 
  318       radii[1] = 
Distance(getDouble(radiiKey, 1), Distance::Kilometers);
 
  319       radii[2] = 
Distance(getDouble(radiiKey, 2), Distance::Kilometers);
 
  321       m_target->setRadii(radii);
 
  324     *m_spkBodyCode = m_target->naifBodyCode();
 
  328       *m_spkCode = (int) kernels[
"NaifSpkCode"];
 
  332       *m_ckCode = (int) kernels[
"NaifCkCode"];
 
  336       *m_sclkCode = (int) kernels[
"NaifSclkCode"];
 
  339     if (!m_target->isSky()) {
 
  341         *m_spkBodyCode = (int) kernels[
"NaifSpkBodyCode"];
 
  345     if (m_target->isSky()) {
 
  353       if ((m_usingNaif) || (!m_naifKeywords->hasKeyword(
"BODY_FRAME_CODE"))) {
 
  356         cidfrm_c(*m_spkBodyCode, 
sizeof(frameName), &frameCode, frameName, &found);
 
  359           QString naifTarget = 
"IAU_" + m_target->name().toUpper();
 
  360           namfrm_c(naifTarget.toLatin1().data(), &frameCode);
 
  361           if (frameCode == 0) {
 
  362             QString msg = 
"Can not find NAIF BODY_FRAME_CODE for target [" 
  363                          + m_target->name() + 
"]";
 
  364             throw IException(IException::Io, msg, _FILEINFO_);
 
  368         QVariant result = (int)frameCode;
 
  369         storeValue(
"BODY_FRAME_CODE", 0, SpiceIntType, result);
 
  372         frameCode = getInteger(
"BODY_FRAME_CODE", 0);
 
  376       *m_bodyFrameCode = frameCode;
 
  386     vector<Distance> radius = m_target->radii();
 
  387     Distance targetRadius((radius[0] + radius[2])/2.0);
 
  389                                                   ltState, targetRadius);
 
  391     m_sunPosition = 
new SpicePosition(10, m_target->naifBodyCode());
 
  397       m_sunPosition->LoadCache(isd[
"sun_position"]);
 
  398       if (m_sunPosition->cacheSize() > 3) {
 
  399         m_sunPosition->Memcache2HermiteCache(0.01);
 
  401       m_bodyRotation->LoadCache(isd[
"body_rotation"]);
 
  402       m_bodyRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes);
 
  403       if (m_bodyRotation->cacheSize() > 5) {
 
  404         m_bodyRotation->LoadTimeCache();
 
  408     else if (kernels[
"TargetPosition"][0].toUpper() == 
"TABLE") {
 
  410       m_sunPosition->LoadCache(t);
 
  413       m_bodyRotation->LoadCache(t2);
 
  429     if (kernels[
"InstrumentPointing"].size() == 0) {
 
  431                        "No camera pointing available",
 
  437     if (kernels[
"InstrumentPointing"][0].toUpper() == 
"NADIR") {
 
  438       if (m_instrumentRotation) {
 
  439         delete m_instrumentRotation;
 
  440         m_instrumentRotation = NULL;
 
  443       m_instrumentRotation = 
new SpiceRotation(*m_ikCode, *m_spkBodyCode);
 
  445     else if (m_usingAle) {
 
  446      m_instrumentRotation->LoadCache(isd[
"instrument_pointing"]);
 
  447      m_instrumentRotation->MinimizeCache(SpiceRotation::DownsizeStatus::Yes);
 
  448      if (m_instrumentRotation->cacheSize() > 5) {
 
  449        m_instrumentRotation->LoadTimeCache();
 
  452     else if (kernels[
"InstrumentPointing"][0].toUpper() == 
"TABLE") {
 
  454       m_instrumentRotation->LoadCache(t);
 
  458     if (kernels[
"InstrumentPosition"].size() == 0) {
 
  460                        "No instrument position available",
 
  465       m_instrumentPosition->LoadCache(isd[
"instrument_position"]);
 
  466       if (m_instrumentPosition->cacheSize() > 3) {
 
  467         m_instrumentPosition->Memcache2HermiteCache(0.01);
 
  470     else if (kernels[
"InstrumentPosition"][0].toUpper() == 
"TABLE") {
 
  472       m_instrumentPosition->LoadCache(t);
 
  474     NaifStatus::CheckErrors();
 
  487     NaifStatus::CheckErrors();
 
  489     for (
int i = 0; i < key.
size(); i++) {
 
  490       if (key[i] == 
"") 
continue;
 
  491       if (key[i].toUpper() == 
"NULL") 
break;
 
  492       if (key[i].toUpper() == 
"NADIR") 
break;
 
  493       if (key[i].toUpper() == 
"TABLE" && !noTables) 
break;
 
  494       if (key[i].toUpper() == 
"TABLE" && noTables) 
continue;
 
  497         QString msg = 
"Spice file does not exist [" + file.
expanded() + 
"]";
 
  498         throw IException(IException::Io, msg, _FILEINFO_);
 
  501       furnsh_c(fileName.toLatin1().data());
 
  502       m_kernels->push_back(key[i]);
 
  505     NaifStatus::CheckErrors();
 
  512     NaifStatus::CheckErrors();
 
  514     if (m_solarLongitude != NULL) {
 
  515       delete m_solarLongitude;
 
  516       m_solarLongitude = NULL;
 
  524     if (m_startTime != NULL) {
 
  529     if (m_endTime != NULL) {
 
  534     if (m_cacheSize != NULL) {
 
  539     if (m_startTimePadding != NULL) {
 
  540       delete m_startTimePadding;
 
  541       m_startTimePadding = NULL;
 
  544     if (m_endTimePadding != NULL) {
 
  545       delete m_endTimePadding;
 
  546       m_endTimePadding = NULL;
 
  549     if (m_instrumentPosition != NULL) {
 
  550       delete m_instrumentPosition;
 
  551       m_instrumentPosition = NULL;
 
  554     if (m_instrumentRotation != NULL) {
 
  555       delete m_instrumentRotation;
 
  556       m_instrumentRotation = NULL;
 
  559     if (m_sunPosition != NULL) {
 
  560       delete m_sunPosition;
 
  561       m_sunPosition = NULL;
 
  564     if (m_bodyRotation != NULL) {
 
  565       delete m_bodyRotation;
 
  566       m_bodyRotation = NULL;
 
  569     if (m_spkCode != NULL) {
 
  574     if (m_ckCode != NULL) {
 
  579     if (m_ikCode != NULL) {
 
  584     if (m_sclkCode != NULL) {
 
  589     if (m_spkBodyCode != NULL) {
 
  590       delete m_spkBodyCode;
 
  591       m_spkBodyCode = NULL;
 
  594     if (m_bodyFrameCode != NULL) {
 
  595       delete m_bodyFrameCode;
 
  596       m_bodyFrameCode = NULL;
 
  599     if (m_target != NULL) {
 
  605     for (
int i = 0; m_kernels && i < m_kernels->size(); i++) {
 
  608       unload_c(fileName.toLatin1().data());
 
  611     if (m_kernels != NULL) {
 
  615     NaifStatus::CheckErrors();
 
  650       int cacheSize, 
double tol) {
 
  651     NaifStatus::CheckErrors();
 
  654     if (cacheSize <= 0) {
 
  655       QString msg = 
"Argument cacheSize must be greater than zero";
 
  656       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  659     if (startTime > endTime) {
 
  660       QString msg = 
"Argument startTime must be less than or equal to endTime";
 
  661       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  664     if (*m_cacheSize > 0) {
 
  665       QString msg = 
"A cache has already been created";
 
  666       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  669     if (cacheSize == 1 && (*m_startTimePadding != 0 || *m_endTimePadding != 0)) {
 
  670       QString msg = 
"This instrument does not support time padding";
 
  671       throw IException(IException::User, msg, _FILEINFO_);
 
  675     if (getSpkAbCorrState(abcorr)) {
 
  676       instrumentPosition()->SetAberrationCorrection(
"NONE");
 
  679     iTime avgTime((startTime.
Et() + endTime.
Et()) / 2.0);
 
  680     computeSolarLongitude(avgTime);
 
  683     if (!m_bodyRotation->IsCached()) {
 
  684       int bodyRotationCacheSize = cacheSize;
 
  685       if (cacheSize > 2) bodyRotationCacheSize = 2;
 
  686       m_bodyRotation->LoadCache(
 
  687           startTime.
Et() - *m_startTimePadding,
 
  688           endTime.
Et() + *m_endTimePadding,
 
  689           bodyRotationCacheSize);
 
  692     if (m_instrumentRotation->GetSource() < SpiceRotation::Memcache) {
 
  693       if (cacheSize > 3) m_instrumentRotation->MinimizeCache(SpiceRotation::Yes);
 
  694       m_instrumentRotation->LoadCache(
 
  695           startTime.
Et() - *m_startTimePadding,
 
  696           endTime.
Et() + *m_endTimePadding,
 
  700     if (m_instrumentPosition->GetSource() < SpicePosition::Memcache) {
 
  701       m_instrumentPosition->LoadCache(
 
  702           startTime.
Et() - *m_startTimePadding,
 
  703           endTime.
Et() + *m_endTimePadding,
 
  705       if (cacheSize > 3) m_instrumentPosition->Memcache2HermiteCache(tol);
 
  708     if (!m_sunPosition->IsCached()) {
 
  709       int sunPositionCacheSize = cacheSize;
 
  710       if (cacheSize > 2) sunPositionCacheSize = 2;
 
  711       m_sunPosition->LoadCache(
 
  712           startTime.
Et() - *m_startTimePadding,
 
  713           endTime.
Et() + *m_endTimePadding,
 
  714           sunPositionCacheSize);
 
  718     *m_startTime = startTime;
 
  719     *m_endTime = endTime;
 
  720     *m_cacheSize = cacheSize;
 
  724     for (
int i = 0; i < m_kernels->size(); i++) {
 
  727       unload_c(fileName.toLatin1().data());
 
  732     NaifStatus::CheckErrors();
 
  743   iTime Spice::cacheStartTime()
 const {
 
  780   void Spice::setTime(
const iTime &et) {
 
  790       if (*m_cacheSize == 0) {
 
  791         if (m_startTime->Et() == 0.0 && m_endTime->Et() == 0.0
 
  792             && getSpkAbCorrState(abcorr)) {
 
  793           instrumentPosition()->SetAberrationCorrection(
"NONE");
 
  800     m_bodyRotation->SetEphemerisTime(et.
Et());
 
  801     m_instrumentRotation->SetEphemerisTime(et.
Et());
 
  802     m_instrumentPosition->SetEphemerisTime(et.
Et());
 
  803     m_sunPosition->SetEphemerisTime(et.
Et());
 
  805     std::vector<double> uB = m_bodyRotation->ReferenceVector(m_sunPosition->Coordinate());
 
  810     computeSolarLongitude(*m_et);
 
  822   void Spice::instrumentPosition(
double p[3])
 const {
 
  823     instrumentBodyFixedPosition(p);
 
  835   void Spice::instrumentBodyFixedPosition(
double p[3])
 const {
 
  837       QString msg = 
"Unable to retrieve instrument's body fixed position." 
  838                     " Spice::SetTime must be called first.";
 
  839       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  842     std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
 
  853   void Spice::instrumentBodyFixedVelocity(
double v[3])
 const {
 
  855       QString msg = 
"Unable to retrieve instrument's body fixed velocity." 
  856                     " Spice::SetTime must be called first.";
 
  857       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  860     std::vector<double> state;
 
  861     state.push_back(m_instrumentPosition->Coordinate()[0]);
 
  862     state.push_back(m_instrumentPosition->Coordinate()[1]);
 
  863     state.push_back(m_instrumentPosition->Coordinate()[2]);
 
  864     state.push_back(m_instrumentPosition->Velocity()[0]);
 
  865     state.push_back(m_instrumentPosition->Velocity()[1]);
 
  866     state.push_back(m_instrumentPosition->Velocity()[2]);
 
  868     std::vector<double> vB = m_bodyRotation->ReferenceVector(state);
 
  886       QString msg = 
"Unable to retrieve the time." 
  887                     " Spice::SetTime must be called first.";
 
  888       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  902   void Spice::sunPosition(
double p[3])
 const {
 
  904       QString msg = 
"Unable to retrieve sun's position." 
  905                     " Spice::SetTime must be called first.";
 
  906       throw IException(IException::Programmer, msg, _FILEINFO_);
 
  918   double Spice::targetCenterDistance()
 const {
 
  919     std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
 
  920     return sqrt(pow(sB[0], 2) + pow(sB[1], 2) + pow(sB[2], 2));
 
  931     for (
int i = 0; i < 3; i++)
 
  932        r[i] =m_target->radii()[i];
 
  941   SpiceInt Spice::naifBodyCode()
 const {
 
  942     return (
int) m_target->naifBodyCode();
 
  950   SpiceInt Spice::naifSpkCode()
 const {
 
  959   SpiceInt Spice::naifCkCode()
 const {
 
  968   SpiceInt Spice::naifIkCode()
 const {
 
  978   SpiceInt Spice::naifSclkCode()
 const {
 
  989   SpiceInt Spice::naifBodyFrameCode()
 const {
 
  990     return *m_bodyFrameCode;
 
  999     return *m_naifKeywords;
 
 1009   double Spice::resolution() {
 
 1025   SpiceInt Spice::getInteger(
const QString &key, 
int index) {
 
 1026     return readValue(key, SpiceIntType, index).toInt();
 
 1039   SpiceDouble Spice::getDouble(
const QString &key, 
int index) {
 
 1040     return readValue(key, SpiceDoubleType, index).toDouble();
 
 1053   iTime Spice::getClockTime(QString clockValue, 
int sclkCode, 
bool clockTicks) {
 
 1054     if (sclkCode == -1) {
 
 1055       sclkCode = naifSclkCode();
 
 1060     QString key = 
"CLOCK_ET_" + 
Isis::toString(sclkCode) + 
"_" + clockValue;
 
 1061     QVariant storedClockTime = getStoredResult(key, SpiceDoubleType);
 
 1063     if (storedClockTime.isNull()) {
 
 1064       SpiceDouble timeOutput;
 
 1065       NaifStatus::CheckErrors();
 
 1067         sct2e_c(sclkCode, (SpiceDouble) clockValue.toDouble(), &timeOutput);
 
 1070         scs2e_c(sclkCode, clockValue.toLatin1().data(), &timeOutput);
 
 1072       NaifStatus::CheckErrors();
 
 1073       storedClockTime = timeOutput;
 
 1074       storeResult(key, SpiceDoubleType, timeOutput);
 
 1077     result = storedClockTime.toDouble();
 
 1096     if (m_usingNaif && !m_usingAle) {
 
 1097       NaifStatus::CheckErrors();
 
 1100       SpiceBoolean found = 
false;
 
 1104       SpiceInt numValuesRead;
 
 1106       if (type == SpiceDoubleType) {
 
 1107         SpiceDouble kernelValue;
 
 1108         gdpool_c(key.toLatin1().data(), (SpiceInt)index, 1,
 
 1109                  &numValuesRead, &kernelValue, &found);
 
 1112           result = kernelValue;
 
 1114       else if (type == SpiceStringType) {
 
 1115         char kernelValue[512];
 
 1116         gcpool_c(key.toLatin1().data(), (SpiceInt)index, 1, 
sizeof(kernelValue),
 
 1117                  &numValuesRead, kernelValue, &found);
 
 1120           result = kernelValue;
 
 1122       else if (type == SpiceIntType) {
 
 1123         SpiceInt kernelValue;
 
 1124         gipool_c(key.toLatin1().data(), (SpiceInt)index, 1, &numValuesRead,
 
 1125                  &kernelValue, &found);
 
 1128           result = (int)kernelValue;
 
 1132         QString msg = 
"Can not find [" + key + 
"] in text kernels";
 
 1133         throw IException(IException::Io, msg, _FILEINFO_);
 
 1136       storeValue(key, index, type, result);
 
 1137       NaifStatus::CheckErrors();
 
 1141       result = readStoredValue(key, type, index);
 
 1143       if (result.isNull()) {
 
 1144         QString msg = 
"The camera is requesting spice data [" + key + 
"] that " 
 1145                       "was not attached, please re-run spiceinit";
 
 1146         throw IException(IException::Unknown, msg, _FILEINFO_);
 
 1154   void Spice::storeResult(QString name, SpiceValueType type, QVariant value) {
 
 1155     if (type == SpiceDoubleType) {
 
 1158       double doubleVal = value.toDouble();
 
 1159       doubleVal = swapper.Double(&doubleVal);
 
 1160       QByteArray byteCode((
char *) &doubleVal, 
sizeof(
double));
 
 1162       type = SpiceByteCodeType;
 
 1165     storeValue(name + 
"_COMPUTED", 0, type, value);
 
 1169   QVariant Spice::getStoredResult(QString name, SpiceValueType type) {
 
 1170     bool wasDouble = 
false;
 
 1172     if (type == SpiceDoubleType) {
 
 1174       type = SpiceByteCodeType;
 
 1177     QVariant stored = readStoredValue(name + 
"_COMPUTED", type, 0);
 
 1179     if (wasDouble && !stored.isNull()) {
 
 1180       EndianSwapper swapper(
"LSB");
 
 1181       double doubleVal = swapper.Double((
void *)QByteArray::fromHex(
 
 1182           stored.toByteArray()).data());
 
 1190   void Spice::storeValue(QString key, 
int index, SpiceValueType type,
 
 1192     if (!m_naifKeywords->hasKeyword(key)) {
 
 1193       m_naifKeywords->addKeyword(PvlKeyword(key));
 
 1196     PvlKeyword &storedKey = m_naifKeywords->findKeyword(key);
 
 1198     while(index >= storedKey.size()) {
 
 1199       storedKey.addValue(
"");
 
 1202     if (type == SpiceByteCodeType) {
 
 1203       storedKey[index] = QString(value.toByteArray().toHex().data());
 
 1205     else if (type == SpiceStringType) {
 
 1206       storedKey[index] = value.toString();
 
 1208     else if (type == SpiceDoubleType) {
 
 1209       storedKey[index] = 
toString(value.toDouble());
 
 1211     else if (type == SpiceIntType) {
 
 1212       storedKey[index] = 
toString(value.toInt());
 
 1215       QString msg = 
"Unable to store variant in labels for key [" + key + 
"]";
 
 1216       throw IException(IException::Unknown, msg, _FILEINFO_);
 
 1221   QVariant Spice::readStoredValue(QString key, SpiceValueType type,
 
 1226     if (m_naifKeywords->hasKeyword(key) && (!m_usingNaif || m_usingAle)) {
 
 1227       PvlKeyword &storedKeyword = m_naifKeywords->findKeyword(key);
 
 1230         if (type == SpiceDoubleType) {
 
 1231           result = 
toDouble(storedKeyword[index]);
 
 1233         else if (type == SpiceStringType) {
 
 1234           result = storedKeyword[index];
 
 1236         else if (type == SpiceByteCodeType || SpiceStringType) {
 
 1237           result = storedKeyword[index].toLatin1();
 
 1239         else if (type == SpiceIntType) {
 
 1240           result = 
toInt(storedKeyword[index]);
 
 1243       catch(IException &) {
 
 1261   QString Spice::getString(
const QString &key, 
int index) {
 
 1262     return readValue(key, SpiceStringType, index).toString();
 
 1278   void Spice::subSpacecraftPoint(
double &lat, 
double &lon) {
 
 1279     NaifStatus::CheckErrors();
 
 1282       QString msg = 
"Unable to retrieve subspacecraft position." 
 1283                     " Spice::SetTime must be called first.";
 
 1284       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1287     SpiceDouble usB[3], dist;
 
 1288     std::vector<double> vsB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
 
 1293     unorm_c(sB, usB, &dist);
 
 1295     std::vector<Distance> radii = target()->radii();
 
 1296     SpiceDouble a = radii[0].kilometers();
 
 1297     SpiceDouble b = radii[1].kilometers();
 
 1298     SpiceDouble c = radii[2].kilometers();
 
 1300     SpiceDouble originB[3];
 
 1301     originB[0] = originB[1] = originB[2] = 0.0;
 
 1304     SpiceDouble subB[3];
 
 1306     surfpt_c(originB, usB, a, b, c, subB, &found);
 
 1308     SpiceDouble mylon, mylat;
 
 1309     reclat_c(subB, &a, &mylon, &mylat);
 
 1310     lat = mylat * 180.0 / 
PI;
 
 1311     lon = mylon * 180.0 / 
PI;
 
 1312     if (lon < 0.0) lon += 360.0;
 
 1314     NaifStatus::CheckErrors();
 
 1329   void Spice::subSolarPoint(
double &lat, 
double &lon) {
 
 1330     NaifStatus::CheckErrors();
 
 1333       QString msg = 
"Unable to retrieve subsolar point." 
 1334                     " Spice::SetTime must be called first.";
 
 1335       throw IException(IException::Programmer, msg, _FILEINFO_);
 
 1338     SpiceDouble uuB[3], dist;
 
 1339     unorm_c(m_uB, uuB, &dist);
 
 1340     std::vector<Distance> radii = target()->radii();
 
 1342     SpiceDouble a = radii[0].kilometers();
 
 1343     SpiceDouble b = radii[1].kilometers();
 
 1344     SpiceDouble c = radii[2].kilometers();
 
 1346     SpiceDouble originB[3];
 
 1347     originB[0] = originB[1] = originB[2] = 0.0;
 
 1350     SpiceDouble subB[3];
 
 1351     surfpt_c(originB, uuB, a, b, c, subB, &found);
 
 1353     SpiceDouble mylon, mylat;
 
 1354     reclat_c(subB, &a, &mylon, &mylat);
 
 1356     lat = mylat * 180.0 / 
PI;
 
 1357     lon = mylon * 180.0 / 
PI;
 
 1358     if (lon < 0.0) lon += 360.0;
 
 1359     NaifStatus::CheckErrors();
 
 1378   QString Spice::targetName()
 const {
 
 1379     return m_target->
name();
 
 1383   double Spice::sunToBodyDist()
 const {
 
 1384     std::vector<double> sunPosition = m_sunPosition->Coordinate();
 
 1385     std::vector<double> bodyRotation = m_bodyRotation->Matrix();
 
 1387     double sunPosFromTarget[3];
 
 1388     mxv_c(&bodyRotation[0], &sunPosition[0], sunPosFromTarget);
 
 1390     return vnorm_c(sunPosFromTarget);
 
 1400   void Spice::computeSolarLongitude(
iTime et) {
 
 1401     NaifStatus::CheckErrors();
 
 1403     if (m_target->isSky()) {
 
 1408     if (m_usingAle || !m_usingNaif) {
 
 1409       double og_time = m_bodyRotation->EphemerisTime();
 
 1410       m_bodyRotation->SetEphemerisTime(et.
Et());
 
 1411       m_sunPosition->SetEphemerisTime(et.
Et());
 
 1413       std::vector<double> bodyRotMat = m_bodyRotation->Matrix();
 
 1414       std::vector<double> sunPos = m_sunPosition->Coordinate();
 
 1415       std::vector<double> sunVel = m_sunPosition->Velocity();
 
 1418       ucrss_c(&sunPos[0], &sunVel[0], sunAv);
 
 1421       for (
int i = 0; i < 3; i++) {
 
 1422         npole[i] = bodyRotMat[6+i];
 
 1425       double x[3], y[3], z[3];
 
 1427       ucrss_c(npole, z, x);
 
 1431       for (
int i = 0; i < 3; i++) {
 
 1438       mxv_c(trans, &sunPos[0], pos);
 
 1440       double radius, ls, lat;
 
 1441       reclat_c(pos, &radius, &ls, &lat);
 
 1445       NaifStatus::CheckErrors();
 
 1446       m_bodyRotation->SetEphemerisTime(og_time);
 
 1447       m_sunPosition->SetEphemerisTime(og_time);
 
 1451     if (m_bodyRotation->IsCached()) 
return;
 
 1453     double tipm[3][3], npole[3];
 
 1458     cidfrm_c(*m_spkBodyCode, 
sizeof(frameName), &frameCode, frameName, &found);
 
 1461       pxform_c(
"J2000", frameName, et.
Et(), tipm);
 
 1464       tipbod_c(
"J2000", *m_spkBodyCode, et.
Et(), tipm);
 
 1467     for (
int i = 0; i < 3; i++) {
 
 1468       npole[i] = tipm[2][i];
 
 1471     double state[6], lt;
 
 1472     spkez_c(*m_spkBodyCode, et.
Et(), 
"J2000", 
"NONE", 10, state, <);
 
 1475     ucrss_c(state, &state[3], uavel);
 
 1477     double x[3], y[3], z[3];
 
 1479     ucrss_c(npole, z, x);
 
 1483     for (
int i = 0; i < 3; i++) {
 
 1489     spkez_c(10, et.
Et(), 
"J2000", 
"LT+S", *m_spkBodyCode, state, <);
 
 1492     mxv_c(trans, state, pos);
 
 1494     double radius, ls, lat;
 
 1495     reclat_c(pos, &radius, &ls, &lat);
 
 1499     NaifStatus::CheckErrors();
 
 1511       computeSolarLongitude(*m_et);
 
 1512       return *m_solarLongitude;
 
 1526   bool Spice::hasKernels(
Pvl &lab) {
 
 1530     std::vector<string> keywords;
 
 1531     keywords.push_back(
"TargetPosition");
 
 1533     if (kernels.
hasKeyword(
"SpacecraftPosition")) {
 
 1534       keywords.push_back(
"SpacecraftPosition");
 
 1537       keywords.push_back(
"InstrumentPosition");
 
 1540     if (kernels.
hasKeyword(
"SpacecraftPointing")) {
 
 1541       keywords.push_back(
"SpacecraftPointing");
 
 1544       keywords.push_back(
"InstrumentPointing");
 
 1548       keywords.push_back(
"Frame");
 
 1552       keywords.push_back(
"Extra");
 
 1556     for (
int ikey = 0; ikey < (int) keywords.size(); ikey++) {
 
 1557       key = kernels[ikey];
 
 1559       for (
int i = 0; i < key.
size(); i++) {
 
 1560         if (key[i] == 
"") 
return false;
 
 1561         if (key[i].toUpper() == 
"NULL") 
return false;
 
 1562         if (key[i].toUpper() == 
"NADIR") 
return false;
 
 1563         if (key[i].toUpper() == 
"TABLE") 
return false;
 
 1577   bool Spice::isTimeSet(){
 
 1578     return !(m_et == NULL);
 
 1590     return m_sunPosition;
 
 1601     return m_instrumentPosition;
 
 1612     return m_bodyRotation;
 
 1623     return m_instrumentRotation;
 
 1626   bool Spice::isUsingAle(){