USGS

Isis 3.0 Object Programmers' Reference

Home

Spice.cpp
Go to the documentation of this file.
1 
22 #include "Spice.h"
23 
24 #include <cfloat>
25 #include <iomanip>
26 
27 #include <QDebug>
28 #include <QVector>
29 
30 #include "Constants.h"
31 #include "Distance.h"
32 #include "EllipsoidShape.h"
33 #include "EndianSwapper.h"
34 #include "FileName.h"
35 #include "IException.h"
36 #include "IString.h"
37 #include "iTime.h"
38 #include "Longitude.h"
40 #include "NaifStatus.h"
41 #include "ShapeModel.h"
42 #include "SpacecraftPosition.h"
43 #include "Target.h"
44 
45 #include "getSpkAbCorrState.hpp"
46 
47 using namespace std;
48 
49 namespace Isis {
71  // TODO: DOCUMENT EVERYTHING
72  Spice::Spice(Cube &cube) {
73  Pvl &lab = *cube.label();
74  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
75  bool hasTables = (kernels["TargetPosition"][0] == "Table");
76 
77  init(lab, !hasTables);
78  }
79 
86  Spice::Spice(Cube &cube, bool noTables) {
87  init(*cube.label(), noTables);
88  }
89 
103  void Spice::init(Pvl &lab, bool noTables) {
104  NaifStatus::CheckErrors();
105 
106  // Initialize members
107  m_solarLongitude = new Longitude;
108  m_et = NULL;
109  m_kernels = new QVector<QString>;
110  m_target = new Target(this, lab);
111 
112  m_startTime = new iTime;
113  m_endTime = new iTime;
114  m_cacheSize = new SpiceDouble;
115  *m_cacheSize = 0;
116 
117  m_startTimePadding = new SpiceDouble;
118  *m_startTimePadding = 0;
119  m_endTimePadding = new SpiceDouble;
120  *m_endTimePadding = 0;
121 
122  m_instrumentPosition = NULL;
123  m_instrumentRotation = NULL;
124  m_sunPosition = NULL;
125  m_bodyRotation = NULL;
126 
127  m_allowDownsizing = false;
128 
129  m_spkCode = new SpiceInt;
130  m_ckCode = new SpiceInt;
131  m_ikCode = new SpiceInt;
132  m_sclkCode = new SpiceInt;
133  m_spkBodyCode = new SpiceInt;
134  m_bodyFrameCode = new SpiceInt;
135 
136  m_naifKeywords = new PvlObject("NaifKeywords");
137 
138  // m_sky = false;
139 
140  // Get the kernel group and load main kernels
141  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
142 
143  // Get the time padding first
144  if (kernels.hasKeyword("StartPadding")) {
145  *m_startTimePadding = toDouble(kernels["StartPadding"][0]);
146  }
147  else {
148  *m_startTimePadding = 0.0;
149  }
150 
151  if (kernels.hasKeyword("EndPadding")) {
152  *m_endTimePadding = toDouble(kernels["EndPadding"][0]);
153  }
154  else {
155  *m_endTimePadding = 0.0;
156  }
157 
158  m_usingNaif = !lab.hasObject("NaifKeywords") || noTables;
159 
160  // Modified to load planetary ephemeris SPKs before s/c SPKs since some
161  // missions (e.g., MESSENGER) may augment the s/c SPK with new planet
162  // ephemerides. (2008-02-27 (KJB))
163  if (m_usingNaif) {
164  if (noTables) {
165  load(kernels["TargetPosition"], noTables);
166  load(kernels["InstrumentPosition"], noTables);
167  load(kernels["InstrumentPointing"], noTables);
168  }
169 
170  if (kernels.hasKeyword("Frame")) {
171  load(kernels["Frame"], noTables);
172  }
173 
174  load(kernels["TargetAttitudeShape"], noTables);
175  if (kernels.hasKeyword("Instrument")) {
176  load(kernels["Instrument"], noTables);
177  }
178  // Always load after instrument
179  if (kernels.hasKeyword("InstrumentAddendum")) {
180  load(kernels["InstrumentAddendum"], noTables);
181  }
182  load(kernels["LeapSecond"], noTables);
183  if ( kernels.hasKeyword("SpacecraftClock")) {
184  load(kernels["SpacecraftClock"], noTables);
185  }
186 
187  // Modified to load extra kernels last to allow overriding default values
188  // (2010-04-07) (DAC)
189  if (kernels.hasKeyword("Extra")) {
190  load(kernels["Extra"], noTables);
191  }
192 
193  // If Target is Saturn and ShapeModel is RingPlane, load the extra rings pck file
194  // which changes the prime meridian values to report longitudes with respect to
195  // the ascending node of the ringplane.
196  if (m_target->name().toUpper() == "SATURN" && m_target->shape()->name().toUpper() == "PLANE") {
197  PvlKeyword ringPck = PvlKeyword("RingPCK","$cassini/kernels/pck/saturnRings_v001.tpc");
198  load(ringPck, noTables);
199  }
200  }
201  else {
202  *m_naifKeywords = lab.findObject("NaifKeywords");
203  }
204 
205  // Get NAIF ik, spk, sclk, and ck codes
206  //
207  // Use ikcode to get parameters from instrument kernel such as focal
208  // length, distortions, focal plane maps, etc
209  //
210  // Use spkcode to get spacecraft position from spk file
211  //
212  // Use sclkcode to transform times from et to tics
213  //
214  // Use ckcode to transform between frames
215  //
216  // Use bodycode to obtain radii and attitude (pole position/omega0)
217  //
218  // Use spkbodycode to read body position from spk
219 
220  QString trykey = "NaifIkCode";
221  if (kernels.hasKeyword("NaifFrameCode")) trykey = "NaifFrameCode";
222  *m_ikCode = toInt(kernels[trykey][0]);
223 
224  *m_spkCode = *m_ikCode / 1000;
225  *m_sclkCode = *m_spkCode;
226  *m_ckCode = *m_ikCode;
227 
228  if (!m_target->isSky()) {
229  QString radiiKey = "BODY" + toString(m_target->naifBodyCode()) + "_RADII";
230  std::vector<Distance> radii(3,Distance());
231  radii[0] = Distance(getDouble(radiiKey, 0), Distance::Kilometers);
232  radii[1] = Distance(getDouble(radiiKey, 1), Distance::Kilometers);
233  radii[2] = Distance(getDouble(radiiKey, 2), Distance::Kilometers);
234  // m_target doesn't have the getDouble method so Spice gets the radii for it
235  m_target->setRadii(radii);
236  }
237  *m_spkBodyCode = m_target->naifBodyCode();
238 
239  // Override them if they exist in the labels
240  if (kernels.hasKeyword("NaifSpkCode")) {
241  *m_spkCode = (int) kernels["NaifSpkCode"];
242  }
243 
244  if (kernels.hasKeyword("NaifCkCode")) {
245  *m_ckCode = (int) kernels["NaifCkCode"];
246  }
247 
248  if (kernels.hasKeyword("NaifSclkCode")) {
249  *m_sclkCode = (int) kernels["NaifSclkCode"];
250  }
251 
252  if (!m_target->isSky()) {
253  if (kernels.hasKeyword("NaifSpkBodyCode")) {
254  *m_spkBodyCode = (int) kernels["NaifSpkBodyCode"];
255  }
256  }
257 
258  if (m_target->isSky()) {
259  // Create the identity rotation for sky targets
260  // Everything in bodyfixed will really be J2000
261  m_bodyRotation = new SpiceRotation(1);
262  }
263  else {
264  // JAA - Modified to stored and look for the frame body code in the
265  // cube labels
266  SpiceInt frameCode;
267  if ((m_usingNaif) || (!m_naifKeywords->hasKeyword("BODY_FRAME_CODE"))) {
268  char frameName[32];
269  SpiceBoolean found;
270  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName,
271  &found);
272 
273  if (!found) {
274  QString naifTarget = "IAU_" + QString(m_target->name()).toUpper();
275  namfrm_c(naifTarget.toAscii().data(), &frameCode);
276  if (frameCode == 0) {
277  string msg = "Can not find NAIF BODY_FRAME_CODE for target ["
278  + IString(m_target->name()) + "]";
279  throw IException(IException::Io, msg, _FILEINFO_);
280  }
281  }
282 
283  QVariant result = (int)frameCode;
284  storeValue("BODY_FRAME_CODE",0,SpiceIntType,result);
285  }
286  else {
287  frameCode = getInteger("BODY_FRAME_CODE",0);
288  }
289 
290  m_bodyRotation = new SpiceRotation(frameCode);
291  *m_bodyFrameCode = frameCode;
292  }
293 
294  m_instrumentRotation = new SpiceRotation(*m_ckCode);
295 
296  // Set up for observer/target and light time correction to between s/c
297  // and target body.
298  LightTimeCorrectionState ltState(*m_ikCode, this);
300 
301  vector<Distance> radius = m_target->radii();
302  Distance targetRadius((radius[0] + radius[2])/2.0);
303  m_instrumentPosition = new SpacecraftPosition(*m_spkCode, *m_spkBodyCode,
304  ltState, targetRadius);
305 
306  m_sunPosition = new SpicePosition(10, m_target->naifBodyCode());
307 
308  // Check to see if we have nadir pointing that needs to be computed &
309  // See if we have table blobs to load
310  if (kernels["TargetPosition"][0].toUpper() == "TABLE") {
311  Table t("SunPosition", lab.fileName(), lab);
312  m_sunPosition->LoadCache(t);
313 
314  Table t2("BodyRotation", lab.fileName(), lab);
315  m_bodyRotation->LoadCache(t2);
316  if (t2.Label().hasKeyword("SolarLongitude")) {
317  *m_solarLongitude = Longitude(t2.Label()["SolarLongitude"],
318  Angle::Degrees);
319  }
320  else {
321  solarLongitude();
322  }
323  }
324 
325  // We can't assume InstrumentPointing & InstrumentPosition exist, old
326  // files may be around with the old keywords, SpacecraftPointing &
327  // SpacecraftPosition. The old keywords were in existance before the
328  // Table option, so we don't need to check for Table under the old
329  // keywords.
330 
331  if (kernels["InstrumentPointing"].size() == 0) {
332  throw IException(IException::Unknown,
333  "No camera pointing available",
334  _FILEINFO_);
335  }
336 
337  // 2009-03-18 Tracie Sucharski - Removed test for old keywords, any files
338  // with the old keywords should be re-run through spiceinit.
339  if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") {
340  if (m_instrumentRotation) {
341  delete m_instrumentRotation;
342  m_instrumentRotation = NULL;
343  }
344 
345  m_instrumentRotation = new SpiceRotation(*m_ikCode, *m_spkBodyCode);
346  }
347  else if (kernels["InstrumentPointing"][0].toUpper() == "TABLE") {
348  Table t("InstrumentPointing", lab.fileName(), lab);
349  m_instrumentRotation->LoadCache(t);
350  }
351 
352  if (kernels["InstrumentPosition"].size() == 0) {
353  throw IException(IException::Unknown,
354  "No instrument position available",
355  _FILEINFO_);
356  }
357 
358  if (kernels["InstrumentPosition"][0].toUpper() == "TABLE") {
359  Table t("InstrumentPosition", lab.fileName(), lab);
360  m_instrumentPosition->LoadCache(t);
361  }
362 
363  NaifStatus::CheckErrors();
364  }
365 
366 
375  void Spice::load(PvlKeyword &key, bool noTables) {
376  NaifStatus::CheckErrors();
377 
378  for (int i = 0; i < key.size(); i++) {
379  if (key[i] == "") continue;
380  if (IString(key[i]).UpCase() == "NULL") break;
381  if (IString(key[i]).UpCase() == "NADIR") break;
382  if (IString(key[i]).UpCase() == "TABLE" && !noTables) break;
383  if (IString(key[i]).UpCase() == "TABLE" && noTables) continue;
384  FileName file(key[i]);
385  if (!file.fileExists()) {
386  QString msg = "Spice file does not exist [" + file.expanded() + "]";
387  throw IException(IException::Io, msg, _FILEINFO_);
388  }
389  QString fileName(file.expanded());
390  furnsh_c(fileName.toAscii().data());
391  m_kernels->push_back((QString)key[i]);
392  }
393 
394  NaifStatus::CheckErrors();
395  }
396 
400  Spice::~Spice() {
401  NaifStatus::CheckErrors();
402 
403  if (m_solarLongitude != NULL) {
404  delete m_solarLongitude;
405  m_solarLongitude = NULL;
406  }
407 
408  if (m_et != NULL) {
409  delete m_et;
410  m_et = NULL;
411  }
412 
413  if (m_startTime != NULL) {
414  delete m_startTime;
415  m_startTime = NULL;
416  }
417 
418  if (m_endTime != NULL) {
419  delete m_endTime;
420  m_endTime = NULL;
421  }
422 
423  if (m_cacheSize != NULL) {
424  delete m_cacheSize;
425  m_cacheSize = NULL;
426  }
427 
428  if (m_startTimePadding != NULL) {
429  delete m_startTimePadding;
430  m_startTimePadding = NULL;
431  }
432 
433  if (m_endTimePadding != NULL) {
434  delete m_endTimePadding;
435  m_endTimePadding = NULL;
436  }
437 
438  if (m_instrumentPosition != NULL) {
439  delete m_instrumentPosition;
440  m_instrumentPosition = NULL;
441  }
442 
443  if (m_instrumentRotation != NULL) {
444  delete m_instrumentRotation;
445  m_instrumentRotation = NULL;
446  }
447 
448  if (m_sunPosition != NULL) {
449  delete m_sunPosition;
450  m_sunPosition = NULL;
451  }
452 
453  if (m_bodyRotation != NULL) {
454  delete m_bodyRotation;
455  m_bodyRotation = NULL;
456  }
457 
458  if (m_spkCode != NULL) {
459  delete m_spkCode;
460  m_spkCode = NULL;
461  }
462 
463  if (m_ckCode != NULL) {
464  delete m_ckCode;
465  m_ckCode = NULL;
466  }
467 
468  if (m_ikCode != NULL) {
469  delete m_ikCode;
470  m_ikCode = NULL;
471  }
472 
473  if (m_sclkCode != NULL) {
474  delete m_sclkCode;
475  m_sclkCode = NULL;
476  }
477 
478  if (m_spkBodyCode != NULL) {
479  delete m_spkBodyCode;
480  m_spkBodyCode = NULL;
481  }
482 
483  if (m_bodyFrameCode != NULL) {
484  delete m_bodyFrameCode;
485  m_bodyFrameCode = NULL;
486  }
487 
488  if (m_target != NULL) {
489  delete m_target;
490  m_target = NULL;
491  }
492 
493  // Unload the kernels (TODO: Can this be done faster)
494  for (int i = 0; m_kernels && i < m_kernels->size(); i++) {
495  FileName file(m_kernels->at(i));
496  QString fileName(file.expanded());
497  unload_c(fileName.toAscii().data());
498  }
499 
500  if (m_kernels != NULL) {
501  delete m_kernels;
502  m_kernels = NULL;
503  }
504 
505  NaifStatus::CheckErrors();
506  }
507 
539  void Spice::createCache(iTime startTime, iTime endTime,
540  int cacheSize, double tol) {
541  NaifStatus::CheckErrors();
542 
543  // Check for errors
544  if (cacheSize <= 0) {
545  string msg = "Argument cacheSize must be greater than zero";
546  throw IException(IException::Programmer, msg, _FILEINFO_);
547  }
548 
549  if (startTime > endTime) {
550  string msg = "Argument startTime must be less than or equal to endTime";
551  throw IException(IException::Programmer, msg, _FILEINFO_);
552  }
553 
554  if (*m_cacheSize > 0) {
555  string msg = "A cache has already been created";
556  throw IException(IException::Programmer, msg, _FILEINFO_);
557  }
558 
559  if (cacheSize == 1 && (*m_startTimePadding != 0 || *m_endTimePadding != 0)) {
560  string msg = "This instrument does not support time padding";
561  throw IException(IException::User, msg, _FILEINFO_);
562  }
563 
564  string abcorr;
565  if (getSpkAbCorrState(abcorr) ) {
566  instrumentPosition()->SetAberrationCorrection("NONE");
567  }
568 
569  iTime avgTime((startTime.Et() + endTime.Et()) / 2.0);
570  computeSolarLongitude(avgTime);
571 
572  // Cache everything
573  if (!m_bodyRotation->IsCached()) {
574  int bodyRotationCacheSize = cacheSize;
575  if (cacheSize > 2) bodyRotationCacheSize = 2;
576  m_bodyRotation->LoadCache(
577  startTime.Et() - *m_startTimePadding,
578  endTime.Et() + *m_endTimePadding,
579  bodyRotationCacheSize);
580  }
581 
582  if (m_instrumentRotation->GetSource() < SpiceRotation::Memcache) {
583  if (cacheSize > 3) m_instrumentRotation->MinimizeCache(SpiceRotation::Yes);
584  m_instrumentRotation->LoadCache(
585  startTime.Et() - *m_startTimePadding,
586  endTime.Et() + *m_endTimePadding,
587  cacheSize);
588  }
589 
590  if (m_instrumentPosition->GetSource() < SpicePosition::Memcache) {
591  m_instrumentPosition->LoadCache(
592  startTime.Et() - *m_startTimePadding,
593  endTime.Et() + *m_endTimePadding,
594  cacheSize);
595  if (cacheSize > 3) m_instrumentPosition->Memcache2HermiteCache(tol);
596  }
597 
598  if (!m_sunPosition->IsCached()) {
599  int sunPositionCacheSize = cacheSize;
600  if (cacheSize > 2) sunPositionCacheSize = 2;
601  m_sunPosition->LoadCache(
602  startTime.Et() - *m_startTimePadding,
603  endTime.Et() + *m_endTimePadding,
604  sunPositionCacheSize);
605  }
606 
607  // Save the time and cache size
608  *m_startTime = startTime;
609  *m_endTime = endTime;
610  *m_cacheSize = cacheSize;
611  m_et = NULL;
612 
613  // Unload the kernels (TODO: Can this be done faster)
614  for (int i = 0; i < m_kernels->size(); i++) {
615  FileName file(m_kernels->at(i));
616  QString fileName(file.expanded());
617  unload_c(fileName.toAscii().data());
618  }
619 
620  m_kernels->clear();
621 
622  NaifStatus::CheckErrors();
623  }
624 
625 
633  iTime Spice::cacheStartTime() const {
634  if (m_startTime) {
635  return *m_startTime;
636  }
637 
638  return iTime();
639  }
640 
648  iTime Spice::cacheEndTime() const {
649  if (m_endTime) {
650  return *m_endTime;
651  }
652 
653  return iTime();
654  }
655 
670  void Spice::setTime(const iTime &et) {
671 
672  if (m_et == NULL) {
673  m_et = new iTime();
674  // Before the Spice is cached, but after the camera aberration correction
675  // is set, check to see if the instrument position kernel was created
676  // by spkwriter. If so turn off aberration corrections because the camera
677  // set aberration corrections are included in the spk already.
678  string abcorr;
679  if (*m_cacheSize == 0) {
680  if (m_startTime->Et() == 0.0 && m_endTime->Et() == 0.0
681  && getSpkAbCorrState(abcorr)) {
682  instrumentPosition()->SetAberrationCorrection("NONE");
683  }
684  }
685  }
686 
687  *m_et = et;
688 
689  m_bodyRotation->SetEphemerisTime(et.Et());
690  m_instrumentRotation->SetEphemerisTime(et.Et());
691  m_instrumentPosition->SetEphemerisTime(et.Et());
692  m_sunPosition->SetEphemerisTime(et.Et());
693 
694  std::vector<double> uB = m_bodyRotation->ReferenceVector(m_sunPosition->Coordinate());
695  m_uB[0] = uB[0];
696  m_uB[1] = uB[1];
697  m_uB[2] = uB[2];
698 
699  computeSolarLongitude(*m_et);
700  }
701 
711  void Spice::instrumentPosition(double p[3]) const {
712  instrumentBodyFixedPosition(p);
713  }
714 
724  void Spice::instrumentBodyFixedPosition(double p[3]) const {
725  if (m_et == NULL) {
726  std::string msg = "You must call SetTime first";
727  throw IException(IException::Programmer, msg, _FILEINFO_);
728  }
729 
730  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
731  p[0] = sB[0];
732  p[1] = sB[1];
733  p[2] = sB[2];
734  }
735 
741  void Spice::instrumentBodyFixedVelocity(double v[3]) const {
742  if (m_et == NULL) {
743  std::string msg = "You must call SetTime first";
744  throw IException(IException::Programmer, msg, _FILEINFO_);
745  }
746 
747  std::vector<double> state;
748  state.push_back(m_instrumentPosition->Coordinate()[0]);
749  state.push_back(m_instrumentPosition->Coordinate()[1]);
750  state.push_back(m_instrumentPosition->Coordinate()[2]);
751  state.push_back(m_instrumentPosition->Velocity()[0]);
752  state.push_back(m_instrumentPosition->Velocity()[1]);
753  state.push_back(m_instrumentPosition->Velocity()[2]);
754 
755  std::vector<double> vB = m_bodyRotation->ReferenceVector(state);
756  v[0] = vB[3];
757  v[1] = vB[4];
758  v[2] = vB[5];
759  }
760 
767  iTime Spice::time() const {
768  return *m_et;
769  }
770 
779  void Spice::sunPosition(double p[3]) const {
780  if (m_et == NULL) {
781  std::string msg = "You must call SetTime first";
782  throw IException(IException::Programmer, msg, _FILEINFO_);
783  }
784  p[0] = m_uB[0];
785  p[1] = m_uB[1];
786  p[2] = m_uB[2];
787  }
788 
794  double Spice::targetCenterDistance() const {
795  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
796  return sqrt(pow(sB[0], 2) + pow(sB[1], 2) + pow(sB[2], 2));
797  }
798 
806  void Spice::radii(Distance r[3]) const {
807  for (int i = 0; i < 3; i++)
808  r[i] =m_target->radii()[i];
809  }
810 
817  SpiceInt Spice::naifBodyCode() const {
818  return (int) m_target->naifBodyCode();
819  }
820 
826  SpiceInt Spice::naifSpkCode() const {
827  return *m_spkCode;
828  }
829 
835  SpiceInt Spice::naifCkCode() const {
836  return *m_ckCode;
837  }
838 
844  SpiceInt Spice::naifIkCode() const {
845  return *m_ikCode;
846  }
847 
854  SpiceInt Spice::naifSclkCode() const {
855  return *m_sclkCode;
856  }
857 
865  SpiceInt Spice::naifBodyFrameCode() const {
866  return *m_bodyFrameCode;
867  }
868 
869 
874  PvlObject Spice::getStoredNaifKeywords() const {
875  return *m_naifKeywords;
876  }
877 
884  double Spice::resolution() {
885  return 1.;
886  };
887 
888 
900  SpiceInt Spice::getInteger(const QString &key, int index) {
901  return readValue(key, SpiceIntType, index).toInt();
902  }
903 
914  SpiceDouble Spice::getDouble(const QString &key, int index) {
915  return readValue(key, SpiceDoubleType, index).toDouble();
916  }
917 
918 
925  iTime Spice::getClockTime(QString clockValue, int sclkCode) {
926  if (sclkCode == -1) {
927  sclkCode = naifSclkCode();
928  }
929 
930  iTime result;
931 
932  QString key = "CLOCK_ET_" + toString(sclkCode) + "_" + clockValue;
933  QVariant storedClockTime = getStoredResult(key, SpiceDoubleType);
934 
935  if (storedClockTime.isNull()) {
936  SpiceDouble timeOutput;
937  scs2e_c(sclkCode, clockValue.toAscii().data(), &timeOutput);
938  storedClockTime = timeOutput;
939  storeResult(key, SpiceDoubleType, timeOutput);
940  }
941 
942  result = storedClockTime.toDouble();
943 
944  return result;
945  }
946 
947 
958  QVariant Spice::readValue(QString key, SpiceValueType type, int index) {
959  QVariant result;
960 
961  if (m_usingNaif) {
962  NaifStatus::CheckErrors();
963 
964  // This is the success status of the naif call
965  SpiceBoolean found = false;
966 
967  // Naif tells us how many values were read, but we always just read one.
968  // Use this variable to make naif happy.
969  SpiceInt numValuesRead;
970 
971  if (type == SpiceDoubleType) {
972  SpiceDouble kernelValue;
973  gdpool_c(key.toAscii().data(), (SpiceInt)index, 1,
974  &numValuesRead, &kernelValue, &found);
975 
976  if (found)
977  result = kernelValue;
978  }
979  else if (type == SpiceStringType) {
980  char kernelValue[512];
981  gcpool_c(key.toAscii().data(), (SpiceInt)index, 1, sizeof(kernelValue),
982  &numValuesRead, kernelValue, &found);
983 
984  if (found)
985  result = kernelValue;
986  }
987  else if (type == SpiceIntType) {
988  SpiceInt kernelValue;
989  gipool_c(key.toAscii().data(), (SpiceInt)index, 1, &numValuesRead,
990  &kernelValue, &found);
991 
992  if (found)
993  result = (int)kernelValue;
994  }
995 
996  if (!found) {
997  QString msg = "Can not find [" + key + "] in text kernels";
998  throw IException(IException::Io, msg, _FILEINFO_);
999  }
1000 
1001  storeValue(key, index, type, result);
1002  }
1003  else {
1004  // Read from PvlObject that is our naif keywords
1005  result = readStoredValue(key, type, index);
1006 
1007  if (result.isNull()) {
1008  IString msg = "The camera is requesting spice data [" + key + "] that "
1009  "was not attached, please re-run spiceinit";
1010  throw IException(IException::Unknown, msg, _FILEINFO_);
1011  }
1012  }
1013 
1014  return result;
1015  }
1016 
1017 
1018  void Spice::storeResult(QString name, SpiceValueType type, QVariant value) {
1019  if (type == SpiceDoubleType) {
1020  EndianSwapper swapper("LSB");
1021 
1022  double doubleVal = value.toDouble();
1023  doubleVal = swapper.Double(&doubleVal);
1024  QByteArray byteCode((char *) &doubleVal, sizeof(double));
1025  value = byteCode;
1026  type = SpiceByteCodeType;
1027  }
1028 
1029  storeValue(name + "_COMPUTED", 0, type, value);
1030  }
1031 
1032 
1033  QVariant Spice::getStoredResult(QString name, SpiceValueType type) {
1034  bool wasDouble = false;
1035 
1036  if (type == SpiceDoubleType) {
1037  wasDouble = true;
1038  type = SpiceByteCodeType;
1039  }
1040 
1041  QVariant stored = readStoredValue(name + "_COMPUTED", type, 0);
1042 
1043  if (wasDouble && !stored.isNull()) {
1044  EndianSwapper swapper("LSB");
1045  double doubleVal = swapper.Double((void *)QByteArray::fromHex(
1046  stored.toByteArray()).data());
1047  stored = doubleVal;
1048  }
1049 
1050  return stored;
1051  }
1052 
1053 
1054  void Spice::storeValue(QString key, int index, SpiceValueType type,
1055  QVariant value) {
1056  if (!m_naifKeywords->hasKeyword(key)) {
1057  m_naifKeywords->addKeyword(PvlKeyword(key));
1058  }
1059 
1060  PvlKeyword &storedKey = m_naifKeywords->findKeyword(key);
1061 
1062  while(index >= storedKey.size()) {
1063  storedKey.addValue("");
1064  }
1065 
1066  if (type == SpiceByteCodeType) {
1067  storedKey[index] = QString(value.toByteArray().toHex().data());
1068  }
1069  else if (type == SpiceStringType) {
1070  storedKey[index] = value.toString();
1071  }
1072  else if (type == SpiceDoubleType) {
1073  storedKey[index] = toString(value.toDouble());
1074  }
1075  else if (type == SpiceIntType) {
1076  storedKey[index] = toString(value.toInt());
1077  }
1078  else {
1079  IString msg = "Unable to store variant in labels for key [" + key + "]";
1080  throw IException(IException::Unknown, msg, _FILEINFO_);
1081  }
1082  }
1083 
1084 
1085  QVariant Spice::readStoredValue(QString key, SpiceValueType type,
1086  int index) {
1087  // Read from PvlObject that is our naif keywords
1088  QVariant result;
1089 
1090  if (m_naifKeywords->hasKeyword(key) && !m_usingNaif) {
1091  PvlKeyword &storedKeyword = m_naifKeywords->findKeyword(key);
1092 
1093  try {
1094  if (type == SpiceDoubleType) {
1095  result = toDouble(storedKeyword[index]);
1096  }
1097  else if (type == SpiceStringType) {
1098  result = storedKeyword[index];
1099  }
1100  else if (type == SpiceByteCodeType || SpiceStringType) {
1101  result = storedKeyword[index].toAscii();
1102  }
1103  else if (type == SpiceIntType) {
1104  result = toInt(storedKeyword[index]);
1105  }
1106  }
1107  catch(IException &) {
1108  }
1109  }
1110 
1111  return result;
1112  }
1113 
1125  QString Spice::getString(const QString &key, int index) {
1126  return readValue(key, SpiceStringType, index).toString();
1127  }
1128 
1129 
1142  void Spice::subSpacecraftPoint(double &lat, double &lon) {
1143  NaifStatus::CheckErrors();
1144 
1145  if (m_et == NULL) {
1146  std::string msg = "You must call SetTime first";
1147  throw IException(IException::Programmer, msg, _FILEINFO_);
1148  }
1149 
1150  SpiceDouble usB[3], dist;
1151  std::vector<double> vsB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
1152  SpiceDouble sB[3];
1153  sB[0] = vsB[0];
1154  sB[1] = vsB[1];
1155  sB[2] = vsB[2];
1156  unorm_c(sB, usB, &dist);
1157 
1158  std::vector<Distance> radii = target()->radii();
1159  SpiceDouble a = radii[0].kilometers();
1160  SpiceDouble b = radii[1].kilometers();
1161  SpiceDouble c = radii[2].kilometers();
1162 
1163  SpiceDouble originB[3];
1164  originB[0] = originB[1] = originB[2] = 0.0;
1165 
1166  SpiceBoolean found;
1167  SpiceDouble subB[3];
1168  surfpt_c(originB, usB, a, b, c, subB, &found);
1169 
1170  SpiceDouble mylon, mylat;
1171  reclat_c(subB, &a, &mylon, &mylat);
1172  lat = mylat * 180.0 / PI;
1173  lon = mylon * 180.0 / PI;
1174  if (lon < 0.0) lon += 360.0;
1175 
1176  NaifStatus::CheckErrors();
1177  }
1178 
1190  void Spice::subSolarPoint(double &lat, double &lon) {
1191  NaifStatus::CheckErrors();
1192 
1193  if (m_et == NULL) {
1194  std::string msg = "You must call SetTime first";
1195  throw IException(IException::Programmer, msg, _FILEINFO_);
1196  }
1197 
1198  SpiceDouble uuB[3], dist;
1199  unorm_c(m_uB, uuB, &dist);
1200 
1201  std::vector<Distance> radii = target()->radii();
1202  SpiceDouble a = radii[0].kilometers();
1203  SpiceDouble b = radii[1].kilometers();
1204  SpiceDouble c = radii[2].kilometers();
1205 
1206  SpiceDouble originB[3];
1207  originB[0] = originB[1] = originB[2] = 0.0;
1208 
1209  SpiceBoolean found;
1210  SpiceDouble subB[3];
1211  surfpt_c(originB, uuB, a, b, c, subB, &found);
1212 
1213  SpiceDouble mylon, mylat;
1214  reclat_c(subB, &a, &mylon, &mylat);
1215  lat = mylat * 180.0 / PI;
1216  lon = mylon * 180.0 / PI;
1217  if (lon < 0.0) lon += 360.0;
1218 
1219  NaifStatus::CheckErrors();
1220  }
1221 
1222 
1228  Target *Spice::target() const {
1229  return m_target;
1230  }
1231 
1232 
1238  QString Spice::targetName() const {
1239  return m_target->name();
1240  }
1241 
1242 
1249  void Spice::computeSolarLongitude(iTime et) {
1250  NaifStatus::CheckErrors();
1251 
1252  if (m_target->isSky()) {
1253  *m_solarLongitude = Longitude();
1254  return;
1255  }
1256 
1257  if (m_bodyRotation->IsCached()) return;
1258 
1259  double tipm[3][3], npole[3];
1260  char frameName[32];
1261  SpiceInt frameCode;
1262  SpiceBoolean found;
1263 
1264  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found);
1265 
1266  if (found) {
1267  pxform_c("J2000", frameName, et.Et(), tipm);
1268  }
1269  else {
1270  tipbod_c("J2000", *m_spkBodyCode, et.Et(), tipm);
1271  }
1272 
1273  for (int i = 0; i < 3; i++) {
1274  npole[i] = tipm[2][i];
1275  }
1276 
1277  double state[6], lt;
1278  spkez_c(*m_spkBodyCode, et.Et(), "J2000", "NONE", 10, state, &lt);
1279 
1280  double uavel[3];
1281  ucrss_c(state, &state[3], uavel);
1282 
1283  double x[3], y[3], z[3];
1284  vequ_c(uavel, z);
1285  ucrss_c(npole, z, x);
1286  ucrss_c(z, x, y);
1287 
1288  double trans[3][3];
1289  for (int i = 0; i < 3; i++) {
1290  trans[0][i] = x[i];
1291  trans[1][i] = y[i];
1292  trans[2][i] = z[i];
1293  }
1294 
1295  spkez_c(10, et.Et(), "J2000", "LT+S", *m_spkBodyCode, state, &lt);
1296 
1297  double pos[3];
1298  mxv_c(trans, state, pos);
1299 
1300  double radius, ls, lat;
1301  reclat_c(pos, &radius, &ls, &lat);
1302 
1303  *m_solarLongitude = Longitude(ls, Angle::Radians).force360Domain();
1304 
1305  NaifStatus::CheckErrors();
1306  }
1307 
1308 
1314  Longitude Spice::solarLongitude() {
1315  if (m_et) {
1316  computeSolarLongitude(*m_et);
1317  return *m_solarLongitude;
1318  }
1319 
1320  return Longitude();
1321  }
1322 
1323 
1331  bool Spice::hasKernels(Pvl &lab) {
1332 
1333  // Get the kernel group and check main kernels
1334  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
1335  std::vector<string> keywords;
1336  keywords.push_back("TargetPosition");
1337 
1338  if (kernels.hasKeyword("SpacecraftPosition")) {
1339  keywords.push_back("SpacecraftPosition");
1340  }
1341  else {
1342  keywords.push_back("InstrumentPosition");
1343  }
1344 
1345  if (kernels.hasKeyword("SpacecraftPointing")) {
1346  keywords.push_back("SpacecraftPointing");
1347  }
1348  else {
1349  keywords.push_back("InstrumentPointing");
1350  }
1351 
1352  if (kernels.hasKeyword("Frame")) {
1353  keywords.push_back("Frame");
1354  }
1355 
1356  if (kernels.hasKeyword("Extra")) {
1357  keywords.push_back("Extra");
1358  }
1359 
1360  PvlKeyword key;
1361  for (int ikey = 0; ikey < (int) keywords.size(); ikey++) {
1362  key = kernels[ikey];
1363 
1364  for (int i = 0; i < key.size(); i++) {
1365  if (key[i] == "") return false;
1366  if (IString(key[i]).UpCase() == "NULL") return false;
1367  if (IString(key[i]).UpCase() == "NADIR") return false;
1368  if (IString(key[i]).UpCase() == "TABLE") return false;
1369  }
1370  }
1371  return true;
1372  }
1373 
1381  SpicePosition *Spice::sunPosition() const {
1382  return m_sunPosition;
1383  }
1384 
1392  SpicePosition *Spice::instrumentPosition() const {
1393  return m_instrumentPosition;
1394  }
1395 
1403  SpiceRotation *Spice::bodyRotation() const {
1404  return m_bodyRotation;
1405  }
1406 
1414  SpiceRotation *Spice::instrumentRotation() const {
1415  return m_instrumentRotation;
1416  }
1417 
1418 }