Isis 3.0 Programmer Reference
Back | 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 <getSpkAbCorrState.hpp>
31 
32 #include "Constants.h"
33 #include "Distance.h"
34 #include "EllipsoidShape.h"
35 #include "EndianSwapper.h"
36 #include "FileName.h"
37 #include "IException.h"
38 #include "IString.h"
39 #include "iTime.h"
40 #include "Longitude.h"
42 #include "NaifStatus.h"
43 #include "ShapeModel.h"
44 #include "SpacecraftPosition.h"
45 #include "Target.h"
46 
47 using namespace std;
48 
49 namespace Isis {
58  // TODO: DOCUMENT EVERYTHING
59  Spice::Spice(Pvl &lab) {
60  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
61  bool hasTables = (kernels["TargetPosition"][0] == "Table");
62 
63  init(lab, !hasTables);
64  }
65 
66 
88  // TODO: DOCUMENT EVERYTHING
89  Spice::Spice(Cube &cube) {
90  Pvl &lab = *cube.label();
91  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
92  bool hasTables = (kernels["TargetPosition"][0] == "Table");
93 
94  init(lab, !hasTables);
95  }
96 
103  Spice::Spice(Cube &cube, bool noTables) {
104  init(*cube.label(), noTables);
105  }
106 
120  void Spice::init(Pvl &lab, bool noTables) {
121  NaifStatus::CheckErrors();
122 
123  // Initialize members
124  m_solarLongitude = new Longitude;
125  m_et = NULL;
126  m_kernels = new QVector<QString>;
127 
128  m_startTime = new iTime;
129  m_endTime = new iTime;
130  m_cacheSize = new SpiceDouble;
131  *m_cacheSize = 0;
132 
133  m_startTimePadding = new SpiceDouble;
134  *m_startTimePadding = 0;
135  m_endTimePadding = new SpiceDouble;
136  *m_endTimePadding = 0;
137 
138  m_instrumentPosition = NULL;
139  m_instrumentRotation = NULL;
140  m_sunPosition = NULL;
141  m_bodyRotation = NULL;
142 
143  m_allowDownsizing = false;
144 
145  m_spkCode = new SpiceInt;
146  m_ckCode = new SpiceInt;
147  m_ikCode = new SpiceInt;
148  m_sclkCode = new SpiceInt;
149  m_spkBodyCode = new SpiceInt;
150  m_bodyFrameCode = new SpiceInt;
151 
152  m_naifKeywords = new PvlObject("NaifKeywords");
153 
154  // m_sky = false;
155 
156  // Get the kernel group and load main kernels
157  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
158 
159  // Get the time padding first
160  if (kernels.hasKeyword("StartPadding")) {
161  *m_startTimePadding = toDouble(kernels["StartPadding"][0]);
162  }
163  else {
164  *m_startTimePadding = 0.0;
165  }
166 
167  if (kernels.hasKeyword("EndPadding")) {
168  *m_endTimePadding = toDouble(kernels["EndPadding"][0]);
169  }
170  else {
171  *m_endTimePadding = 0.0;
172  }
173 
174  m_usingNaif = !lab.hasObject("NaifKeywords") || noTables;
175 
176  // Modified to load planetary ephemeris SPKs before s/c SPKs since some
177  // missions (e.g., MESSENGER) may augment the s/c SPK with new planet
178  // ephemerides. (2008-02-27 (KJB))
179  if (m_usingNaif) {
180  if (noTables) {
181  load(kernels["TargetPosition"], noTables);
182  load(kernels["InstrumentPosition"], noTables);
183  load(kernels["InstrumentPointing"], noTables);
184  }
185 
186  if (kernels.hasKeyword("Frame")) {
187  load(kernels["Frame"], noTables);
188  }
189 
190  load(kernels["TargetAttitudeShape"], noTables);
191  if (kernels.hasKeyword("Instrument")) {
192  load(kernels["Instrument"], noTables);
193  }
194  // Always load after instrument
195  if (kernels.hasKeyword("InstrumentAddendum")) {
196  load(kernels["InstrumentAddendum"], noTables);
197  }
198  load(kernels["LeapSecond"], noTables);
199  if ( kernels.hasKeyword("SpacecraftClock")) {
200  load(kernels["SpacecraftClock"], noTables);
201  }
202 
203  // Modified to load extra kernels last to allow overriding default values
204  // (2010-04-07) (DAC)
205  if (kernels.hasKeyword("Extra")) {
206  load(kernels["Extra"], noTables);
207  }
208 
209  // Moved the construction of the Target after the NAIF kenels have been loaded or the
210  // NAIF keywords have been pulled from the cube labels, so we can find target body codes
211  // that are defined in kernels and not just body codes build into spicelib
212  // TODO: Move this below the else once the rings code below has been refactored
213  m_target = new Target(this, lab);
214 
215  // This should not be here. Consider having spiceinit add the necessary rings kernels to the
216  // Extra parameter if the user has set the shape model to RingPlane.
217  // If Target is Saturn and ShapeModel is RingPlane, load the extra rings pck file
218  // which changes the prime meridian values to report longitudes with respect to
219  // the ascending node of the ringplane.
220  if (m_target->name().toUpper() == "SATURN" && m_target->shape()->name().toUpper() == "PLANE") {
221  PvlKeyword ringPck = PvlKeyword("RingPCK","$cassini/kernels/pck/saturnRings_v001.tpc");
222  load(ringPck, noTables);
223  }
224  }
225  else {
226  *m_naifKeywords = lab.findObject("NaifKeywords");
227 
228  // Moved the construction of the Target after the NAIF kenels have been loaded or the
229  // NAIF keywords have been pulled from the cube labels, so we can find target body codes
230  // that are defined in kernels and not just body codes build into spicelib
231  // TODO: Move this below the else once the rings code above has been refactored
232  m_target = new Target(this, lab);
233 
234  }
235 
236 
237  // Get NAIF ik, spk, sclk, and ck codes
238  //
239  // Use ikcode to get parameters from instrument kernel such as focal
240  // length, distortions, focal plane maps, etc
241  //
242  // Use spkcode to get spacecraft position from spk file
243  //
244  // Use sclkcode to transform times from et to tics
245  //
246  // Use ckcode to transform between frames
247  //
248  // Use bodycode to obtain radii and attitude (pole position/omega0)
249  //
250  // Use spkbodycode to read body position from spk
251 
252  QString trykey = "NaifIkCode";
253  if (kernels.hasKeyword("NaifFrameCode")) trykey = "NaifFrameCode";
254  *m_ikCode = toInt(kernels[trykey][0]);
255 
256  *m_spkCode = *m_ikCode / 1000;
257  *m_sclkCode = *m_spkCode;
258  *m_ckCode = *m_ikCode;
259 
260  if (!m_target->isSky()) {
261  QString radiiKey = "BODY" + Isis::toString(m_target->naifBodyCode()) + "_RADII";
262  std::vector<Distance> radii(3,Distance());
263  radii[0] = Distance(getDouble(radiiKey, 0), Distance::Kilometers);
264  radii[1] = Distance(getDouble(radiiKey, 1), Distance::Kilometers);
265  radii[2] = Distance(getDouble(radiiKey, 2), Distance::Kilometers);
266  // m_target doesn't have the getDouble method so Spice gets the radii for it
267  m_target->setRadii(radii);
268  }
269  *m_spkBodyCode = m_target->naifBodyCode();
270 
271  // Override them if they exist in the labels
272  if (kernels.hasKeyword("NaifSpkCode")) {
273  *m_spkCode = (int) kernels["NaifSpkCode"];
274  }
275 
276  if (kernels.hasKeyword("NaifCkCode")) {
277  *m_ckCode = (int) kernels["NaifCkCode"];
278  }
279 
280  if (kernels.hasKeyword("NaifSclkCode")) {
281  *m_sclkCode = (int) kernels["NaifSclkCode"];
282  }
283 
284  if (!m_target->isSky()) {
285  if (kernels.hasKeyword("NaifSpkBodyCode")) {
286  *m_spkBodyCode = (int) kernels["NaifSpkBodyCode"];
287  }
288  }
289 
290  if (m_target->isSky()) {
291  // Create the identity rotation for sky targets
292  // Everything in bodyfixed will really be J2000
293  m_bodyRotation = new SpiceRotation(1);
294  }
295  else {
296  // JAA - Modified to store and look for the frame body code in the cube labels
297  SpiceInt frameCode;
298  if ((m_usingNaif) || (!m_naifKeywords->hasKeyword("BODY_FRAME_CODE"))) {
299  char frameName[32];
300  SpiceBoolean found;
301  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found);
302 
303  if (!found) {
304  QString naifTarget = "IAU_" + m_target->name().toUpper();
305  namfrm_c(naifTarget.toLatin1().data(), &frameCode);
306  if (frameCode == 0) {
307  QString msg = "Can not find NAIF BODY_FRAME_CODE for target ["
308  + m_target->name() + "]";
309  throw IException(IException::Io, msg, _FILEINFO_);
310  }
311  }
312 
313  QVariant result = (int)frameCode;
314  storeValue("BODY_FRAME_CODE", 0, SpiceIntType, result);
315  }
316  else {
317  frameCode = getInteger("BODY_FRAME_CODE", 0);
318  }
319 
320  m_bodyRotation = new SpiceRotation(frameCode);
321  *m_bodyFrameCode = frameCode;
322  }
323 
324  m_instrumentRotation = new SpiceRotation(*m_ckCode);
325 
326  // Set up for observer/target and light time correction to between s/c
327  // and target body.
328  LightTimeCorrectionState ltState(*m_ikCode, this);
330 
331  vector<Distance> radius = m_target->radii();
332  Distance targetRadius((radius[0] + radius[2])/2.0);
333  m_instrumentPosition = new SpacecraftPosition(*m_spkCode, *m_spkBodyCode,
334  ltState, targetRadius);
335 
336  m_sunPosition = new SpicePosition(10, m_target->naifBodyCode());
337 
338  // Check to see if we have nadir pointing that needs to be computed &
339  // See if we have table blobs to load
340  if (kernels["TargetPosition"][0].toUpper() == "TABLE") {
341  Table t("SunPosition", lab.fileName(), lab);
342  m_sunPosition->LoadCache(t);
343 
344  Table t2("BodyRotation", lab.fileName(), lab);
345  m_bodyRotation->LoadCache(t2);
346  if (t2.Label().hasKeyword("SolarLongitude")) {
347  *m_solarLongitude = Longitude(t2.Label()["SolarLongitude"],
348  Angle::Degrees);
349  }
350  else {
351  solarLongitude();
352  }
353  }
354 
355  // We can't assume InstrumentPointing & InstrumentPosition exist, old
356  // files may be around with the old keywords, SpacecraftPointing &
357  // SpacecraftPosition. The old keywords were in existance before the
358  // Table option, so we don't need to check for Table under the old
359  // keywords.
360 
361  if (kernels["InstrumentPointing"].size() == 0) {
362  throw IException(IException::Unknown,
363  "No camera pointing available",
364  _FILEINFO_);
365  }
366 
367  // 2009-03-18 Tracie Sucharski - Removed test for old keywords, any files
368  // with the old keywords should be re-run through spiceinit.
369  if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") {
370  if (m_instrumentRotation) {
371  delete m_instrumentRotation;
372  m_instrumentRotation = NULL;
373  }
374 
375  m_instrumentRotation = new SpiceRotation(*m_ikCode, *m_spkBodyCode);
376  }
377  else if (kernels["InstrumentPointing"][0].toUpper() == "TABLE") {
378  Table t("InstrumentPointing", lab.fileName(), lab);
379  m_instrumentRotation->LoadCache(t);
380  }
381 
382  if (kernels["InstrumentPosition"].size() == 0) {
383  throw IException(IException::Unknown,
384  "No instrument position available",
385  _FILEINFO_);
386  }
387 
388  if (kernels["InstrumentPosition"][0].toUpper() == "TABLE") {
389  Table t("InstrumentPosition", lab.fileName(), lab);
390  m_instrumentPosition->LoadCache(t);
391  }
392 
393  NaifStatus::CheckErrors();
394  }
395 
396 
405  void Spice::load(PvlKeyword &key, bool noTables) {
406  NaifStatus::CheckErrors();
407 
408  for (int i = 0; i < key.size(); i++) {
409  if (key[i] == "") continue;
410  if (key[i].toUpper() == "NULL") break;
411  if (key[i].toUpper() == "NADIR") break;
412  if (key[i].toUpper() == "TABLE" && !noTables) break;
413  if (key[i].toUpper() == "TABLE" && noTables) continue;
414  FileName file(key[i]);
415  if (!file.fileExists()) {
416  QString msg = "Spice file does not exist [" + file.expanded() + "]";
417  throw IException(IException::Io, msg, _FILEINFO_);
418  }
419  QString fileName = file.expanded();
420  furnsh_c(fileName.toLatin1().data());
421  m_kernels->push_back(key[i]);
422  }
423 
424  NaifStatus::CheckErrors();
425  }
426 
430  Spice::~Spice() {
431  NaifStatus::CheckErrors();
432 
433  if (m_solarLongitude != NULL) {
434  delete m_solarLongitude;
435  m_solarLongitude = NULL;
436  }
437 
438  if (m_et != NULL) {
439  delete m_et;
440  m_et = NULL;
441  }
442 
443  if (m_startTime != NULL) {
444  delete m_startTime;
445  m_startTime = NULL;
446  }
447 
448  if (m_endTime != NULL) {
449  delete m_endTime;
450  m_endTime = NULL;
451  }
452 
453  if (m_cacheSize != NULL) {
454  delete m_cacheSize;
455  m_cacheSize = NULL;
456  }
457 
458  if (m_startTimePadding != NULL) {
459  delete m_startTimePadding;
460  m_startTimePadding = NULL;
461  }
462 
463  if (m_endTimePadding != NULL) {
464  delete m_endTimePadding;
465  m_endTimePadding = NULL;
466  }
467 
468  if (m_instrumentPosition != NULL) {
469  delete m_instrumentPosition;
470  m_instrumentPosition = NULL;
471  }
472 
473  if (m_instrumentRotation != NULL) {
474  delete m_instrumentRotation;
475  m_instrumentRotation = NULL;
476  }
477 
478  if (m_sunPosition != NULL) {
479  delete m_sunPosition;
480  m_sunPosition = NULL;
481  }
482 
483  if (m_bodyRotation != NULL) {
484  delete m_bodyRotation;
485  m_bodyRotation = NULL;
486  }
487 
488  if (m_spkCode != NULL) {
489  delete m_spkCode;
490  m_spkCode = NULL;
491  }
492 
493  if (m_ckCode != NULL) {
494  delete m_ckCode;
495  m_ckCode = NULL;
496  }
497 
498  if (m_ikCode != NULL) {
499  delete m_ikCode;
500  m_ikCode = NULL;
501  }
502 
503  if (m_sclkCode != NULL) {
504  delete m_sclkCode;
505  m_sclkCode = NULL;
506  }
507 
508  if (m_spkBodyCode != NULL) {
509  delete m_spkBodyCode;
510  m_spkBodyCode = NULL;
511  }
512 
513  if (m_bodyFrameCode != NULL) {
514  delete m_bodyFrameCode;
515  m_bodyFrameCode = NULL;
516  }
517 
518  if (m_target != NULL) {
519  delete m_target;
520  m_target = NULL;
521  }
522 
523  // Unload the kernels (TODO: Can this be done faster)
524  for (int i = 0; m_kernels && i < m_kernels->size(); i++) {
525  FileName file(m_kernels->at(i));
526  QString fileName = file.expanded();
527  unload_c(fileName.toLatin1().data());
528  }
529 
530  if (m_kernels != NULL) {
531  delete m_kernels;
532  m_kernels = NULL;
533  }
534 
535  NaifStatus::CheckErrors();
536  }
537 
569  void Spice::createCache(iTime startTime, iTime endTime,
570  int cacheSize, double tol) {
571  NaifStatus::CheckErrors();
572 
573  // Check for errors
574  if (cacheSize <= 0) {
575  QString msg = "Argument cacheSize must be greater than zero";
576  throw IException(IException::Programmer, msg, _FILEINFO_);
577  }
578 
579  if (startTime > endTime) {
580  QString msg = "Argument startTime must be less than or equal to endTime";
581  throw IException(IException::Programmer, msg, _FILEINFO_);
582  }
583 
584  if (*m_cacheSize > 0) {
585  QString msg = "A cache has already been created";
586  throw IException(IException::Programmer, msg, _FILEINFO_);
587  }
588 
589  if (cacheSize == 1 && (*m_startTimePadding != 0 || *m_endTimePadding != 0)) {
590  QString msg = "This instrument does not support time padding";
591  throw IException(IException::User, msg, _FILEINFO_);
592  }
593 
594  string abcorr;
595  if (getSpkAbCorrState(abcorr)) {
596  instrumentPosition()->SetAberrationCorrection("NONE");
597  }
598 
599  iTime avgTime((startTime.Et() + endTime.Et()) / 2.0);
600  computeSolarLongitude(avgTime);
601 
602  // Cache everything
603  if (!m_bodyRotation->IsCached()) {
604  int bodyRotationCacheSize = cacheSize;
605  if (cacheSize > 2) bodyRotationCacheSize = 2;
606  m_bodyRotation->LoadCache(
607  startTime.Et() - *m_startTimePadding,
608  endTime.Et() + *m_endTimePadding,
609  bodyRotationCacheSize);
610  }
611 
612  if (m_instrumentRotation->GetSource() < SpiceRotation::Memcache) {
613  if (cacheSize > 3) m_instrumentRotation->MinimizeCache(SpiceRotation::Yes);
614  m_instrumentRotation->LoadCache(
615  startTime.Et() - *m_startTimePadding,
616  endTime.Et() + *m_endTimePadding,
617  cacheSize);
618  }
619 
620  if (m_instrumentPosition->GetSource() < SpicePosition::Memcache) {
621  m_instrumentPosition->LoadCache(
622  startTime.Et() - *m_startTimePadding,
623  endTime.Et() + *m_endTimePadding,
624  cacheSize);
625  if (cacheSize > 3) m_instrumentPosition->Memcache2HermiteCache(tol);
626  }
627 
628  if (!m_sunPosition->IsCached()) {
629  int sunPositionCacheSize = cacheSize;
630  if (cacheSize > 2) sunPositionCacheSize = 2;
631  m_sunPosition->LoadCache(
632  startTime.Et() - *m_startTimePadding,
633  endTime.Et() + *m_endTimePadding,
634  sunPositionCacheSize);
635  }
636 
637  // Save the time and cache size
638  *m_startTime = startTime;
639  *m_endTime = endTime;
640  *m_cacheSize = cacheSize;
641  m_et = NULL;
642 
643  // Unload the kernels (TODO: Can this be done faster)
644  for (int i = 0; i < m_kernels->size(); i++) {
645  FileName file(m_kernels->at(i));
646  QString fileName = file.expanded();
647  unload_c(fileName.toLatin1().data());
648  }
649 
650  m_kernels->clear();
651 
652  NaifStatus::CheckErrors();
653  }
654 
655 
663  iTime Spice::cacheStartTime() const {
664  if (m_startTime) {
665  return *m_startTime;
666  }
667 
668  return iTime();
669  }
670 
678  iTime Spice::cacheEndTime() const {
679  if (m_endTime) {
680  return *m_endTime;
681  }
682 
683  return iTime();
684  }
685 
700  void Spice::setTime(const iTime &et) {
701 
702  if (m_et == NULL) {
703  m_et = new iTime();
704 
705  // Before the Spice is cached, but after the camera aberration correction
706  // is set, check to see if the instrument position kernel was created
707  // by spkwriter. If so turn off aberration corrections because the camera
708  // set aberration corrections are included in the spk already.
709  string abcorr;
710  if (*m_cacheSize == 0) {
711  if (m_startTime->Et() == 0.0 && m_endTime->Et() == 0.0
712  && getSpkAbCorrState(abcorr)) {
713  instrumentPosition()->SetAberrationCorrection("NONE");
714  }
715  }
716  }
717 
718  *m_et = et;
719 
720  m_bodyRotation->SetEphemerisTime(et.Et());
721  m_instrumentRotation->SetEphemerisTime(et.Et());
722  m_instrumentPosition->SetEphemerisTime(et.Et());
723  m_sunPosition->SetEphemerisTime(et.Et());
724 
725  std::vector<double> uB = m_bodyRotation->ReferenceVector(m_sunPosition->Coordinate());
726  m_uB[0] = uB[0];
727  m_uB[1] = uB[1];
728  m_uB[2] = uB[2];
729 
730  computeSolarLongitude(*m_et);
731  }
732 
742  void Spice::instrumentPosition(double p[3]) const {
743  instrumentBodyFixedPosition(p);
744  }
745 
755  void Spice::instrumentBodyFixedPosition(double p[3]) const {
756  if (m_et == NULL) {
757  QString msg = "Unable to retrieve instrument's body fixed position."
758  " Spice::SetTime must be called first.";
759  throw IException(IException::Programmer, msg, _FILEINFO_);
760  }
761 
762  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
763  p[0] = sB[0];
764  p[1] = sB[1];
765  p[2] = sB[2];
766  }
767 
773  void Spice::instrumentBodyFixedVelocity(double v[3]) const {
774  if (m_et == NULL) {
775  QString msg = "Unable to retrieve instrument's body fixed velocity."
776  " Spice::SetTime must be called first.";
777  throw IException(IException::Programmer, msg, _FILEINFO_);
778  }
779 
780  std::vector<double> state;
781  state.push_back(m_instrumentPosition->Coordinate()[0]);
782  state.push_back(m_instrumentPosition->Coordinate()[1]);
783  state.push_back(m_instrumentPosition->Coordinate()[2]);
784  state.push_back(m_instrumentPosition->Velocity()[0]);
785  state.push_back(m_instrumentPosition->Velocity()[1]);
786  state.push_back(m_instrumentPosition->Velocity()[2]);
787 
788  std::vector<double> vB = m_bodyRotation->ReferenceVector(state);
789  v[0] = vB[3];
790  v[1] = vB[4];
791  v[2] = vB[5];
792  }
793 
794 
804  iTime Spice::time() const {
805  if (m_et == NULL) {
806  QString msg = "Unable to retrieve the time."
807  " Spice::SetTime must be called first.";
808  throw IException(IException::Programmer, msg, _FILEINFO_);
809  }
810  return *m_et;
811  }
812 
813 
822  void Spice::sunPosition(double p[3]) const {
823  if (m_et == NULL) {
824  QString msg = "Unable to retrieve sun's position."
825  " Spice::SetTime must be called first.";
826  throw IException(IException::Programmer, msg, _FILEINFO_);
827  }
828  p[0] = m_uB[0];
829  p[1] = m_uB[1];
830  p[2] = m_uB[2];
831  }
832 
838  double Spice::targetCenterDistance() const {
839  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
840  return sqrt(pow(sB[0], 2) + pow(sB[1], 2) + pow(sB[2], 2));
841  }
842 
850  void Spice::radii(Distance r[3]) const {
851  for (int i = 0; i < 3; i++)
852  r[i] =m_target->radii()[i];
853  }
854 
861  SpiceInt Spice::naifBodyCode() const {
862  return (int) m_target->naifBodyCode();
863  }
864 
870  SpiceInt Spice::naifSpkCode() const {
871  return *m_spkCode;
872  }
873 
879  SpiceInt Spice::naifCkCode() const {
880  return *m_ckCode;
881  }
882 
888  SpiceInt Spice::naifIkCode() const {
889  return *m_ikCode;
890  }
891 
898  SpiceInt Spice::naifSclkCode() const {
899  return *m_sclkCode;
900  }
901 
909  SpiceInt Spice::naifBodyFrameCode() const {
910  return *m_bodyFrameCode;
911  }
912 
913 
918  PvlObject Spice::getStoredNaifKeywords() const {
919  return *m_naifKeywords;
920  }
921 
928  double Spice::resolution() {
929  return 1.;
930  };
931 
932 
944  SpiceInt Spice::getInteger(const QString &key, int index) {
945  return readValue(key, SpiceIntType, index).toInt();
946  }
947 
958  SpiceDouble Spice::getDouble(const QString &key, int index) {
959  return readValue(key, SpiceDoubleType, index).toDouble();
960  }
961 
962 
969  iTime Spice::getClockTime(QString clockValue, int sclkCode) {
970  if (sclkCode == -1) {
971  sclkCode = naifSclkCode();
972  }
973 
974  iTime result;
975 
976  QString key = "CLOCK_ET_" + Isis::toString(sclkCode) + "_" + clockValue;
977  QVariant storedClockTime = getStoredResult(key, SpiceDoubleType);
978 
979  if (storedClockTime.isNull()) {
980  SpiceDouble timeOutput;
981  NaifStatus::CheckErrors();
982  scs2e_c(sclkCode, clockValue.toLatin1().data(), &timeOutput);
983  NaifStatus::CheckErrors();
984  storedClockTime = timeOutput;
985  storeResult(key, SpiceDoubleType, timeOutput);
986  }
987 
988  result = storedClockTime.toDouble();
989 
990  return result;
991  }
992 
993 
1004  QVariant Spice::readValue(QString key, SpiceValueType type, int index) {
1005  QVariant result;
1006 
1007  if (m_usingNaif) {
1008  NaifStatus::CheckErrors();
1009 
1010  // This is the success status of the naif call
1011  SpiceBoolean found = false;
1012 
1013  // Naif tells us how many values were read, but we always just read one.
1014  // Use this variable to make naif happy.
1015  SpiceInt numValuesRead;
1016 
1017  if (type == SpiceDoubleType) {
1018  SpiceDouble kernelValue;
1019  gdpool_c(key.toLatin1().data(), (SpiceInt)index, 1,
1020  &numValuesRead, &kernelValue, &found);
1021 
1022  if (found)
1023  result = kernelValue;
1024  }
1025  else if (type == SpiceStringType) {
1026  char kernelValue[512];
1027  gcpool_c(key.toLatin1().data(), (SpiceInt)index, 1, sizeof(kernelValue),
1028  &numValuesRead, kernelValue, &found);
1029 
1030  if (found)
1031  result = kernelValue;
1032  }
1033  else if (type == SpiceIntType) {
1034  SpiceInt kernelValue;
1035  gipool_c(key.toLatin1().data(), (SpiceInt)index, 1, &numValuesRead,
1036  &kernelValue, &found);
1037 
1038  if (found)
1039  result = (int)kernelValue;
1040  }
1041 
1042  if (!found) {
1043  QString msg = "Can not find [" + key + "] in text kernels";
1044  throw IException(IException::Io, msg, _FILEINFO_);
1045  }
1046 
1047  storeValue(key, index, type, result);
1048  NaifStatus::CheckErrors();
1049  }
1050  else {
1051  // Read from PvlObject that is our naif keywords
1052  result = readStoredValue(key, type, index);
1053 
1054  if (result.isNull()) {
1055  QString msg = "The camera is requesting spice data [" + key + "] that "
1056  "was not attached, please re-run spiceinit";
1057  throw IException(IException::Unknown, msg, _FILEINFO_);
1058  }
1059  }
1060 
1061  return result;
1062  }
1063 
1064 
1065  void Spice::storeResult(QString name, SpiceValueType type, QVariant value) {
1066  if (type == SpiceDoubleType) {
1067  EndianSwapper swapper("LSB");
1068 
1069  double doubleVal = value.toDouble();
1070  doubleVal = swapper.Double(&doubleVal);
1071  QByteArray byteCode((char *) &doubleVal, sizeof(double));
1072  value = byteCode;
1073  type = SpiceByteCodeType;
1074  }
1075 
1076  storeValue(name + "_COMPUTED", 0, type, value);
1077  }
1078 
1079 
1080  QVariant Spice::getStoredResult(QString name, SpiceValueType type) {
1081  bool wasDouble = false;
1082 
1083  if (type == SpiceDoubleType) {
1084  wasDouble = true;
1085  type = SpiceByteCodeType;
1086  }
1087 
1088  QVariant stored = readStoredValue(name + "_COMPUTED", type, 0);
1089 
1090  if (wasDouble && !stored.isNull()) {
1091  EndianSwapper swapper("LSB");
1092  double doubleVal = swapper.Double((void *)QByteArray::fromHex(
1093  stored.toByteArray()).data());
1094  stored = doubleVal;
1095  }
1096 
1097  return stored;
1098  }
1099 
1100 
1101  void Spice::storeValue(QString key, int index, SpiceValueType type,
1102  QVariant value) {
1103  if (!m_naifKeywords->hasKeyword(key)) {
1104  m_naifKeywords->addKeyword(PvlKeyword(key));
1105  }
1106 
1107  PvlKeyword &storedKey = m_naifKeywords->findKeyword(key);
1108 
1109  while(index >= storedKey.size()) {
1110  storedKey.addValue("");
1111  }
1112 
1113  if (type == SpiceByteCodeType) {
1114  storedKey[index] = QString(value.toByteArray().toHex().data());
1115  }
1116  else if (type == SpiceStringType) {
1117  storedKey[index] = value.toString();
1118  }
1119  else if (type == SpiceDoubleType) {
1120  storedKey[index] = toString(value.toDouble());
1121  }
1122  else if (type == SpiceIntType) {
1123  storedKey[index] = toString(value.toInt());
1124  }
1125  else {
1126  QString msg = "Unable to store variant in labels for key [" + key + "]";
1127  throw IException(IException::Unknown, msg, _FILEINFO_);
1128  }
1129  }
1130 
1131 
1132  QVariant Spice::readStoredValue(QString key, SpiceValueType type,
1133  int index) {
1134  // Read from PvlObject that is our naif keywords
1135  QVariant result;
1136 
1137  if (m_naifKeywords->hasKeyword(key) && !m_usingNaif) {
1138  PvlKeyword &storedKeyword = m_naifKeywords->findKeyword(key);
1139 
1140  try {
1141  if (type == SpiceDoubleType) {
1142  result = toDouble(storedKeyword[index]);
1143  }
1144  else if (type == SpiceStringType) {
1145  result = storedKeyword[index];
1146  }
1147  else if (type == SpiceByteCodeType || SpiceStringType) {
1148  result = storedKeyword[index].toLatin1();
1149  }
1150  else if (type == SpiceIntType) {
1151  result = toInt(storedKeyword[index]);
1152  }
1153  }
1154  catch(IException &) {
1155  }
1156  }
1157 
1158  return result;
1159  }
1160 
1172  QString Spice::getString(const QString &key, int index) {
1173  return readValue(key, SpiceStringType, index).toString();
1174  }
1175 
1176 
1189  void Spice::subSpacecraftPoint(double &lat, double &lon) {
1190  NaifStatus::CheckErrors();
1191 
1192  if (m_et == NULL) {
1193  QString msg = "Unable to retrieve subspacecraft position."
1194  " Spice::SetTime must be called first.";
1195  throw IException(IException::Programmer, msg, _FILEINFO_);
1196  }
1197 
1198  SpiceDouble usB[3], dist;
1199  std::vector<double> vsB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
1200  SpiceDouble sB[3];
1201  sB[0] = vsB[0];
1202  sB[1] = vsB[1];
1203  sB[2] = vsB[2];
1204  unorm_c(sB, usB, &dist);
1205 
1206  std::vector<Distance> radii = target()->radii();
1207  SpiceDouble a = radii[0].kilometers();
1208  SpiceDouble b = radii[1].kilometers();
1209  SpiceDouble c = radii[2].kilometers();
1210 
1211  SpiceDouble originB[3];
1212  originB[0] = originB[1] = originB[2] = 0.0;
1213 
1214  SpiceBoolean found;
1215  SpiceDouble subB[3];
1216  surfpt_c(originB, usB, a, b, c, subB, &found);
1217 
1218  SpiceDouble mylon, mylat;
1219  reclat_c(subB, &a, &mylon, &mylat);
1220  lat = mylat * 180.0 / PI;
1221  lon = mylon * 180.0 / PI;
1222  if (lon < 0.0) lon += 360.0;
1223 
1224  NaifStatus::CheckErrors();
1225  }
1226 
1238  void Spice::subSolarPoint(double &lat, double &lon) {
1239  NaifStatus::CheckErrors();
1240 
1241  if (m_et == NULL) {
1242  QString msg = "Unable to retrieve subsolar point."
1243  " Spice::SetTime must be called first.";
1244  throw IException(IException::Programmer, msg, _FILEINFO_);
1245  }
1246 
1247  SpiceDouble uuB[3], dist;
1248  unorm_c(m_uB, uuB, &dist);
1249 
1250  std::vector<Distance> radii = target()->radii();
1251  SpiceDouble a = radii[0].kilometers();
1252  SpiceDouble b = radii[1].kilometers();
1253  SpiceDouble c = radii[2].kilometers();
1254 
1255  SpiceDouble originB[3];
1256  originB[0] = originB[1] = originB[2] = 0.0;
1257 
1258  SpiceBoolean found;
1259  SpiceDouble subB[3];
1260  surfpt_c(originB, uuB, a, b, c, subB, &found);
1261 
1262  SpiceDouble mylon, mylat;
1263  reclat_c(subB, &a, &mylon, &mylat);
1264  lat = mylat * 180.0 / PI;
1265  lon = mylon * 180.0 / PI;
1266  if (lon < 0.0) lon += 360.0;
1267 
1268  NaifStatus::CheckErrors();
1269  }
1270 
1271 
1277  Target *Spice::target() const {
1278  return m_target;
1279  }
1280 
1281 
1287  QString Spice::targetName() const {
1288  return m_target->name();
1289  }
1290 
1291 
1298  void Spice::computeSolarLongitude(iTime et) {
1299  NaifStatus::CheckErrors();
1300 
1301  if (m_target->isSky()) {
1302  *m_solarLongitude = Longitude();
1303  return;
1304  }
1305 
1306  if (m_bodyRotation->IsCached()) return;
1307 
1308  double tipm[3][3], npole[3];
1309  char frameName[32];
1310  SpiceInt frameCode;
1311  SpiceBoolean found;
1312 
1313  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found);
1314 
1315  if (found) {
1316  pxform_c("J2000", frameName, et.Et(), tipm);
1317  }
1318  else {
1319  tipbod_c("J2000", *m_spkBodyCode, et.Et(), tipm);
1320  }
1321 
1322  for (int i = 0; i < 3; i++) {
1323  npole[i] = tipm[2][i];
1324  }
1325 
1326  double state[6], lt;
1327  spkez_c(*m_spkBodyCode, et.Et(), "J2000", "NONE", 10, state, &lt);
1328 
1329  double uavel[3];
1330  ucrss_c(state, &state[3], uavel);
1331 
1332  double x[3], y[3], z[3];
1333  vequ_c(uavel, z);
1334  ucrss_c(npole, z, x);
1335  ucrss_c(z, x, y);
1336 
1337  double trans[3][3];
1338  for (int i = 0; i < 3; i++) {
1339  trans[0][i] = x[i];
1340  trans[1][i] = y[i];
1341  trans[2][i] = z[i];
1342  }
1343 
1344  spkez_c(10, et.Et(), "J2000", "LT+S", *m_spkBodyCode, state, &lt);
1345 
1346  double pos[3];
1347  mxv_c(trans, state, pos);
1348 
1349  double radius, ls, lat;
1350  reclat_c(pos, &radius, &ls, &lat);
1351 
1352  *m_solarLongitude = Longitude(ls, Angle::Radians).force360Domain();
1353 
1354  NaifStatus::CheckErrors();
1355  }
1356 
1357 
1363  Longitude Spice::solarLongitude() {
1364  if (m_et) {
1365  computeSolarLongitude(*m_et);
1366  return *m_solarLongitude;
1367  }
1368 
1369  return Longitude();
1370  }
1371 
1372 
1380  bool Spice::hasKernels(Pvl &lab) {
1381 
1382  // Get the kernel group and check main kernels
1383  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
1384  std::vector<string> keywords;
1385  keywords.push_back("TargetPosition");
1386 
1387  if (kernels.hasKeyword("SpacecraftPosition")) {
1388  keywords.push_back("SpacecraftPosition");
1389  }
1390  else {
1391  keywords.push_back("InstrumentPosition");
1392  }
1393 
1394  if (kernels.hasKeyword("SpacecraftPointing")) {
1395  keywords.push_back("SpacecraftPointing");
1396  }
1397  else {
1398  keywords.push_back("InstrumentPointing");
1399  }
1400 
1401  if (kernels.hasKeyword("Frame")) {
1402  keywords.push_back("Frame");
1403  }
1404 
1405  if (kernels.hasKeyword("Extra")) {
1406  keywords.push_back("Extra");
1407  }
1408 
1409  PvlKeyword key;
1410  for (int ikey = 0; ikey < (int) keywords.size(); ikey++) {
1411  key = kernels[ikey];
1412 
1413  for (int i = 0; i < key.size(); i++) {
1414  if (key[i] == "") return false;
1415  if (key[i].toUpper() == "NULL") return false;
1416  if (key[i].toUpper() == "NADIR") return false;
1417  if (key[i].toUpper() == "TABLE") return false;
1418  }
1419  }
1420  return true;
1421  }
1422 
1423 
1431  bool Spice::isTimeSet(){
1432  return !(m_et == NULL);
1433  }
1434 
1435 
1443  SpicePosition *Spice::sunPosition() const {
1444  return m_sunPosition;
1445  }
1446 
1454  SpicePosition *Spice::instrumentPosition() const {
1455  return m_instrumentPosition;
1456  }
1457 
1465  SpiceRotation *Spice::bodyRotation() const {
1466  return m_bodyRotation;
1467  }
1468 
1476  SpiceRotation *Spice::instrumentRotation() const {
1477  return m_instrumentRotation;
1478  }
1479 
1480 }
Provides swap observer/target and improved light time correction.
int size() const
Returns the number of values stored in this keyword.
Definition: PvlKeyword.h:141
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
File name manipulation and expansion.
Definition: FileName.h:111
Parse and return pieces of a time string.
Definition: iTime.h:74
SpiceValueType
NAIF value primitive type.
Definition: Spice.h:344
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:286
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition: Cube.cpp:1298
const double PI(3.14159265358979323846)
The mathematical constant PI.
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:108
Provides interface to user configurable Light Time correction feature.
QString fileName() const
Returns the filename used to initialise the Pvl object.
Definition: PvlContainer.h:247
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
Distance measurement, usually in meters.
Definition: Distance.h:47
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition: iTime.h:135
Longitude force360Domain() const
This returns a longitude that is constricted to 0-360 degrees.
Definition: Longitude.cpp:280
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
QString name() const
Return target name.
Definition: Target.cpp:483
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
A single keyword-value pair.
Definition: PvlKeyword.h:98
Byte swapper.
Definition: EndianSwapper.h:54
Obtain SPICE rotation information for a body.
Container for cube-like labels.
Definition: Pvl.h:135
This class is used to create and store valid Isis3 targets.
Definition: Target.h:63
bool checkSpkKernelsForAberrationCorrection()
Check for light time/stellar aberration tag in SPK comments.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
bool hasObject(const QString &name) const
Returns a boolean value based on whether the object exists in the current PvlObject or not...
Definition: PvlObject.h:335
Class for storing Table blobs information.
Definition: Table.h:74
Isis exception class.
Definition: IException.h:99
Obtain SPICE position information for a body.
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
IO Handler for Isis Cubes.
Definition: Cube.h:158

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:29:39