Isis 3 Programmer Reference
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  // Get target body code and radii and store them in the Naif group
262  // DAC modified to look for and store body code so that the radii keyword name
263  // will be able to be constructed even for new bodies not in the standard PCK yet.
264  QString radiiKey = "BODY" + Isis::toString(m_target->naifBodyCode()) + "_RADII";
265  QVariant result = m_target->naifBodyCode();
266  storeValue("BODY_CODE", 0, SpiceIntType, result);
267  std::vector<Distance> radii(3,Distance());
268  radii[0] = Distance(getDouble(radiiKey, 0), Distance::Kilometers);
269  radii[1] = Distance(getDouble(radiiKey, 1), Distance::Kilometers);
270  radii[2] = Distance(getDouble(radiiKey, 2), Distance::Kilometers);
271  // m_target doesn't have the getDouble method so Spice gets the radii for it
272  m_target->setRadii(radii);
273  }
274  *m_spkBodyCode = m_target->naifBodyCode();
275 
276  // Override them if they exist in the labels
277  if (kernels.hasKeyword("NaifSpkCode")) {
278  *m_spkCode = (int) kernels["NaifSpkCode"];
279  }
280 
281  if (kernels.hasKeyword("NaifCkCode")) {
282  *m_ckCode = (int) kernels["NaifCkCode"];
283  }
284 
285  if (kernels.hasKeyword("NaifSclkCode")) {
286  *m_sclkCode = (int) kernels["NaifSclkCode"];
287  }
288 
289  if (!m_target->isSky()) {
290  if (kernels.hasKeyword("NaifSpkBodyCode")) {
291  *m_spkBodyCode = (int) kernels["NaifSpkBodyCode"];
292  }
293  }
294 
295  if (m_target->isSky()) {
296  // Create the identity rotation for sky targets
297  // Everything in bodyfixed will really be J2000
298  m_bodyRotation = new SpiceRotation(1);
299  }
300  else {
301  // JAA - Modified to store and look for the frame body code in the cube labels
302  SpiceInt frameCode;
303  if ((m_usingNaif) || (!m_naifKeywords->hasKeyword("BODY_FRAME_CODE"))) {
304  char frameName[32];
305  SpiceBoolean found;
306  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found);
307 
308  if (!found) {
309  QString naifTarget = "IAU_" + m_target->name().toUpper();
310  namfrm_c(naifTarget.toLatin1().data(), &frameCode);
311  if (frameCode == 0) {
312  QString msg = "Can not find NAIF BODY_FRAME_CODE for target ["
313  + m_target->name() + "]";
314  throw IException(IException::Io, msg, _FILEINFO_);
315  }
316  }
317 
318  QVariant result = (int)frameCode;
319  storeValue("BODY_FRAME_CODE", 0, SpiceIntType, result);
320  }
321  else {
322  frameCode = getInteger("BODY_FRAME_CODE", 0);
323  }
324 
325  m_bodyRotation = new SpiceRotation(frameCode);
326  *m_bodyFrameCode = frameCode;
327  }
328 
329  m_instrumentRotation = new SpiceRotation(*m_ckCode);
330 
331  // Set up for observer/target and light time correction to between s/c
332  // and target body.
333  LightTimeCorrectionState ltState(*m_ikCode, this);
335 
336  vector<Distance> radius = m_target->radii();
337  Distance targetRadius((radius[0] + radius[2])/2.0);
338  m_instrumentPosition = new SpacecraftPosition(*m_spkCode, *m_spkBodyCode,
339  ltState, targetRadius);
340 
341  m_sunPosition = new SpicePosition(10, m_target->naifBodyCode());
342 
343  // Check to see if we have nadir pointing that needs to be computed &
344  // See if we have table blobs to load
345  if (kernels["TargetPosition"][0].toUpper() == "TABLE") {
346  Table t("SunPosition", lab.fileName(), lab);
347  m_sunPosition->LoadCache(t);
348 
349  Table t2("BodyRotation", lab.fileName(), lab);
350  m_bodyRotation->LoadCache(t2);
351  if (t2.Label().hasKeyword("SolarLongitude")) {
352  *m_solarLongitude = Longitude(t2.Label()["SolarLongitude"],
353  Angle::Degrees);
354  }
355  else {
356  solarLongitude();
357  }
358  }
359 
360  // We can't assume InstrumentPointing & InstrumentPosition exist, old
361  // files may be around with the old keywords, SpacecraftPointing &
362  // SpacecraftPosition. The old keywords were in existance before the
363  // Table option, so we don't need to check for Table under the old
364  // keywords.
365 
366  if (kernels["InstrumentPointing"].size() == 0) {
367  throw IException(IException::Unknown,
368  "No camera pointing available",
369  _FILEINFO_);
370  }
371 
372  // 2009-03-18 Tracie Sucharski - Removed test for old keywords, any files
373  // with the old keywords should be re-run through spiceinit.
374  if (kernels["InstrumentPointing"][0].toUpper() == "NADIR") {
375  if (m_instrumentRotation) {
376  delete m_instrumentRotation;
377  m_instrumentRotation = NULL;
378  }
379 
380  m_instrumentRotation = new SpiceRotation(*m_ikCode, *m_spkBodyCode);
381  }
382  else if (kernels["InstrumentPointing"][0].toUpper() == "TABLE") {
383  Table t("InstrumentPointing", lab.fileName(), lab);
384  m_instrumentRotation->LoadCache(t);
385  }
386 
387  if (kernels["InstrumentPosition"].size() == 0) {
388  throw IException(IException::Unknown,
389  "No instrument position available",
390  _FILEINFO_);
391  }
392 
393  if (kernels["InstrumentPosition"][0].toUpper() == "TABLE") {
394  Table t("InstrumentPosition", lab.fileName(), lab);
395  m_instrumentPosition->LoadCache(t);
396  }
397 
398  NaifStatus::CheckErrors();
399  }
400 
401 
410  void Spice::load(PvlKeyword &key, bool noTables) {
411  NaifStatus::CheckErrors();
412 
413  for (int i = 0; i < key.size(); i++) {
414  if (key[i] == "") continue;
415  if (key[i].toUpper() == "NULL") break;
416  if (key[i].toUpper() == "NADIR") break;
417  if (key[i].toUpper() == "TABLE" && !noTables) break;
418  if (key[i].toUpper() == "TABLE" && noTables) continue;
419  FileName file(key[i]);
420  if (!file.fileExists()) {
421  QString msg = "Spice file does not exist [" + file.expanded() + "]";
422  throw IException(IException::Io, msg, _FILEINFO_);
423  }
424  QString fileName = file.expanded();
425  furnsh_c(fileName.toLatin1().data());
426  m_kernels->push_back(key[i]);
427  }
428 
429  NaifStatus::CheckErrors();
430  }
431 
435  Spice::~Spice() {
436  NaifStatus::CheckErrors();
437 
438  if (m_solarLongitude != NULL) {
439  delete m_solarLongitude;
440  m_solarLongitude = NULL;
441  }
442 
443  if (m_et != NULL) {
444  delete m_et;
445  m_et = NULL;
446  }
447 
448  if (m_startTime != NULL) {
449  delete m_startTime;
450  m_startTime = NULL;
451  }
452 
453  if (m_endTime != NULL) {
454  delete m_endTime;
455  m_endTime = NULL;
456  }
457 
458  if (m_cacheSize != NULL) {
459  delete m_cacheSize;
460  m_cacheSize = NULL;
461  }
462 
463  if (m_startTimePadding != NULL) {
464  delete m_startTimePadding;
465  m_startTimePadding = NULL;
466  }
467 
468  if (m_endTimePadding != NULL) {
469  delete m_endTimePadding;
470  m_endTimePadding = NULL;
471  }
472 
473  if (m_instrumentPosition != NULL) {
474  delete m_instrumentPosition;
475  m_instrumentPosition = NULL;
476  }
477 
478  if (m_instrumentRotation != NULL) {
479  delete m_instrumentRotation;
480  m_instrumentRotation = NULL;
481  }
482 
483  if (m_sunPosition != NULL) {
484  delete m_sunPosition;
485  m_sunPosition = NULL;
486  }
487 
488  if (m_bodyRotation != NULL) {
489  delete m_bodyRotation;
490  m_bodyRotation = NULL;
491  }
492 
493  if (m_spkCode != NULL) {
494  delete m_spkCode;
495  m_spkCode = NULL;
496  }
497 
498  if (m_ckCode != NULL) {
499  delete m_ckCode;
500  m_ckCode = NULL;
501  }
502 
503  if (m_ikCode != NULL) {
504  delete m_ikCode;
505  m_ikCode = NULL;
506  }
507 
508  if (m_sclkCode != NULL) {
509  delete m_sclkCode;
510  m_sclkCode = NULL;
511  }
512 
513  if (m_spkBodyCode != NULL) {
514  delete m_spkBodyCode;
515  m_spkBodyCode = NULL;
516  }
517 
518  if (m_bodyFrameCode != NULL) {
519  delete m_bodyFrameCode;
520  m_bodyFrameCode = NULL;
521  }
522 
523  if (m_target != NULL) {
524  delete m_target;
525  m_target = NULL;
526  }
527 
528  // Unload the kernels (TODO: Can this be done faster)
529  for (int i = 0; m_kernels && i < m_kernels->size(); i++) {
530  FileName file(m_kernels->at(i));
531  QString fileName = file.expanded();
532  unload_c(fileName.toLatin1().data());
533  }
534 
535  if (m_kernels != NULL) {
536  delete m_kernels;
537  m_kernels = NULL;
538  }
539 
540  NaifStatus::CheckErrors();
541  }
542 
574  void Spice::createCache(iTime startTime, iTime endTime,
575  int cacheSize, double tol) {
576  NaifStatus::CheckErrors();
577 
578  // Check for errors
579  if (cacheSize <= 0) {
580  QString msg = "Argument cacheSize must be greater than zero";
581  throw IException(IException::Programmer, msg, _FILEINFO_);
582  }
583 
584  if (startTime > endTime) {
585  QString msg = "Argument startTime must be less than or equal to endTime";
586  throw IException(IException::Programmer, msg, _FILEINFO_);
587  }
588 
589  if (*m_cacheSize > 0) {
590  QString msg = "A cache has already been created";
591  throw IException(IException::Programmer, msg, _FILEINFO_);
592  }
593 
594  if (cacheSize == 1 && (*m_startTimePadding != 0 || *m_endTimePadding != 0)) {
595  QString msg = "This instrument does not support time padding";
596  throw IException(IException::User, msg, _FILEINFO_);
597  }
598 
599  string abcorr;
600  if (getSpkAbCorrState(abcorr)) {
601  instrumentPosition()->SetAberrationCorrection("NONE");
602  }
603 
604  iTime avgTime((startTime.Et() + endTime.Et()) / 2.0);
605  computeSolarLongitude(avgTime);
606 
607  // Cache everything
608  if (!m_bodyRotation->IsCached()) {
609  int bodyRotationCacheSize = cacheSize;
610  if (cacheSize > 2) bodyRotationCacheSize = 2;
611  m_bodyRotation->LoadCache(
612  startTime.Et() - *m_startTimePadding,
613  endTime.Et() + *m_endTimePadding,
614  bodyRotationCacheSize);
615  }
616 
617  if (m_instrumentRotation->GetSource() < SpiceRotation::Memcache) {
618  if (cacheSize > 3) m_instrumentRotation->MinimizeCache(SpiceRotation::Yes);
619  m_instrumentRotation->LoadCache(
620  startTime.Et() - *m_startTimePadding,
621  endTime.Et() + *m_endTimePadding,
622  cacheSize);
623  }
624 
625  if (m_instrumentPosition->GetSource() < SpicePosition::Memcache) {
626  m_instrumentPosition->LoadCache(
627  startTime.Et() - *m_startTimePadding,
628  endTime.Et() + *m_endTimePadding,
629  cacheSize);
630  if (cacheSize > 3) m_instrumentPosition->Memcache2HermiteCache(tol);
631  }
632 
633  if (!m_sunPosition->IsCached()) {
634  int sunPositionCacheSize = cacheSize;
635  if (cacheSize > 2) sunPositionCacheSize = 2;
636  m_sunPosition->LoadCache(
637  startTime.Et() - *m_startTimePadding,
638  endTime.Et() + *m_endTimePadding,
639  sunPositionCacheSize);
640  }
641 
642  // Save the time and cache size
643  *m_startTime = startTime;
644  *m_endTime = endTime;
645  *m_cacheSize = cacheSize;
646  m_et = NULL;
647 
648  // Unload the kernels (TODO: Can this be done faster)
649  for (int i = 0; i < m_kernels->size(); i++) {
650  FileName file(m_kernels->at(i));
651  QString fileName = file.expanded();
652  unload_c(fileName.toLatin1().data());
653  }
654 
655  m_kernels->clear();
656 
657  NaifStatus::CheckErrors();
658  }
659 
660 
668  iTime Spice::cacheStartTime() const {
669  if (m_startTime) {
670  return *m_startTime;
671  }
672 
673  return iTime();
674  }
675 
683  iTime Spice::cacheEndTime() const {
684  if (m_endTime) {
685  return *m_endTime;
686  }
687 
688  return iTime();
689  }
690 
705  void Spice::setTime(const iTime &et) {
706 
707  if (m_et == NULL) {
708  m_et = new iTime();
709 
710  // Before the Spice is cached, but after the camera aberration correction
711  // is set, check to see if the instrument position kernel was created
712  // by spkwriter. If so turn off aberration corrections because the camera
713  // set aberration corrections are included in the spk already.
714  string abcorr;
715  if (*m_cacheSize == 0) {
716  if (m_startTime->Et() == 0.0 && m_endTime->Et() == 0.0
717  && getSpkAbCorrState(abcorr)) {
718  instrumentPosition()->SetAberrationCorrection("NONE");
719  }
720  }
721  }
722 
723  *m_et = et;
724 
725  m_bodyRotation->SetEphemerisTime(et.Et());
726  m_instrumentRotation->SetEphemerisTime(et.Et());
727  m_instrumentPosition->SetEphemerisTime(et.Et());
728  m_sunPosition->SetEphemerisTime(et.Et());
729 
730  std::vector<double> uB = m_bodyRotation->ReferenceVector(m_sunPosition->Coordinate());
731  m_uB[0] = uB[0];
732  m_uB[1] = uB[1];
733  m_uB[2] = uB[2];
734 
735  computeSolarLongitude(*m_et);
736  }
737 
747  void Spice::instrumentPosition(double p[3]) const {
748  instrumentBodyFixedPosition(p);
749  }
750 
760  void Spice::instrumentBodyFixedPosition(double p[3]) const {
761  if (m_et == NULL) {
762  QString msg = "Unable to retrieve instrument's body fixed position."
763  " Spice::SetTime must be called first.";
764  throw IException(IException::Programmer, msg, _FILEINFO_);
765  }
766 
767  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
768  p[0] = sB[0];
769  p[1] = sB[1];
770  p[2] = sB[2];
771  }
772 
778  void Spice::instrumentBodyFixedVelocity(double v[3]) const {
779  if (m_et == NULL) {
780  QString msg = "Unable to retrieve instrument's body fixed velocity."
781  " Spice::SetTime must be called first.";
782  throw IException(IException::Programmer, msg, _FILEINFO_);
783  }
784 
785  std::vector<double> state;
786  state.push_back(m_instrumentPosition->Coordinate()[0]);
787  state.push_back(m_instrumentPosition->Coordinate()[1]);
788  state.push_back(m_instrumentPosition->Coordinate()[2]);
789  state.push_back(m_instrumentPosition->Velocity()[0]);
790  state.push_back(m_instrumentPosition->Velocity()[1]);
791  state.push_back(m_instrumentPosition->Velocity()[2]);
792 
793  std::vector<double> vB = m_bodyRotation->ReferenceVector(state);
794  v[0] = vB[3];
795  v[1] = vB[4];
796  v[2] = vB[5];
797  }
798 
799 
809  iTime Spice::time() const {
810  if (m_et == NULL) {
811  QString msg = "Unable to retrieve the time."
812  " Spice::SetTime must be called first.";
813  throw IException(IException::Programmer, msg, _FILEINFO_);
814  }
815  return *m_et;
816  }
817 
818 
827  void Spice::sunPosition(double p[3]) const {
828  if (m_et == NULL) {
829  QString msg = "Unable to retrieve sun's position."
830  " Spice::SetTime must be called first.";
831  throw IException(IException::Programmer, msg, _FILEINFO_);
832  }
833  p[0] = m_uB[0];
834  p[1] = m_uB[1];
835  p[2] = m_uB[2];
836  }
837 
843  double Spice::targetCenterDistance() const {
844  std::vector<double> sB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
845  return sqrt(pow(sB[0], 2) + pow(sB[1], 2) + pow(sB[2], 2));
846  }
847 
855  void Spice::radii(Distance r[3]) const {
856  for (int i = 0; i < 3; i++)
857  r[i] =m_target->radii()[i];
858  }
859 
866  SpiceInt Spice::naifBodyCode() const {
867  return (int) m_target->naifBodyCode();
868  }
869 
875  SpiceInt Spice::naifSpkCode() const {
876  return *m_spkCode;
877  }
878 
884  SpiceInt Spice::naifCkCode() const {
885  return *m_ckCode;
886  }
887 
893  SpiceInt Spice::naifIkCode() const {
894  return *m_ikCode;
895  }
896 
903  SpiceInt Spice::naifSclkCode() const {
904  return *m_sclkCode;
905  }
906 
914  SpiceInt Spice::naifBodyFrameCode() const {
915  return *m_bodyFrameCode;
916  }
917 
918 
923  PvlObject Spice::getStoredNaifKeywords() const {
924  return *m_naifKeywords;
925  }
926 
933  double Spice::resolution() {
934  return 1.;
935  };
936 
937 
949  SpiceInt Spice::getInteger(const QString &key, int index) {
950  return readValue(key, SpiceIntType, index).toInt();
951  }
952 
963  SpiceDouble Spice::getDouble(const QString &key, int index) {
964  return readValue(key, SpiceDoubleType, index).toDouble();
965  }
966 
967 
977  iTime Spice::getClockTime(QString clockValue, int sclkCode, bool clockTicks) {
978  if (sclkCode == -1) {
979  sclkCode = naifSclkCode();
980  }
981 
982  iTime result;
983 
984  QString key = "CLOCK_ET_" + Isis::toString(sclkCode) + "_" + clockValue;
985  QVariant storedClockTime = getStoredResult(key, SpiceDoubleType);
986 
987  if (storedClockTime.isNull()) {
988  SpiceDouble timeOutput;
989  NaifStatus::CheckErrors();
990  if (clockTicks) {
991  sct2e_c(sclkCode, (SpiceDouble) clockValue.toDouble(), &timeOutput);
992  }
993  else {
994  scs2e_c(sclkCode, clockValue.toLatin1().data(), &timeOutput);
995  }
996  NaifStatus::CheckErrors();
997  storedClockTime = timeOutput;
998  storeResult(key, SpiceDoubleType, timeOutput);
999  }
1000 
1001  result = storedClockTime.toDouble();
1002 
1003  return result;
1004  }
1005 
1006 
1017  QVariant Spice::readValue(QString key, SpiceValueType type, int index) {
1018  QVariant result;
1019 
1020  if (m_usingNaif) {
1021  NaifStatus::CheckErrors();
1022 
1023  // This is the success status of the naif call
1024  SpiceBoolean found = false;
1025 
1026  // Naif tells us how many values were read, but we always just read one.
1027  // Use this variable to make naif happy.
1028  SpiceInt numValuesRead;
1029 
1030  if (type == SpiceDoubleType) {
1031  SpiceDouble kernelValue;
1032  gdpool_c(key.toLatin1().data(), (SpiceInt)index, 1,
1033  &numValuesRead, &kernelValue, &found);
1034 
1035  if (found)
1036  result = kernelValue;
1037  }
1038  else if (type == SpiceStringType) {
1039  char kernelValue[512];
1040  gcpool_c(key.toLatin1().data(), (SpiceInt)index, 1, sizeof(kernelValue),
1041  &numValuesRead, kernelValue, &found);
1042 
1043  if (found)
1044  result = kernelValue;
1045  }
1046  else if (type == SpiceIntType) {
1047  SpiceInt kernelValue;
1048  gipool_c(key.toLatin1().data(), (SpiceInt)index, 1, &numValuesRead,
1049  &kernelValue, &found);
1050 
1051  if (found)
1052  result = (int)kernelValue;
1053  }
1054 
1055  if (!found) {
1056  QString msg = "Can not find [" + key + "] in text kernels";
1057  throw IException(IException::Io, msg, _FILEINFO_);
1058  }
1059 
1060  storeValue(key, index, type, result);
1061  NaifStatus::CheckErrors();
1062  }
1063  else {
1064  // Read from PvlObject that is our naif keywords
1065  result = readStoredValue(key, type, index);
1066 
1067  if (result.isNull()) {
1068  QString msg = "The camera is requesting spice data [" + key + "] that "
1069  "was not attached, please re-run spiceinit";
1070  throw IException(IException::Unknown, msg, _FILEINFO_);
1071  }
1072  }
1073 
1074  return result;
1075  }
1076 
1077 
1078  void Spice::storeResult(QString name, SpiceValueType type, QVariant value) {
1079  if (type == SpiceDoubleType) {
1080  EndianSwapper swapper("LSB");
1081 
1082  double doubleVal = value.toDouble();
1083  doubleVal = swapper.Double(&doubleVal);
1084  QByteArray byteCode((char *) &doubleVal, sizeof(double));
1085  value = byteCode;
1086  type = SpiceByteCodeType;
1087  }
1088 
1089  storeValue(name + "_COMPUTED", 0, type, value);
1090  }
1091 
1092 
1093  QVariant Spice::getStoredResult(QString name, SpiceValueType type) {
1094  bool wasDouble = false;
1095 
1096  if (type == SpiceDoubleType) {
1097  wasDouble = true;
1098  type = SpiceByteCodeType;
1099  }
1100 
1101  QVariant stored = readStoredValue(name + "_COMPUTED", type, 0);
1102 
1103  if (wasDouble && !stored.isNull()) {
1104  EndianSwapper swapper("LSB");
1105  double doubleVal = swapper.Double((void *)QByteArray::fromHex(
1106  stored.toByteArray()).data());
1107  stored = doubleVal;
1108  }
1109 
1110  return stored;
1111  }
1112 
1113 
1114  void Spice::storeValue(QString key, int index, SpiceValueType type,
1115  QVariant value) {
1116  if (!m_naifKeywords->hasKeyword(key)) {
1117  m_naifKeywords->addKeyword(PvlKeyword(key));
1118  }
1119 
1120  PvlKeyword &storedKey = m_naifKeywords->findKeyword(key);
1121 
1122  while(index >= storedKey.size()) {
1123  storedKey.addValue("");
1124  }
1125 
1126  if (type == SpiceByteCodeType) {
1127  storedKey[index] = QString(value.toByteArray().toHex().data());
1128  }
1129  else if (type == SpiceStringType) {
1130  storedKey[index] = value.toString();
1131  }
1132  else if (type == SpiceDoubleType) {
1133  storedKey[index] = toString(value.toDouble());
1134  }
1135  else if (type == SpiceIntType) {
1136  storedKey[index] = toString(value.toInt());
1137  }
1138  else {
1139  QString msg = "Unable to store variant in labels for key [" + key + "]";
1140  throw IException(IException::Unknown, msg, _FILEINFO_);
1141  }
1142  }
1143 
1144 
1145  QVariant Spice::readStoredValue(QString key, SpiceValueType type,
1146  int index) {
1147  // Read from PvlObject that is our naif keywords
1148  QVariant result;
1149 
1150  if (m_naifKeywords->hasKeyword(key) && !m_usingNaif) {
1151  PvlKeyword &storedKeyword = m_naifKeywords->findKeyword(key);
1152 
1153  try {
1154  if (type == SpiceDoubleType) {
1155  result = toDouble(storedKeyword[index]);
1156  }
1157  else if (type == SpiceStringType) {
1158  result = storedKeyword[index];
1159  }
1160  else if (type == SpiceByteCodeType || SpiceStringType) {
1161  result = storedKeyword[index].toLatin1();
1162  }
1163  else if (type == SpiceIntType) {
1164  result = toInt(storedKeyword[index]);
1165  }
1166  }
1167  catch(IException &) {
1168  }
1169  }
1170 
1171  return result;
1172  }
1173 
1185  QString Spice::getString(const QString &key, int index) {
1186  return readValue(key, SpiceStringType, index).toString();
1187  }
1188 
1189 
1202  void Spice::subSpacecraftPoint(double &lat, double &lon) {
1203  NaifStatus::CheckErrors();
1204 
1205  if (m_et == NULL) {
1206  QString msg = "Unable to retrieve subspacecraft position."
1207  " Spice::SetTime must be called first.";
1208  throw IException(IException::Programmer, msg, _FILEINFO_);
1209  }
1210 
1211  SpiceDouble usB[3], dist;
1212  std::vector<double> vsB = m_bodyRotation->ReferenceVector(m_instrumentPosition->Coordinate());
1213  SpiceDouble sB[3];
1214  sB[0] = vsB[0];
1215  sB[1] = vsB[1];
1216  sB[2] = vsB[2];
1217  unorm_c(sB, usB, &dist);
1218 
1219  std::vector<Distance> radii = target()->radii();
1220  SpiceDouble a = radii[0].kilometers();
1221  SpiceDouble b = radii[1].kilometers();
1222  SpiceDouble c = radii[2].kilometers();
1223 
1224  SpiceDouble originB[3];
1225  originB[0] = originB[1] = originB[2] = 0.0;
1226 
1227  SpiceBoolean found;
1228  SpiceDouble subB[3];
1229  surfpt_c(originB, usB, a, b, c, subB, &found);
1230 
1231  SpiceDouble mylon, mylat;
1232  reclat_c(subB, &a, &mylon, &mylat);
1233  lat = mylat * 180.0 / PI;
1234  lon = mylon * 180.0 / PI;
1235  if (lon < 0.0) lon += 360.0;
1236 
1237  NaifStatus::CheckErrors();
1238  }
1239 
1251  void Spice::subSolarPoint(double &lat, double &lon) {
1252  NaifStatus::CheckErrors();
1253 
1254  if (m_et == NULL) {
1255  QString msg = "Unable to retrieve subsolar point."
1256  " Spice::SetTime must be called first.";
1257  throw IException(IException::Programmer, msg, _FILEINFO_);
1258  }
1259 
1260  SpiceDouble uuB[3], dist;
1261  unorm_c(m_uB, uuB, &dist);
1262 
1263  std::vector<Distance> radii = target()->radii();
1264  SpiceDouble a = radii[0].kilometers();
1265  SpiceDouble b = radii[1].kilometers();
1266  SpiceDouble c = radii[2].kilometers();
1267 
1268  SpiceDouble originB[3];
1269  originB[0] = originB[1] = originB[2] = 0.0;
1270 
1271  SpiceBoolean found;
1272  SpiceDouble subB[3];
1273  surfpt_c(originB, uuB, a, b, c, subB, &found);
1274 
1275  SpiceDouble mylon, mylat;
1276  reclat_c(subB, &a, &mylon, &mylat);
1277  lat = mylat * 180.0 / PI;
1278  lon = mylon * 180.0 / PI;
1279  if (lon < 0.0) lon += 360.0;
1280 
1281  NaifStatus::CheckErrors();
1282  }
1283 
1284 
1290  Target *Spice::target() const {
1291  return m_target;
1292  }
1293 
1294 
1300  QString Spice::targetName() const {
1301  return m_target->name();
1302  }
1303 
1304 
1311  void Spice::computeSolarLongitude(iTime et) {
1312  NaifStatus::CheckErrors();
1313 
1314  if (m_target->isSky()) {
1315  *m_solarLongitude = Longitude();
1316  return;
1317  }
1318 
1319  if (m_bodyRotation->IsCached()) return;
1320 
1321  double tipm[3][3], npole[3];
1322  char frameName[32];
1323  SpiceInt frameCode;
1324  SpiceBoolean found;
1325 
1326  cidfrm_c(*m_spkBodyCode, sizeof(frameName), &frameCode, frameName, &found);
1327 
1328  if (found) {
1329  pxform_c("J2000", frameName, et.Et(), tipm);
1330  }
1331  else {
1332  tipbod_c("J2000", *m_spkBodyCode, et.Et(), tipm);
1333  }
1334 
1335  for (int i = 0; i < 3; i++) {
1336  npole[i] = tipm[2][i];
1337  }
1338 
1339  double state[6], lt;
1340  spkez_c(*m_spkBodyCode, et.Et(), "J2000", "NONE", 10, state, &lt);
1341 
1342  double uavel[3];
1343  ucrss_c(state, &state[3], uavel);
1344 
1345  double x[3], y[3], z[3];
1346  vequ_c(uavel, z);
1347  ucrss_c(npole, z, x);
1348  ucrss_c(z, x, y);
1349 
1350  double trans[3][3];
1351  for (int i = 0; i < 3; i++) {
1352  trans[0][i] = x[i];
1353  trans[1][i] = y[i];
1354  trans[2][i] = z[i];
1355  }
1356 
1357  spkez_c(10, et.Et(), "J2000", "LT+S", *m_spkBodyCode, state, &lt);
1358 
1359  double pos[3];
1360  mxv_c(trans, state, pos);
1361 
1362  double radius, ls, lat;
1363  reclat_c(pos, &radius, &ls, &lat);
1364 
1365  *m_solarLongitude = Longitude(ls, Angle::Radians).force360Domain();
1366 
1367  NaifStatus::CheckErrors();
1368  }
1369 
1370 
1376  Longitude Spice::solarLongitude() {
1377  if (m_et) {
1378  computeSolarLongitude(*m_et);
1379  return *m_solarLongitude;
1380  }
1381 
1382  return Longitude();
1383  }
1384 
1385 
1393  bool Spice::hasKernels(Pvl &lab) {
1394 
1395  // Get the kernel group and check main kernels
1396  PvlGroup kernels = lab.findGroup("Kernels", Pvl::Traverse);
1397  std::vector<string> keywords;
1398  keywords.push_back("TargetPosition");
1399 
1400  if (kernels.hasKeyword("SpacecraftPosition")) {
1401  keywords.push_back("SpacecraftPosition");
1402  }
1403  else {
1404  keywords.push_back("InstrumentPosition");
1405  }
1406 
1407  if (kernels.hasKeyword("SpacecraftPointing")) {
1408  keywords.push_back("SpacecraftPointing");
1409  }
1410  else {
1411  keywords.push_back("InstrumentPointing");
1412  }
1413 
1414  if (kernels.hasKeyword("Frame")) {
1415  keywords.push_back("Frame");
1416  }
1417 
1418  if (kernels.hasKeyword("Extra")) {
1419  keywords.push_back("Extra");
1420  }
1421 
1422  PvlKeyword key;
1423  for (int ikey = 0; ikey < (int) keywords.size(); ikey++) {
1424  key = kernels[ikey];
1425 
1426  for (int i = 0; i < key.size(); i++) {
1427  if (key[i] == "") return false;
1428  if (key[i].toUpper() == "NULL") return false;
1429  if (key[i].toUpper() == "NADIR") return false;
1430  if (key[i].toUpper() == "TABLE") return false;
1431  }
1432  }
1433  return true;
1434  }
1435 
1436 
1444  bool Spice::isTimeSet(){
1445  return !(m_et == NULL);
1446  }
1447 
1448 
1456  SpicePosition *Spice::sunPosition() const {
1457  return m_sunPosition;
1458  }
1459 
1467  SpicePosition *Spice::instrumentPosition() const {
1468  return m_instrumentPosition;
1469  }
1470 
1478  SpiceRotation *Spice::bodyRotation() const {
1479  return m_bodyRotation;
1480  }
1481 
1489  SpiceRotation *Spice::instrumentRotation() const {
1490  return m_instrumentRotation;
1491  }
1492 
1493 }
Provides swap observer/target and improved light time correction.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
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:116
Parse and return pieces of a time string.
Definition: iTime.h:78
const double PI
The mathematical constant PI.
Definition: Constants.h:56
SpiceValueType
NAIF value primitive type.
Definition: Spice.h:357
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
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.
Namespace for the standard library.
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
Longitude force360Domain() const
This returns a longitude that is constricted to 0-360 degrees.
Definition: Longitude.cpp:280
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
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
int size() const
Returns the number of values stored in this keyword.
Definition: PvlKeyword.h:141
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A single keyword-value pair.
Definition: PvlKeyword.h:98
Byte swapper.
Definition: EndianSwapper.h:55
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:212
QString name() const
Return target name.
Definition: Target.cpp:506
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:76
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.
Pvl * label() const
Returns a pointer to the IsisLabel object associated with the cube.
Definition: Cube.cpp:1346
Class for storing Table blobs information.
Definition: Table.h:77
QString fileName() const
Returns the filename used to initialise the Pvl object.
Definition: PvlContainer.h:246
Isis exception class.
Definition: IException.h:107
Obtain SPICE position information for a body.
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition: iTime.h:139
bool fileExists() const
Returns true if the file exists; false otherwise.
Definition: FileName.cpp:465
IO Handler for Isis Cubes.
Definition: Cube.h:170