Isis 3 Programmer Reference
SpiceRotation.cpp
1 #include "SpiceRotation.h"
2 
3 #include <algorithm>
4 #include <cfloat>
5 #include <cmath>
6 #include <iomanip>
7 #include <string>
8 #include <vector>
9 
10 #include <QDebug>
11 #include <QString>
12 
13 #include <SpiceUsr.h>
14 #include <SpiceZfc.h>
15 #include <SpiceZmc.h>
16 
17 #include "BasisFunction.h"
18 #include "IException.h"
19 #include "IString.h"
20 #include "LeastSquares.h"
21 #include "LineEquation.h"
22 #include "NaifStatus.h"
23 #include "PolynomialUnivariate.h"
24 #include "Quaternion.h"
25 #include "Table.h"
26 #include "TableField.h"
27 
28 // Declarations for bindings for Naif Spicelib routines that do not have
29 // a wrapper
30 extern int refchg_(integer *frame1, integer *frame2, doublereal *et,
31  doublereal *rotate);
32 extern int frmchg_(integer *frame1, integer *frame2, doublereal *et,
33  doublereal *rotate);
34 extern int invstm_(doublereal *mat, doublereal *invmat);
35 // Temporary declarations for bindings for Naif supportlib routines
36 
37 // These three declarations should be removed once supportlib is in Isis3
38 extern int ck3sdn(double sdntol, bool avflag, int *nrec,
39  double *sclkdp, double *quats, double *avvs,
40  int nints, double *starts, double *dparr,
41  int *intarr);
42 
43 namespace Isis {
52  p_constantFrames.push_back(frameCode);
53  p_timeBias = 0.0;
54  p_source = Spice;
55  p_CJ.resize(9);
56  p_matrixSet = false;
57  p_et = -DBL_MAX;
58  p_degree = 2;
59  p_degreeApplied = false;
60  p_noOverride = true;
61  p_axis1 = 3;
62  p_axis2 = 1;
63  p_axis3 = 3;
65  p_hasAngularVelocity = false;
66  p_av.resize(3);
69  p_fullCacheSize = 0;
72  }
73 
74 
85  SpiceRotation::SpiceRotation(int frameCode, int targetCode) {
87 
88  p_constantFrames.push_back(frameCode);
89  p_targetCode = targetCode;
90  p_timeBias = 0.0;
91  p_source = Nadir;
92  p_CJ.resize(9);
93  p_matrixSet = false;
94  p_et = -DBL_MAX;
95  p_axisP = 3;
96  p_degree = 2;
97  p_degreeApplied = false;
98  p_noOverride = true;
99  p_axis1 = 3;
100  p_axis2 = 1;
101  p_axis3 = 3;
103  p_hasAngularVelocity = false;
104  p_av.resize(3);
106  p_fullCacheEndTime = 0;
107 
108  p_fullCacheSize = 0;
109  m_frameType = DYN;
110  m_tOrientationAvailable = false;
111 
112  // Determine the axis for the velocity vector
113  QString key = "INS" + toString(frameCode) + "_TRANSX";
114  SpiceDouble transX[2];
115  SpiceInt number;
116  SpiceBoolean found;
117  //Read starting at element 1 (skipping element 0)
118  gdpool_c(key.toLatin1().data(), 1, 2, &number, transX, &found);
119 
120  if (!found) {
121  QString msg = "Cannot find [" + key + "] in text kernels";
122  throw IException(IException::Io, msg, _FILEINFO_);
123  }
124 
125  p_axisV = 2;
126  if (transX[0] < transX[1]) p_axisV = 1;
127 
129  }
130 
131 
138  p_cacheTime = rotToCopy.p_cacheTime;
139  p_cache = rotToCopy.p_cache;
140  p_cacheAv = rotToCopy.p_cacheAv;
141  p_av = rotToCopy.p_av;
142  p_degree = rotToCopy.p_degree;
143  p_axis1 = rotToCopy.p_axis1;
144  p_axis2 = rotToCopy.p_axis2;
145  p_axis3 = rotToCopy.p_axis3;
146 
148  p_timeFrames = rotToCopy.p_timeFrames;
149  p_timeBias = rotToCopy.p_timeBias;
150 
151  p_et = rotToCopy.p_et;
152  p_quaternion = rotToCopy.p_quaternion;
153  p_matrixSet = rotToCopy.p_matrixSet;
154  p_source = rotToCopy.p_source;
155  p_axisP = rotToCopy.p_axisP;
156  p_axisV = rotToCopy.p_axisV;
157  p_targetCode = rotToCopy.p_targetCode;
158  p_baseTime = rotToCopy.p_baseTime;
159  p_timeScale = rotToCopy.p_timeScale;
160  p_degreeApplied = rotToCopy.p_degreeApplied;
161 
162 // for (std::vector<double>::size_type i = 0; i < rotToCopy.p_coefficients[0].size(); i++)
163  for (int i = 0; i < 3; i++)
164  p_coefficients[i] = rotToCopy.p_coefficients[i];
165 
166  p_noOverride = rotToCopy.p_noOverride;
169  p_minimizeCache = rotToCopy.p_minimizeCache;
172  p_fullCacheSize = rotToCopy.p_fullCacheSize;
173  p_TC = rotToCopy.p_TC;
174 
175  p_CJ = rotToCopy.p_CJ;
176  p_degree = rotToCopy.p_degree;
178  m_frameType = rotToCopy.m_frameType;
179 
180  }
181 
182 
187  }
188 
189 
196  void SpiceRotation::SetFrame(int frameCode) {
197  p_constantFrames[0] = frameCode;
198  }
199 
200 
208  return p_constantFrames[0];
209  }
210 
211 
224  void SpiceRotation::SetTimeBias(double timeBias) {
225  p_timeBias = timeBias;
226  }
227 
228 
243 
244  // Save the time
245  if (p_et == et) return;
246  p_et = et;
247 
248  // Read from the cache
249  if (p_source == Memcache) {
251  }
252 
253  // Apply coefficients defining a function for each of the three camera angles and angular
254  // velocity if available
255  else if (p_source == PolyFunction) {
257  }
258  // Apply coefficients defining a function for each of the three camera angles and angular
259  // velocity if available
260  else if (p_source == PolyFunctionOverSpice) {
262  }
263  // Read from the kernel
264  else if (p_source == Spice) {
266  // Retrieve the J2000 (code=1) to reference rotation matrix
267  }
268  // Apply coefficients from PCK version of IAU solution for target body orientation and angular
269  // velocity???
270  else if (p_source == PckPolyFunction) {
272  }
273  // Compute from Nadir
274  else {
276  }
277 
278  // Set the quaternion for this rotation
279 // p_quaternion.Set ( p_CJ );
280  }
281 
282 
289  return p_et;
290  }
291 
292 
298  bool SpiceRotation::IsCached() const {
299  return (p_cache.size() > 0);
300  }
301 
302 
309  p_minimizeCache = status;
310  }
311 
312 
331  void SpiceRotation::LoadCache(double startTime, double endTime, int size) {
332 
333  // Check for valid arguments
334  if (size <= 0) {
335  QString msg = "Argument cacheSize must not be less or equal to zero";
337  }
338 
339  if (startTime > endTime) {
340  QString msg = "Argument startTime must be less than or equal to endTime";
342  }
343 
344  if ((startTime != endTime) && (size == 1)) {
345  QString msg = "Cache size must be more than 1 if startTime and endTime differ";
347  }
348 
349  // Make sure cache isn't already loaded
350  if (p_source == Memcache) {
351  QString msg = "A SpiceRotation cache has already been created";
353  }
354 
355  // Save full cache parameters
356  p_fullCacheStartTime = startTime;
357  p_fullCacheEndTime = endTime;
358  p_fullCacheSize = size;
359 
360  // Make sure the constant frame is loaded. This method also does the frame trace.
361  if (p_timeFrames.size() == 0) InitConstantRotation(startTime);
362 
363  // Set the frame type. If the frame class is PCK, load the constants.
364  if (p_source == Spice) {
365  setFrameType();
366  }
367 
368  LoadTimeCache();
369  int cacheSize = p_cacheTime.size();
370 
371  // Loop and load the cache
372  for (int i = 0; i < cacheSize; i++) {
373  double et = p_cacheTime[i];
374  SetEphemerisTime(et);
375  p_cache.push_back(p_CJ);
376  if (p_hasAngularVelocity) p_cacheAv.push_back(p_av);
377  }
378  p_source = Memcache;
379 
380  // Downsize already loaded caches (both time and quats)
381  if (p_minimizeCache == Yes && cacheSize > 5) {
382  LoadTimeCache();
383  }
384  }
385 
386 
400  void SpiceRotation::LoadCache(double time) {
401  LoadCache(time, time, 1);
402  }
403 
404 
430  // Clear any existing cached data to make it reentrant (KJB 2011-07-20).
431  p_timeFrames.clear();
432  p_TC.clear();
433  p_cache.clear();
434  p_cacheTime.clear();
435  p_cacheAv.clear();
436  p_hasAngularVelocity = false;
437 
438  // Load the constant and time-based frame traces and the constant rotation
439  if (table.Label().hasKeyword("TimeDependentFrames")) {
440  PvlKeyword labelTimeFrames = table.Label()["TimeDependentFrames"];
441  for (int i = 0; i < labelTimeFrames.size(); i++) {
442  p_timeFrames.push_back(toInt(labelTimeFrames[i]));
443  }
444  }
445  else {
446  p_timeFrames.push_back(p_constantFrames[0]);
447  p_timeFrames.push_back(J2000Code);
448  }
449 
450  if (table.Label().hasKeyword("ConstantRotation")) {
451  PvlKeyword labelConstantFrames = table.Label()["ConstantFrames"];
452  p_constantFrames.clear();
453 
454  for (int i = 0; i < labelConstantFrames.size(); i++) {
455  p_constantFrames.push_back(toInt(labelConstantFrames[i]));
456  }
457  PvlKeyword labelConstantRotation = table.Label()["ConstantRotation"];
458 
459  for (int i = 0; i < labelConstantRotation.size(); i++) {
460  p_TC.push_back(toDouble(labelConstantRotation[i]));
461  }
462  }
463  else {
464  p_TC.resize(9);
465  ident_c((SpiceDouble( *)[3]) &p_TC[0]);
466  }
467 
468  // Load the full cache time information from the label if available
469  if (table.Label().hasKeyword("CkTableStartTime")) {
470  p_fullCacheStartTime = toDouble(table.Label().findKeyword("CkTableStartTime")[0]);
471  }
472  if (table.Label().hasKeyword("CkTableEndTime")) {
473  p_fullCacheEndTime = toDouble(table.Label().findKeyword("CkTableEndTime")[0]);
474  }
475  if (table.Label().hasKeyword("CkTableOriginalSize")) {
476  p_fullCacheSize = toInt(table.Label().findKeyword("CkTableOriginalSize")[0]);
477  }
478 
479  // Load FrameTypeCode from labels if available and the planetary constants keywords
480  if (table.Label().hasKeyword("FrameTypeCode")) {
481  m_frameType = (FrameType) toInt(table.Label().findKeyword("FrameTypeCode")[0]);
482  }
483  else {
485  }
486 
487  if (m_frameType == PCK) {
488  loadPCFromTable(table.Label());
489  }
490 
491  int recFields = table[0].Fields();
492 
493  // Loop through and move the table to the cache. Retrieve the first record to
494  // establish the type of cache and then use the appropriate loop.
495 
496  // list table of quaternion and time
497  if (recFields == 5) {
498  for (int r = 0; r < table.Records(); r++) {
499  TableRecord &rec = table[r];
500 
501  if (rec.Fields() != recFields) {
502  // throw and error
503  }
504 
505  std::vector<double> j2000Quat;
506  j2000Quat.push_back((double)rec[0]);
507  j2000Quat.push_back((double)rec[1]);
508  j2000Quat.push_back((double)rec[2]);
509  j2000Quat.push_back((double)rec[3]);
510 
511  Quaternion q(j2000Quat);
512  std::vector<double> CJ = q.ToMatrix();
513  p_cache.push_back(CJ);
514  p_cacheTime.push_back((double)rec[4]);
515  }
516  p_source = Memcache;
517  }
518 
519  // list table of quaternion, angular velocity vector, and time
520  else if (recFields == 8) {
521  for (int r = 0; r < table.Records(); r++) {
522  TableRecord &rec = table[r];
523 
524  if (rec.Fields() != recFields) {
525  // throw and error
526  }
527 
528  std::vector<double> j2000Quat;
529  j2000Quat.push_back((double)rec[0]);
530  j2000Quat.push_back((double)rec[1]);
531  j2000Quat.push_back((double)rec[2]);
532  j2000Quat.push_back((double)rec[3]);
533 
534 
535  Quaternion q(j2000Quat);
536  std::vector<double> CJ = q.ToMatrix();
537  p_cache.push_back(CJ);
538 
539  std::vector<double> av;
540  av.push_back((double)rec[4]);
541  av.push_back((double)rec[5]);
542  av.push_back((double)rec[6]);
543  p_cacheAv.push_back(av);
544 
545  p_cacheTime.push_back((double)rec[7]);
546  p_hasAngularVelocity = true;
547  }
548  p_source = Memcache;
549  }
550 
551  // coefficient table for angle1, angle2, and angle3
552  else if (recFields == 3) {
553  std::vector<double> coeffAng1, coeffAng2, coeffAng3;
554 
555  for (int r = 0; r < table.Records() - 1; r++) {
556  TableRecord &rec = table[r];
557 
558  if (rec.Fields() != recFields) {
559  // throw an error
560  }
561  coeffAng1.push_back((double)rec[0]);
562  coeffAng2.push_back((double)rec[1]);
563  coeffAng3.push_back((double)rec[2]);
564  }
565 
566  // Take care of time parameters
567  TableRecord &rec = table[table.Records()-1];
568  double baseTime = (double)rec[0];
569  double timeScale = (double)rec[1];
570  double degree = (double)rec[2];
571  SetPolynomialDegree((int) degree);
572  SetOverrideBaseTime(baseTime, timeScale);
573  SetPolynomial(coeffAng1, coeffAng2, coeffAng3);
575  if (degree > 0) p_hasAngularVelocity = true;
576  if (degree == 0 && p_cacheAv.size() > 0) p_hasAngularVelocity = true;
577  }
578  else {
579  QString msg = "Expecting either three, five, or eight fields in the SpiceRotation table";
581  }
582  }
583 
584 
595  // Save current et
596  double et = p_et;
597  p_et = -DBL_MAX;
598 
599  if (p_source == PolyFunction) {
600  // Clear existing matrices from cache
601  p_cacheTime.clear();
602  p_cache.clear();
603 
604  // Clear the angular velocity cache if we can calculate it instead. It can't be calculated
605  // for functions of degree 0 (framing cameras), so keep the original av. It is better than
606  // nothing.
607  if (p_degree > 0 && p_cacheAv.size() > 1) p_cacheAv.clear();
608 
609  // Load the time cache first
611  LoadTimeCache();
612 
613  if (p_fullCacheSize > 1) {
614  // Load the matrix and av caches
615  for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
616  SetEphemerisTime(p_cacheTime.at(pos));
617  p_cache.push_back(p_CJ);
618  p_cacheAv.push_back(p_av);
619  }
620  }
621  else {
622  // Load the matrix for the single updated time instance
624  p_cache.push_back(p_CJ);
625  }
626  }
627  else if (p_source == PolyFunctionOverSpice) {
628  SpiceRotation tempRot(*this);
629 
630  std::vector<double>::size_type maxSize = p_fullCacheSize;
631 
632  // Clear the existing caches
633  p_cache.clear();
634  p_cacheTime.clear();
635  p_cacheAv.clear();
636 
637  // Reload the time cache first
639  LoadTimeCache();
640 
641  for (std::vector<double>::size_type pos = 0; pos < maxSize; pos++) {
642  tempRot.SetEphemerisTime(p_cacheTime.at(pos));
643  p_cache.push_back(tempRot.TimeBasedMatrix());
644  if (p_hasAngularVelocity) p_cacheAv.push_back(tempRot.AngularVelocity());
645  }
646  }
647  else { //(p_source < PolyFunction)
648  QString msg = "The SpiceRotation has not yet been fit to a function";
650  }
651 
652  // Set source to cache and reset current et
653  // Make sure source is Memcache now
654  p_source = Memcache;
655  p_et = -DBL_MAX;
656  SetEphemerisTime(et);
657  }
658 
659 
674  Table SpiceRotation::LineCache(const QString &tableName) {
675 
676  // Apply the function and fill the caches
678 
679  if (p_source != Memcache) {
680  QString msg = "Only cached rotations can be returned as a line cache of quaternions and time";
682  }
683  // Load the table and return it to caller
684  return Cache(tableName);
685  }
686 
687 
710  Table SpiceRotation::Cache(const QString &tableName) {
711  // First handle conversion of PolyFunctionOverSpiceConstant
712  // by converting it to the full Memcache and try to downsize it
714  LineCache(tableName);
715 
716  //std::cout << "Full cache size is " << p_cache.size() << endl;
718  LoadTimeCache();
719 
720  //std::cout << "Minimized cache size is " << p_cache.size() << endl;
721  }
722 
723  // Load the list of rotations and their corresponding times
724  if (p_source == Memcache) {
725  TableField q0("J2000Q0", TableField::Double);
726  TableField q1("J2000Q1", TableField::Double);
727  TableField q2("J2000Q2", TableField::Double);
728  TableField q3("J2000Q3", TableField::Double);
730 
731  TableRecord record;
732  record += q0;
733  record += q1;
734  record += q2;
735  record += q3;
736  int timePos = 4;
737 
738  if (p_hasAngularVelocity) {
739  TableField av1("AV1", TableField::Double);
740  TableField av2("AV2", TableField::Double);
741  TableField av3("AV3", TableField::Double);
742  record += av1;
743  record += av2;
744  record += av3;
745  timePos = 7;
746  }
747 
748  record += t;
749  Table table(tableName, record);
750 
751  for (int i = 0; i < (int)p_cache.size(); i++) {
752  Quaternion q(p_cache[i]);
753  std::vector<double> v = q.GetQuaternion();
754  record[0] = v[0];
755  record[1] = v[1];
756  record[2] = v[2];
757  record[3] = v[3];
758 
759  if (p_hasAngularVelocity) {
760  record[4] = p_cacheAv[i][0];
761  record[5] = p_cacheAv[i][1];
762  record[6] = p_cacheAv[i][2];
763  }
764 
765  record[timePos] = p_cacheTime[i];
766  table += record;
767  }
768 
769  CacheLabel(table);
770  return table;
771  }
772  // Just load the position for the single epoch
773  else if (p_source == PolyFunction && p_degree == 0 && p_fullCacheSize == 1) {
774  return LineCache(tableName);
775  }
776  // Load the coefficients for the curves fit to the 3 camera angles
777  else if (p_source == PolyFunction) {
778  TableField angle1("J2000Ang1", TableField::Double);
779  TableField angle2("J2000Ang2", TableField::Double);
780  TableField angle3("J2000Ang3", TableField::Double);
781 
782  TableRecord record;
783  record += angle1;
784  record += angle2;
785  record += angle3;
786 
787  Table table(tableName, record);
788 
789  for (int cindex = 0; cindex < p_degree + 1; cindex++) {
790  record[0] = p_coefficients[0][cindex];
791  record[1] = p_coefficients[1][cindex];
792  record[2] = p_coefficients[2][cindex];
793  table += record;
794  }
795 
796  // Load one more table entry with the time adjustments for the fit equation
797  // t = (et - baseTime)/ timeScale
798  record[0] = p_baseTime;
799  record[1] = p_timeScale;
800  record[2] = (double) p_degree;
801 
802  table += record;
803  CacheLabel(table);
804  return table;
805  }
806  else {
807  // throw an error -- should not get here -- invalid Spice Source
808  QString msg = "To create table source of data must be either Memcache or PolyFunction";
810  }
811  }
812 
813 
821  void SpiceRotation::loadPCFromSpice(int centerBody) {
823  SpiceInt centerBodyCode = (SpiceInt) centerBody;
824 
825  // Retrieve the frame class from Naif. We distinguish PCK types into text PCK,
826  // binary PCK, and PCK not referenced to J2000. Isis binary PCK can
827  // not be used to solve for target body orientation because of the variety and
828  // complexity models used with binary PCK. Currently ISIS does not solve for
829  // target body orientation on bodies not referenced to J2000, but it could be
830  // changed to handle that case.
832 
833  if (m_frameType == PCK) {
834  // Make sure the reference frame is J2000. We will need to modify FrameTrace and
835  // the pck methods in this class to handle this case. At the time this method was
836  // added, this case is not being used in the pck file. It is mentioned in the Naif required
837  // reading file, pck.req, as a possibility for future pck files.
838  // Look for a reference frame keyword for the body. The default is J2000.
839  QString naifKeyword = "BODY" + toString(centerBodyCode) + "_CONSTANTS_REF_FRAME" ;
840  SpiceInt numExpected;
841  SpiceInt numReturned;
842  SpiceChar naifType;
843  SpiceDouble relativeFrameCode = 0;
844  SpiceBoolean found;
845  dtpool_c(naifKeyword.toLatin1().data(), &found, &numExpected, &naifType);
846 
847  if (found) {
848  // Go get the frame name if it is not the default J2000
849  SpiceDouble relativeFrameCode;
850  bodvcd_c(centerBodyCode, "CONSTANTS_REF_FRAME", 1,
851  &numReturned, &relativeFrameCode);
852  }
853 
854  if (!found || relativeFrameCode == 1) { // We only work with J2000 relative frames for now
855 
856  // Make sure the standard coefficients are available for the body code by
857  // checking for ra
858  naifKeyword = "BODY" + toString(centerBodyCode) + "_POLE_RA" ;
859  dtpool_c(naifKeyword.toLatin1().data(), &found, &numExpected, &naifType);
860 
861  if (found) {
862  std::vector<SpiceDouble> d(3);
863  m_raPole.resize(numExpected);
864  m_decPole.resize(numExpected);
865  m_pm.resize(numExpected);
866 
867  bodvcd_c(centerBodyCode, "POLE_RA", numExpected, &numReturned, &d[0]);
868  m_raPole[0].setDegrees(d[0]);
869  m_raPole[1].setDegrees(d[1]);
870  m_raPole[2].setDegrees(d[2]);
871 
872  bodvcd_c(centerBodyCode, "POLE_DEC", numExpected, &numReturned, &d[0]);
873  m_decPole[0].setDegrees(d[0]);
874  m_decPole[1].setDegrees(d[1]);
875  m_decPole[2].setDegrees(d[2]);
876 
877  bodvcd_c(centerBodyCode, "PM", numExpected, &numReturned, &d[0]);
878  m_pm[0].setDegrees(d[0]);
879  m_pm[1].setDegrees(d[1]);
880  m_pm[2].setDegrees(d[2]);
881 
882  // ***TODO*** Get long axis value
884 
885  // Now check for nutation/precession terms. Check for nut/prec ra values
886  // first to see if the terms are even used for this body.
887  naifKeyword = "BODY" + toString(centerBodyCode) + "_NUT_PREC_RA" ;
888  dtpool_c(naifKeyword.toLatin1().data(), &found, &numReturned, &naifType);
889  if (found) {
890  // Get the barycenter (bc) linear coefficients first (2 for each period).
891  // Then we can get the maximum expected coefficients.
892  SpiceInt bcCode = centerBodyCode/100; // Ex: bc code for Jupiter (599) & its moons is 5
893  naifKeyword = "BODY" + toString(bcCode) + "_NUT_PREC_ANGLES" ;
894  dtpool_c(naifKeyword.toLatin1().data(), &found, &numExpected, &naifType);
895  std::vector<double>npAngles(numExpected, 0.);
896  bodvcd_c(bcCode, "NUT_PREC_ANGLES", numExpected, &numReturned, &npAngles[0]);
897  numExpected /= 2.;
898  m_raNutPrec.resize(numExpected, 0.);
899  m_decNutPrec.resize(numExpected, 0.);
900  m_pmNutPrec.resize(numExpected, 0.);
901 
902  std::vector<SpiceDouble> angles(numExpected);
903  bodvcd_c(centerBodyCode, "NUT_PREC_RA", numExpected, &numReturned, &m_raNutPrec[0]);
904  bodvcd_c(centerBodyCode, "NUT_PREC_DEC", numExpected, &numReturned, &m_decNutPrec[0]);
905  bodvcd_c(centerBodyCode, "NUT_PREC_PM", numExpected, &numReturned, &m_pmNutPrec[0]);
906 
907  // Finally get the system linear terms separated into vectors of the constants and the
908  // linear coefficients
909 
910  for (int i = 0; i < numExpected; i++) {
911  m_sysNutPrec0.push_back(Angle(npAngles[i*2], Angle::Degrees));
912  m_sysNutPrec1.push_back(Angle(npAngles[i*2+1], Angle::Degrees));
913  }
914  } // barycenter terms
915  } // PCK constants available
916  } // Relative to J2000
917  else {
919  }
920  } // PCK
921 
923  }
924 
925 
935 
936  // First clear existing cached data
937  m_raPole.clear();
938  m_decPole.clear();
939  m_pm.clear();
940  m_raNutPrec.clear();
941  m_decNutPrec.clear();
942  m_sysNutPrec0.clear();
943  m_sysNutPrec1.clear();
944  int numLoaded = 0;
945 
946  // Load the PCK coeffcients if they are on the label
947  if (label.hasKeyword("PoleRa")) {
948  PvlKeyword labelCoeffs = label["PoleRa"];
949  for (int i = 0; i < labelCoeffs.size(); i++){
950  m_raPole.push_back(Angle(toDouble(labelCoeffs[i]), Angle::Degrees));
951  }
952  numLoaded += 1;
953  }
954  if (label.hasKeyword("PoleDec")) {
955  PvlKeyword labelCoeffs = label["PoleDec"];
956  for (int i = 0; i < labelCoeffs.size(); i++){
957  m_decPole.push_back(Angle(toDouble(labelCoeffs[i]), Angle::Degrees));
958  }
959  numLoaded += 1;
960  }
961  if (label.hasKeyword("PrimeMeridian")) {
962  PvlKeyword labelCoeffs = label["PrimeMeridian"];
963  for (int i = 0; i < labelCoeffs.size(); i++){
964  m_pm.push_back(Angle(toDouble(labelCoeffs[i]), Angle::Degrees));
965  }
966  numLoaded += 1;
967  }
968  if (numLoaded > 2) m_tOrientationAvailable = true;
969 
970  if (label.hasKeyword("PoleRaNutPrec")) {
971  PvlKeyword labelCoeffs = label["PoleRaNutPrec"];
972  for (int i = 0; i < labelCoeffs.size(); i++){
973  m_raNutPrec.push_back(toDouble(labelCoeffs[i]));
974  }
975  }
976  if (label.hasKeyword("PoleDecNutPrec")) {
977  PvlKeyword labelCoeffs = label["PoleDecNutPrec"];
978  for (int i = 0; i < labelCoeffs.size(); i++){
979  m_decNutPrec.push_back(toDouble(labelCoeffs[i]));
980  }
981  }
982  if (label.hasKeyword("PmNutPrec")) {
983  PvlKeyword labelCoeffs = label["PmNutPrec"];
984  for (int i = 0; i < labelCoeffs.size(); i++){
985  m_pmNutPrec.push_back(toDouble(labelCoeffs[i]));
986  }
987  }
988  if (label.hasKeyword("SysNutPrec0")) {
989  PvlKeyword labelCoeffs = label["SysNutPrec0"];
990  for (int i = 0; i < labelCoeffs.size(); i++){
991  m_sysNutPrec0.push_back(Angle(toDouble(labelCoeffs[i]), Angle::Degrees));
992  }
993  }
994  if (label.hasKeyword("SysNutPrec1")) {
995  PvlKeyword labelCoeffs = label["SysNutPrec1"];
996  for (int i = 0; i < labelCoeffs.size(); i++){
997  m_sysNutPrec1.push_back(Angle(toDouble(labelCoeffs[i]), Angle::Degrees));
998  }
999  }
1000 
1002  }
1003 
1004 
1014  // Load the constant and time-based frame traces and the constant rotation
1015  // into the table as labels
1016  if (p_timeFrames.size() > 1) {
1017  table.Label() += PvlKeyword("TimeDependentFrames");
1018 
1019  for (int i = 0; i < (int) p_timeFrames.size(); i++) {
1020  table.Label()["TimeDependentFrames"].addValue(toString(p_timeFrames[i]));
1021  }
1022  }
1023 
1024  if (p_constantFrames.size() > 1) {
1025  table.Label() += PvlKeyword("ConstantFrames");
1026 
1027  for (int i = 0; i < (int) p_constantFrames.size(); i++) {
1028  table.Label()["ConstantFrames"].addValue(toString(p_constantFrames[i]));
1029  }
1030 
1031  table.Label() += PvlKeyword("ConstantRotation");
1032 
1033  for (int i = 0; i < (int) p_TC.size(); i++) {
1034  table.Label()["ConstantRotation"].addValue(toString(p_TC[i]));
1035  }
1036  }
1037 
1038  // Write original time coverage
1039  if (p_fullCacheStartTime != 0) {
1040  table.Label() += PvlKeyword("CkTableStartTime");
1041  table.Label()["CkTableStartTime"].addValue(toString(p_fullCacheStartTime));
1042  }
1043  if (p_fullCacheEndTime != 0) {
1044  table.Label() += PvlKeyword("CkTableEndTime");
1045  table.Label()["CkTableEndTime"].addValue(toString(p_fullCacheEndTime));
1046  }
1047  if (p_fullCacheSize != 0) {
1048  table.Label() += PvlKeyword("CkTableOriginalSize");
1049  table.Label()["CkTableOriginalSize"].addValue(toString(p_fullCacheSize));
1050  }
1051 
1052  // Begin section added 06-20-2015 DAC
1053  table.Label() += PvlKeyword("FrameTypeCode");
1054  table.Label()["FrameTypeCode"].addValue(toString(m_frameType));
1055 
1056  if (m_frameType == PCK) {
1057  // Write out all the body orientation constants
1058  // Pole right ascension coefficients for a quadratic equation
1059  table.Label() += PvlKeyword("PoleRa");
1060 
1061  for (int i = 0; i < (int) m_raPole.size(); i++) {
1062  table.Label()["PoleRa"].addValue(toString(m_raPole[i].degrees()));
1063  }
1064 
1065  // Pole right ascension, declination coefficients for a quadratic equation
1066  table.Label() += PvlKeyword("PoleDec");
1067  for (int i = 0; i < (int) m_decPole.size(); i++) {
1068  table.Label()["PoleDec"].addValue(toString(m_decPole[i].degrees()));
1069  }
1070 
1071  // Prime meridian coefficients for a quadratic equation
1072  table.Label() += PvlKeyword("PrimeMeridian");
1073  for (int i = 0; i < (int) m_pm.size(); i++) {
1074  table.Label()["PrimeMeridian"].addValue(toString(m_pm[i].degrees()));
1075  }
1076 
1077  if (m_raNutPrec.size() > 0) {
1078  // Pole right ascension nutation precision coefficients to the trig terms
1079  table.Label() += PvlKeyword("PoleRaNutPrec");
1080  for (int i = 0; i < (int) m_raNutPrec.size(); i++) {
1081  table.Label()["PoleRaNutPrec"].addValue(toString(m_raNutPrec[i]));
1082  }
1083 
1084  // Pole declination nutation precision coefficients to the trig terms
1085  table.Label() += PvlKeyword("PoleDecNutPrec");
1086  for (int i = 0; i < (int) m_decNutPrec.size(); i++) {
1087  table.Label()["PoleDecNutPrec"].addValue(toString(m_decNutPrec[i]));
1088  }
1089 
1090  // Prime meridian nutation precision coefficients to the trig terms
1091  table.Label() += PvlKeyword("PmNutPrec");
1092  for (int i = 0; i < (int) m_pmNutPrec.size(); i++) {
1093  table.Label()["PmNutPrec"].addValue(toString(m_pmNutPrec[i]));
1094  }
1095 
1096  // System nutation precision constant terms of linear model of periods
1097  table.Label() += PvlKeyword("SysNutPrec0");
1098  for (int i = 0; i < (int) m_sysNutPrec0.size(); i++) {
1099  table.Label()["SysNutPrec0"].addValue(toString(m_sysNutPrec0[i].degrees()));
1100  }
1101 
1102  // System nutation precision linear terms of linear model of periods
1103  table.Label() += PvlKeyword("SysNutPrec1");
1104  for (int i = 0; i < (int) m_sysNutPrec1.size(); i++) {
1105  table.Label()["SysNutPrec1"].addValue(toString(m_sysNutPrec1[i].degrees()));
1106  }
1107  }
1108  }
1109  // End section added 06-20-2015 DAC
1110 
1112  }
1113 
1114 
1120  std::vector<double> SpiceRotation::GetCenterAngles() {
1121  // Compute the center time
1122  double etCenter = (p_fullCacheEndTime + p_fullCacheStartTime) / 2.;
1123  SetEphemerisTime(etCenter);
1124 
1125  return Angles(p_axis3, p_axis2, p_axis1);
1126  }
1127 
1128 
1139  std::vector<double> SpiceRotation::Angles(int axis3, int axis2, int axis1) {
1141 
1142  SpiceDouble ang1, ang2, ang3;
1143  m2eul_c((SpiceDouble *) &p_CJ[0], axis3, axis2, axis1, &ang3, &ang2, &ang1);
1144 
1145  std::vector<double> angles;
1146  angles.push_back(ang1);
1147  angles.push_back(ang2);
1148  angles.push_back(ang3);
1149 
1151  return angles;
1152  }
1153 
1154 
1155 
1166  void SpiceRotation::SetAngles(std::vector<double> angles, int axis3, int axis2, int axis1) {
1167  eul2m_c(angles[2], angles[1], angles[0], axis3, axis2, axis1, (SpiceDouble (*)[3]) &(p_CJ[0]));
1168  p_cache[0] = p_CJ;
1169  // Reset to get the new values
1170  p_et = -DBL_MAX;
1172  }
1173 
1174 
1180  std::vector<double> SpiceRotation::AngularVelocity() {
1181  return p_av;
1182  }
1183 
1184 
1192  return p_constantFrames;
1193  }
1194 
1195 
1201  std::vector<int> SpiceRotation::TimeFrameChain() {
1202  return p_timeFrames;
1203  }
1204 
1205 
1212  return p_hasAngularVelocity;
1213  }
1214 
1215 
1223  std::vector<double> SpiceRotation::J2000Vector(const std::vector<double> &rVec) {
1225 
1226  std::vector<double> jVec;
1227 
1228  if (rVec.size() == 3) {
1229  double TJ[3][3];
1230  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], TJ);
1231  jVec.resize(3);
1232  mtxv_c(TJ, (SpiceDouble *) &rVec[0], (SpiceDouble *) &jVec[0]);
1233  }
1234 
1235  else if (rVec.size() == 6) {
1236  // See Naif routine frmchg for the format of the state matrix. The constant rotation, TC,
1237  // has a derivative with respect to time of I.
1238  if (!p_hasAngularVelocity) {
1239  // throw an error
1240  }
1241  std::vector<double> stateTJ(36);
1242  stateTJ = StateTJ();
1243 
1244  // Now invert (inverse of a state matrix is NOT simply the transpose)
1245  xpose6_c(&stateTJ[0], (SpiceDouble( *) [6]) &stateTJ[0]);
1246  double stateJT[6][6];
1247  invstm_((doublereal *) &stateTJ[0], (doublereal *) stateJT);
1248  xpose6_c(stateJT, stateJT);
1249  jVec.resize(6);
1250 
1251  mxvg_c(stateJT, (SpiceDouble *) &rVec[0], 6, 6, (SpiceDouble *) &jVec[0]);
1252  }
1253 
1255  return (jVec);
1256  }
1257 
1258 
1274  std::vector<Angle> SpiceRotation::poleRaCoefs() {
1275  return m_raPole;
1276  }
1277 
1278 
1294  std::vector<Angle> SpiceRotation::poleDecCoefs() {
1295  return m_decPole;
1296  }
1297 
1298 
1313  std::vector<Angle> SpiceRotation::pmCoefs() {
1314  return m_pm;
1315  }
1316 
1317 
1331  std::vector<double> SpiceRotation::poleRaNutPrecCoefs() {
1332  return m_raNutPrec;
1333  }
1334 
1335 
1348  std::vector<double> SpiceRotation::poleDecNutPrecCoefs() {
1349  return m_decNutPrec;
1350  }
1351 
1352 
1365  std::vector<double> SpiceRotation::pmNutPrecCoefs() {
1366  return m_pmNutPrec;
1367  }
1368 
1369 
1382  std::vector<Angle> SpiceRotation::sysNutPrecConstants() {
1383  return m_sysNutPrec0;
1384  }
1385 
1386 
1400  std::vector<Angle> SpiceRotation::sysNutPrecCoefs() {
1401  return m_sysNutPrec1;
1402  }
1403 
1404 
1424  std::vector<double> SpiceRotation::toJ2000Partial(const std::vector<double> &lookT,
1425  SpiceRotation::PartialType partialVar,
1426  int coeffIndex) {
1428 
1429  std::vector<double> jVec;
1430 
1431  // Get the rotation angles and form the derivative matrix for the partialVar
1432  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1433  int angleIndex = partialVar;
1434  int axes[3] = {p_axis1, p_axis2, p_axis3};
1435 
1436  double angle = angles.at(angleIndex);
1437 
1438  // Get TJ and apply the transpose to the input vector to get it to J2000
1439  double dmatrix[3][3];
1440  drotat_(&angle, (integer *) axes + angleIndex, (doublereal *) dmatrix);
1441  // Transpose to obtain row-major order
1442  xpose_c(dmatrix, dmatrix);
1443 
1444  // Get the derivative of the polynomial with respect to partialVar
1445  double dpoly = 0.;
1446  QString msg;
1447  switch (m_frameType) {
1448  case CK:
1449  case DYN:
1450  dpoly = DPolynomial(coeffIndex);
1451  break;
1452  case PCK:
1453  dpoly = DPckPolynomial(partialVar, coeffIndex);
1454  break;
1455  case BPC:
1456  msg = "Body rotation uses a binary PCK. Solutions for this model are not supported";
1457  throw IException(IException::User, msg, _FILEINFO_);
1458  break;
1459  case NOTJ2000PCK:
1460  msg = "Body rotation uses a PCK not referenced to J2000. "
1461  "Solutions for this model are not supported";
1462  throw IException(IException::User, msg, _FILEINFO_);
1463  break;
1464  default:
1465  msg = "Solutions are not supported for this frame type.";
1466  throw IException(IException::User, msg, _FILEINFO_);
1467  }
1468 
1469  // Multiply dpoly to complete dmatrix
1470  for (int row = 0; row < 3; row++) {
1471  for (int col = 0; col < 3; col++) {
1472  dmatrix[row][col] *= dpoly;
1473  }
1474  }
1475 
1476  // Apply the other 2 angles and chain them all together to get J2000 to constant frame
1477  double dCJ[3][3];
1478  switch (angleIndex) {
1479  case 0:
1480  rotmat_c(dmatrix, angles[1], axes[1], dCJ);
1481  rotmat_c(dCJ, angles[2], axes[2], dCJ);
1482  break;
1483  case 1:
1484  rotate_c(angles[0], axes[0], dCJ);
1485  mxm_c(dmatrix, dCJ, dCJ);
1486  rotmat_c(dCJ, angles[2], axes[2], dCJ);
1487  break;
1488  case 2:
1489  rotate_c(angles[0], axes[0], dCJ);
1490  rotmat_c(dCJ, angles[1], axes[1], dCJ);
1491  mxm_c(dmatrix, dCJ, dCJ);
1492  break;
1493  }
1494 
1495  // Multiply the constant matrix to rotate to target reference frame
1496  double dTJ[3][3];
1497  mxm_c((SpiceDouble *) &p_TC[0], dCJ[0], dTJ);
1498 
1499  // Finally rotate the target vector with the transpose of the
1500  // derivative matrix, dTJ to get a J2000 vector
1501  std::vector<double> lookdJ(3);
1502 
1503  mtxv_c(dTJ, (const SpiceDouble *) &lookT[0], (SpiceDouble *) &lookdJ[0]);
1504 
1506  return (lookdJ);
1507  }
1508 
1509 
1517  std::vector<double> SpiceRotation::ReferenceVector(const std::vector<double> &jVec) {
1519 
1520  std::vector<double> rVec(3);
1521 
1522  if (jVec.size() == 3) {
1523  double TJ[3][3];
1524  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], TJ);
1525  rVec.resize(3);
1526  mxv_c(TJ, (SpiceDouble *) &jVec[0], (SpiceDouble *) &rVec[0]);
1527  }
1528  else if (jVec.size() == 6) {
1529  // See Naif routine frmchg for the format of the state matrix. The constant rotation, TC,
1530  // has a derivative with respect to time of I.
1531  if (!p_hasAngularVelocity) {
1532  // throw an error
1533  }
1534  std::vector<double> stateTJ(36);
1535  stateTJ = StateTJ();
1536  rVec.resize(6);
1537  mxvg_c((SpiceDouble *) &stateTJ[0], (SpiceDouble *) &jVec[0], 6, 6, (SpiceDouble *) &rVec[0]);
1538  }
1539 
1541  return (rVec);
1542  }
1543 
1544 
1558  std::vector<double> coeffAng1, coeffAng2, coeffAng3;
1559 
1560  // Rotation is already stored as a polynomial -- throw an error
1561  if (p_source == PolyFunction) {
1562  // Nothing to do
1563  return;
1564 // QString msg = "Rotation already fit to a polynomial -- spiceint first to refit";
1565 // throw IException(IException::User,msg,_FILEINFO_);
1566  }
1567 
1568  // Adjust degree of polynomial on available data
1569  if (p_cache.size() == 1) {
1570  p_degree = 0;
1571  }
1572  else if (p_cache.size() == 2) {
1573  p_degree = 1;
1574  }
1575 
1576  //Check for polynomial over original pointing constant and initialize coefficients
1577  if (type == PolyFunctionOverSpice) {
1578  coeffAng1.assign(p_degree + 1, 0.);
1579  coeffAng2.assign(p_degree + 1, 0.);
1580  coeffAng3.assign(p_degree + 1, 0.);
1581  SetPolynomial(coeffAng1, coeffAng2, coeffAng3, type);
1582  return;
1583  }
1584 
1588  //
1589  LeastSquares *fitAng1 = new LeastSquares(function1);
1590  LeastSquares *fitAng2 = new LeastSquares(function2);
1591  LeastSquares *fitAng3 = new LeastSquares(function3);
1592 
1593  // Compute the base time
1594  ComputeBaseTime();
1595  std::vector<double> time;
1596 
1597  if (p_cache.size() == 1) {
1598  double t = p_cacheTime.at(0);
1599  SetEphemerisTime(t);
1600  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1601  coeffAng1.push_back(angles[0]);
1602  coeffAng2.push_back(angles[1]);
1603  coeffAng3.push_back(angles[2]);
1604  }
1605  else if (p_cache.size() == 2) {
1606 // Load the times and get the corresponding rotation angles
1607  p_degree = 1;
1608  double t1 = p_cacheTime.at(0);
1609  SetEphemerisTime(t1);
1610  t1 -= p_baseTime;
1611  t1 = t1 / p_timeScale;
1612  std::vector<double> angles1 = Angles(p_axis3, p_axis2, p_axis1);
1613  double t2 = p_cacheTime.at(1);
1614  SetEphemerisTime(t2);
1615  t2 -= p_baseTime;
1616  t2 = t2 / p_timeScale;
1617  std::vector<double> angles2 = Angles(p_axis3, p_axis2, p_axis1);
1618  angles2[0] = WrapAngle(angles1[0], angles2[0]);
1619  angles2[2] = WrapAngle(angles1[2], angles2[2]);
1620  double slope[3];
1621  double intercept[3];
1622 
1623 // Compute the linear equation for each angle and save them
1624  for (int angleIndex = 0; angleIndex < 3; angleIndex++) {
1625  Isis::LineEquation angline(t1, angles1[angleIndex], t2, angles2[angleIndex]);
1626  slope[angleIndex] = angline.Slope();
1627  intercept[angleIndex] = angline.Intercept();
1628  }
1629  coeffAng1.push_back(intercept[0]);
1630  coeffAng1.push_back(slope[0]);
1631  coeffAng2.push_back(intercept[1]);
1632  coeffAng2.push_back(slope[1]);
1633  coeffAng3.push_back(intercept[2]);
1634  coeffAng3.push_back(slope[2]);
1635  }
1636  else {
1637  // Load the known values to compute the fit equation
1638  double start1 = 0.; // value of 1st angle1 in cache
1639  double start3 = 0.; // value of 1st angle1 in cache
1640 
1641  for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
1642  double t = p_cacheTime.at(pos);
1643  time.push_back((t - p_baseTime) / p_timeScale);
1644  SetEphemerisTime(t);
1645  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1646 
1647 // Fix 180/-180 crossovers on angles 1 and 3 before doing fit.
1648  if (pos == 0) {
1649  start1 = angles[0];
1650  start3 = angles[2];
1651  }
1652  else {
1653  angles[0] = WrapAngle(start1, angles[0]);
1654  angles[2] = WrapAngle(start3, angles[2]);
1655  }
1656 
1657  fitAng1->AddKnown(time, angles[0]);
1658  fitAng2->AddKnown(time, angles[1]);
1659  fitAng3->AddKnown(time, angles[2]);
1660  time.clear();
1661 
1662  }
1663  //Solve the equations for the coefficients
1664  fitAng1->Solve();
1665  fitAng2->Solve();
1666  fitAng3->Solve();
1667 
1668  // Delete the least squares objects now that we have all the coefficients
1669  delete fitAng1;
1670  delete fitAng2;
1671  delete fitAng3;
1672 
1673  // For now assume all three angles are fit to a polynomial. Later they may
1674  // each be fit to a unique basis function.
1675  // Fill the coefficient vectors
1676 
1677  for (int i = 0; i < function1.Coefficients(); i++) {
1678  coeffAng1.push_back(function1.Coefficient(i));
1679  coeffAng2.push_back(function2.Coefficient(i));
1680  coeffAng3.push_back(function3.Coefficient(i));
1681  }
1682 
1683  }
1684 
1685  // Now that the coefficients have been calculated set the polynomial with them
1686  SetPolynomial(coeffAng1, coeffAng2, coeffAng3);
1687 
1689  return;
1690  }
1691 
1692 
1707  void SpiceRotation::SetPolynomial(const std::vector<double> &coeffAng1,
1708  const std::vector<double> &coeffAng2,
1709  const std::vector<double> &coeffAng3,
1710  const Source type) {
1711 
1716 
1717  // Load the functions with the coefficients
1718  function1.SetCoefficients(coeffAng1);
1719  function2.SetCoefficients(coeffAng2);
1720  function3.SetCoefficients(coeffAng3);
1721 
1722  // Compute the base time
1723  ComputeBaseTime();
1724 
1725  // Save the current coefficients
1726  p_coefficients[0] = coeffAng1;
1727  p_coefficients[1] = coeffAng2;
1728  p_coefficients[2] = coeffAng3;
1729 
1730  // Set the flag indicating p_degree has been applied to the camera angles, the
1731  // coefficients of the polynomials have been saved, and the cache reloaded from
1732  // the polynomials
1733  p_degreeApplied = true;
1734  // p_source = PolyFunction;
1735  p_source = type;
1736 
1737  // Update the current rotation
1738  double et = p_et;
1739  p_et = -DBL_MAX;
1740  SetEphemerisTime(et);
1741 
1743  return;
1744  }
1745 
1746 
1765 
1766  // Check to see if rotation is already stored as a polynomial
1767  if (p_source == PckPolyFunction) {
1768  p_hasAngularVelocity = true;
1769  return;
1770  }
1771 
1772  // No target body orientation information available -- throw an error
1773  if (!m_tOrientationAvailable) {
1774  QString msg = "Target body orientation information not available. Rerun spiceinit.";
1775  throw IException(IException::User, msg, _FILEINFO_);
1776  }
1777 
1778  /* I think we have nothing to do, but check for available coefficients and set the Source
1779  // Set the scales
1780  p_dscale = 0.000011574074074; // seconds to days conversion
1781  p_tscale = 3.1688087814029D-10; // seconds to Julian centuries conversion
1782  //Set the quadratic part of the equations
1783  //TODO consider just hard coding this part in evaluate
1784  Isis::PolynomialUnivariate functionRa(p_degree); // Basis function fit to target body pole ra
1785  Isis::PolynomialUnivariate functionDec(p_degree); // Basis function fit to target body pole dec
1786  Isis::PolynomialUnivariate functionPm(p_degree); // Basis function fit to target body pm
1787 
1788  // Load the functions with the coefficients
1789  function1.SetCoefficients(p_raPole);
1790  function2.SetCoefficients(p_decPole);
1791  function3.SetCoefficients(p_pm);
1792 
1793  int numNutPrec = p_sysNutPrec0.size;
1794  if (numNutPrec > 0) {
1795  // Set the trigonometric part of the equation
1796  Isis::TrigBasis functionRaNutPrec("raNutPrec", Isis::TrigBasis::Sin, numNutPrec);
1797  Isis::TrigBasis functionRaNutPrec("decNutPrec", Isis::TrigBasis::Cos, numNutPrec);
1798  Isis::TrigBasis functionRaNutPrec("pmNutPrec", Isis::TrigBasis::Sin, numNutPrec);
1799  // Load the functions with the coefficients
1800  functionRaNutPrec.SetTCoefficients(p_raNutPrec);
1801  functionDecNutPrec.SetTCoefficients(p_decNutPrec);
1802  functionPmNutPrec.SetTCoefficients(p_pmNutPrec);
1803  functionRaNutPrec.SetConstants(p_sysNutPrec0);
1804  functionDecNutPrec.SetConstants(p_sysNutPrec0);
1805  functionPmNutPrec.SetConstants(p_sysNutPrec0);
1806  functionRaNutPrec.SetLCoefficients(p_sysNutPrec1);
1807  functionDecNutPrec.SetLCoefficients(p_sysNutPrec1);
1808  functionPmNutPrec.SetLCoefficients(p_sysNutPrec1);
1809  }
1810  */
1811 
1812  // Set the flag indicating p_degree has been applied to the planet angles, the
1813  // coefficients of the polynomials have been saved, and the cache reloaded from
1814  // TODO cache reloaded???
1815  // the polynomials.
1816  // ****At least for the planet angles, I don't think we want to reload the cache until just
1817  // before we write out the table
1818  p_degreeApplied = true;
1819  p_hasAngularVelocity = true;
1821  return;
1822  }
1823 
1824 
1844  void SpiceRotation::setPckPolynomial(const std::vector<Angle> &raCoeff,
1845  const std::vector<Angle> &decCoeff,
1846  const std::vector<Angle> &pmCoeff) {
1847  // Just set the constants and let usePckPolynomial() do the rest
1848  m_raPole = raCoeff;
1849  m_decPole = decCoeff;
1850  m_pm = pmCoeff;
1851  usePckPolynomial();
1852  // Apply new function parameters
1854  }
1855 
1856 
1867  void SpiceRotation::GetPolynomial(std::vector<double> &coeffAng1,
1868  std::vector<double> &coeffAng2,
1869  std::vector<double> &coeffAng3) {
1870  coeffAng1 = p_coefficients[0];
1871  coeffAng2 = p_coefficients[1];
1872  coeffAng3 = p_coefficients[2];
1873 
1874  return;
1875  }
1876 
1877 
1889  void SpiceRotation::getPckPolynomial(std::vector<Angle> &raCoeff,
1890  std::vector<Angle> &decCoeff,
1891  std::vector<Angle> &pmCoeff) {
1892  raCoeff = m_raPole;
1893  decCoeff = m_decPole;
1894  pmCoeff = m_pm;
1895 
1896  return;
1897  }
1898 
1899 
1904  if (p_noOverride) {
1905  p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1907  // Take care of case where 1st and last times are the same
1908  if (p_timeScale == 0) p_timeScale = 1.0;
1909  }
1910  else {
1913  }
1914 
1915  return;
1916  }
1917 
1918 
1926  void SpiceRotation::SetOverrideBaseTime(double baseTime, double timeScale) {
1927  p_overrideBaseTime = baseTime;
1928  p_overrideTimeScale = timeScale;
1929  p_noOverride = false;
1930  return;
1931  }
1932 
1933  void SpiceRotation::SetCacheTime(std::vector<double> cacheTime) {
1934  // Do not reset the cache times if they are already loaded.
1935  if (p_cacheTime.size() <= 0) {
1936  p_cacheTime = cacheTime;
1937  }
1938  }
1939 
1953  double SpiceRotation::DPolynomial(const int coeffIndex) {
1954  double derivative;
1955  double time = (p_et - p_baseTime) / p_timeScale;
1956 
1957  if (coeffIndex > 0 && coeffIndex <= p_degree) {
1958  derivative = pow(time, coeffIndex);
1959  }
1960  else if (coeffIndex == 0) {
1961  derivative = 1;
1962  }
1963  else {
1964  QString msg = "Unable to evaluate the derivative of the SPICE rotation fit polynomial for "
1965  "the given coefficient index [" + toString(coeffIndex) + "]. "
1966  "Index is negative or exceeds degree of polynomial ["
1967  + toString(p_degree) + "]";
1969  }
1970  return derivative;
1971  }
1972 
1973 
1989  const int coeffIndex) {
1990  const double p_dayScale = 86400; // number of seconds in a day
1991  // Number of 24 hour days in a Julian century = 36525
1992  const double p_centScale = p_dayScale * 36525;
1993  double time = 0.;
1994 
1995  switch (partialVar) {
1996  case WRT_RightAscension:
1997  case WRT_Declination:
1998  time = p_et / p_centScale;
1999  break;
2000  case WRT_Twist:
2001  time = p_et / p_dayScale;
2002  break;
2003  }
2004 
2005  double derivative;
2006 
2007  switch (coeffIndex) {
2008  case 0:
2009  derivative = 1;
2010  break;
2011  case 1:
2012  case 2:
2013  derivative = pow(time, coeffIndex);
2014  break;
2015  default:
2016  QString msg = "Unable to evaluate the derivative of the target body rotation fit polynomial "
2017  "for the given coefficient index [" + toString(coeffIndex) + "]. "
2018  "Index is negative or exceeds degree of polynomial ["
2019  + toString(p_degree) + "]";
2021  }
2022 
2023  // Now apply any difference to the partials due to building the rotation matrix on the angles
2024  // 90. + ra, 90. - dec, and pm. The only partial that changes is dec.
2025  if (partialVar == WRT_Declination) {
2026  derivative = -derivative;
2027  }
2028 
2029  return derivative;
2030  }
2031 
2032 
2048  std::vector<double> SpiceRotation::ToReferencePartial(std::vector<double> &lookJ,
2049  SpiceRotation::PartialType partialVar,
2050  int coeffIndex) {
2052  //TODO** To save time possibly save partial matrices
2053 
2054  // Get the rotation angles and form the derivative matrix for the partialVar
2055  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
2056  int angleIndex = partialVar;
2057  int axes[3] = {p_axis1, p_axis2, p_axis3};
2058 
2059  double angle = angles.at(angleIndex);
2060 
2061  double dmatrix[3][3];
2062  drotat_(&angle, (integer *) axes + angleIndex, (doublereal *) dmatrix);
2063  // Transpose to obtain row-major order
2064  xpose_c(dmatrix, dmatrix);
2065 
2066  // Get the derivative of the polynomial with respect to partialVar
2067  double dpoly = 0.;
2068  switch (m_frameType) {
2069  case UNKNOWN: // For now let everything go through for backward compatability
2070  case CK:
2071  case DYN:
2072  dpoly = DPolynomial(coeffIndex);
2073  break;
2074  case PCK:
2075  dpoly = DPckPolynomial(partialVar, coeffIndex);
2076  break;
2077  default:
2078  QString msg = "Only CK, DYN, and PCK partials can be calculated";
2080  }
2081 
2082  // Multiply dpoly to complete dmatrix
2083  for (int row = 0; row < 3; row++) {
2084  for (int col = 0; col < 3; col++) {
2085  dmatrix[row][col] *= dpoly;
2086  }
2087  }
2088 
2089  // Apply the other 2 angles and chain them all together
2090  double dCJ[3][3];
2091  switch (angleIndex) {
2092  case 0:
2093  rotmat_c(dmatrix, angles[1], axes[1], dCJ);
2094  rotmat_c(dCJ, angles[2], axes[2], dCJ);
2095  break;
2096  case 1:
2097  rotate_c(angles[0], axes[0], dCJ);
2098  mxm_c(dmatrix, dCJ, dCJ);
2099  rotmat_c(dCJ, angles[2], axes[2], dCJ);
2100  break;
2101  case 2:
2102  rotate_c(angles[0], axes[0], dCJ);
2103  rotmat_c(dCJ, angles[1], axes[1], dCJ);
2104  mxm_c(dmatrix, dCJ, dCJ);
2105  break;
2106  }
2107 
2108  // Multiply the constant matrix to rotate to the targeted reference frame
2109  double dTJ[3][3];
2110  mxm_c((SpiceDouble *) &p_TC[0], dCJ[0], dTJ);
2111 
2112  // Finally rotate the J2000 vector with the derivative matrix, dTJ to
2113  // get the vector in the targeted reference frame.
2114  std::vector<double> lookdT(3);
2115 
2116  mxv_c(dTJ, (const SpiceDouble *) &lookJ[0], (SpiceDouble *) &lookdT[0]);
2117 
2119  return lookdT;
2120  }
2121 
2122 
2131  double SpiceRotation::WrapAngle(double compareAngle, double angle) {
2133  double diff1 = compareAngle - angle;
2134 
2135  if (diff1 < -1 * pi_c()) {
2136  angle -= twopi_c();
2137  }
2138  else if (diff1 > pi_c()) {
2139  angle += twopi_c();
2140  }
2141 
2143  return angle;
2144  }
2145 
2146 
2160  // Adjust the degree for the data
2161  if (p_fullCacheSize == 1) {
2162  degree = 0;
2163  }
2164  else if (p_fullCacheSize == 2) {
2165  degree = 1;
2166  }
2167  // If polynomials have not been applied yet then simply set the degree and return
2168  if (!p_degreeApplied) {
2169  p_degree = degree;
2170  }
2171 
2172  // Otherwise the existing polynomials need to be either expanded ...
2173  else if (p_degree < degree) { // (increase the number of terms)
2174  std::vector<double> coefAngle1(p_coefficients[0]),
2175  coefAngle2(p_coefficients[1]),
2176  coefAngle3(p_coefficients[2]);
2177 
2178  for (int icoef = p_degree + 1; icoef <= degree; icoef++) {
2179  coefAngle1.push_back(0.);
2180  coefAngle2.push_back(0.);
2181  coefAngle3.push_back(0.);
2182  }
2183  p_degree = degree;
2184  SetPolynomial(coefAngle1, coefAngle2, coefAngle3, p_source);
2185  }
2186  // ... or reduced (decrease the number of terms)
2187  else if (p_degree > degree) {
2188  std::vector<double> coefAngle1(degree + 1),
2189  coefAngle2(degree + 1),
2190  coefAngle3(degree + 1);
2191 
2192  for (int icoef = 0; icoef <= degree; icoef++) {
2193  coefAngle1[icoef] = p_coefficients[0][icoef];
2194  coefAngle2[icoef] = p_coefficients[1][icoef];
2195  coefAngle3[icoef] = p_coefficients[2][icoef];
2196  }
2197  p_degree = degree;
2198  SetPolynomial(coefAngle1, coefAngle2, coefAngle3, p_source);
2199  }
2200  }
2201 
2202 
2209  return m_frameType;
2210  }
2211 
2212 
2219  return p_source;
2220  }
2221 
2222 
2229  p_source = source;
2230  return;
2231  }
2232 
2233 
2240  return p_baseTime;
2241  }
2242 
2243 
2250  return p_timeScale;
2251  }
2252 
2253 
2266  void SpiceRotation::SetAxes(int axis1, int axis2, int axis3) {
2267  if (axis1 < 1 || axis2 < 1 || axis3 < 1 || axis1 > 3 || axis2 > 3 || axis3 > 3) {
2268  QString msg = "A rotation axis is outside the valid range of 1 to 3";
2270  }
2271  p_axis1 = axis1;
2272  p_axis2 = axis2;
2273  p_axis3 = axis3;
2274  }
2275 
2276 
2290  int count = 0;
2291 
2292  double observStart = p_fullCacheStartTime + p_timeBias;
2293  double observEnd = p_fullCacheEndTime + p_timeBias;
2294  // Next line added 12-03-2009 to allow observations to cross segment boundaries
2295  double currentTime = observStart;
2296  bool timeLoaded = false;
2297 
2298  // Get number of ck loaded for this rotation. This method assumes only one SpiceRotation
2299  // object is loaded.
2301  ktotal_c("ck", (SpiceInt *) &count);
2302 
2303  // Downsize the loaded cache
2304  if ((p_source == Memcache) && p_minimizeCache == Yes) {
2305  // Multiple ck case, type 5 ck case, or PolyFunctionOverSpice
2306  // final step -- downsize loaded cache and reload
2307 
2308  if (p_fullCacheSize != (int) p_cache.size()) {
2309 
2310  QString msg =
2311  "Full cache size does NOT match cache size in LoadTimeCache -- should never happen";
2313  }
2314 
2315  SpiceDouble timeSclkdp[p_fullCacheSize];
2316  SpiceDouble quats[p_fullCacheSize][4];
2317  double avvs[p_fullCacheSize][3];// Angular velocity vector
2318 
2319  // We will treat et as the sclock time and avoid converting back and forth
2320  for (int r = 0; r < p_fullCacheSize; r++) {
2321  timeSclkdp[r] = p_cacheTime[r];
2322  SpiceDouble CJ[9] = { p_cache[r][0], p_cache[r][1], p_cache[r][2],
2323  p_cache[r][3], p_cache[r][4], p_cache[r][5],
2324  p_cache[r][6], p_cache[r][7], p_cache[r][8]
2325  };
2326  m2q_c(CJ, quats[r]);
2327  if (p_hasAngularVelocity) {
2328  vequ_c((SpiceDouble *) &p_cacheAv[r][0], avvs[r]);
2329  }
2330  }
2331 
2332  double cubeStarts = timeSclkdp[0]; //,timsSclkdp[ckBlob.Records()-1] };
2333  double radTol = 0.000000017453; //.000001 degrees Make this instrument dependent TODO
2334  SpiceInt avflag = 1; // Don't use angular velocity for now
2335  SpiceInt nints = 1; // Always make an observation a single interpolation interval
2336  double dparr[p_fullCacheSize]; // Double precision work array
2337  SpiceInt intarr[p_fullCacheSize]; // Integer work array
2338  SpiceInt sizOut = p_fullCacheSize; // Size of downsized cache
2339 
2340  ck3sdn(radTol, avflag, (int *) &sizOut, timeSclkdp, (doublereal *) quats,
2341  (SpiceDouble *) avvs, nints, &cubeStarts, dparr, (int *) intarr);
2342 
2343  // Clear full cache and load with downsized version
2344  p_cacheTime.clear();
2345  p_cache.clear();
2346  p_cacheAv.clear();
2347  std::vector<double> av;
2348  av.resize(3);
2349 
2350  for (int r = 0; r < sizOut; r++) {
2351  SpiceDouble et;
2352  // sct2e_c(spcode, timeSclkdp[r], &et);
2353  et = timeSclkdp[r];
2354  p_cacheTime.push_back(et);
2355  std::vector<double> CJ(9);
2356  q2m_c(quats[r], (SpiceDouble( *)[3]) &CJ[0]);
2357  p_cache.push_back(CJ);
2358  vequ_c(avvs[r], (SpiceDouble *) &av[0]);
2359  p_cacheAv.push_back(av);
2360  }
2361 
2362  timeLoaded = true;
2364  }
2365  else if (count == 1 && p_minimizeCache == Yes) {
2366  // case of a single ck -- read instances and data straight from kernel for given time range
2367  SpiceInt handle;
2368 
2369  // Define some Naif constants
2370  int FILESIZ = 128;
2371  int TYPESIZ = 32;
2372  int SOURCESIZ = 128;
2373 // double DIRSIZ = 100;
2374 
2375  SpiceChar file[FILESIZ];
2376  SpiceChar filtyp[TYPESIZ]; // kernel type (ck, ek, etc.)
2377  SpiceChar source[SOURCESIZ];
2378 
2379  SpiceBoolean found;
2380  bool observationSpansToNextSegment = false;
2381 
2382  double segStartEt;
2383  double segStopEt;
2384 
2385  kdata_c(0, "ck", FILESIZ, TYPESIZ, SOURCESIZ, file, filtyp, source, &handle, &found);
2386  dafbfs_c(handle);
2387  daffna_c(&found);
2388  int spCode = ((int)(p_constantFrames[0] / 1000)) * 1000;
2389 
2390  while (found) {
2391  observationSpansToNextSegment = false;
2392  double sum[10]; // daf segment summary
2393  double dc[2]; // segment starting and ending times in tics
2394  SpiceInt ic[6]; // segment summary values:
2395  // instrument code for platform,
2396  // reference frame code,
2397  // data type,
2398  // velocity flag,
2399  // offset to quat 1,
2400  // offset to end.
2401  dafgs_c(sum);
2402  dafus_c(sum, (SpiceInt) 2, (SpiceInt) 6, dc, ic);
2403 
2404  // Don't read type 5 ck here
2405  if (ic[2] == 5) break;
2406 
2407  // Check times for type 3 ck segment if spacecraft matches
2408  if (ic[0] == spCode && ic[2] == 3) {
2409  sct2e_c((int) spCode / 1000, dc[0], &segStartEt);
2410  sct2e_c((int) spCode / 1000, dc[1], &segStopEt);
2412  double et;
2413 
2414  // Get times for this segment
2415  if (currentTime >= segStartEt && currentTime <= segStopEt) {
2416 
2417  // Check for a gap in the time coverage by making sure the time span of the observation
2418  // does not cross a segment unless the next segment starts where the current one ends
2419  if (observationSpansToNextSegment && currentTime > segStartEt) {
2420  QString msg = "Observation crosses segment boundary--unable to interpolate pointing";
2422  }
2423  if (observEnd > segStopEt) {
2424  observationSpansToNextSegment = true;
2425  }
2426 
2427  // Extract necessary header parameters
2428  int dovelocity = ic[3];
2429  int end = ic[5];
2430  double val[2];
2431  dafgda_c(handle, end - 1, end, val);
2432 // int nints = (int) val[0];
2433  int ninstances = (int) val[1];
2434  int numvel = dovelocity * 3;
2435  int quatnoff = ic[4] + (4 + numvel) * ninstances - 1;
2436 // int nrdir = (int) (( ninstances - 1 ) / DIRSIZ); /* sclkdp directory records */
2437  int sclkdp1off = quatnoff + 1;
2438  int sclkdpnoff = sclkdp1off + ninstances - 1;
2439 // int start1off = sclkdpnoff + nrdir + 1;
2440 // int startnoff = start1off + nints - 1;
2441  int sclkSpCode = spCode / 1000;
2442 
2443  // Now get the times
2444  std::vector<double> sclkdp(ninstances);
2445  dafgda_c(handle, sclkdp1off, sclkdpnoff, (SpiceDouble *) &sclkdp[0]);
2446 
2447  int instance = 0;
2448  sct2e_c(sclkSpCode, sclkdp[0], &et);
2449 
2450  while (instance < (ninstances - 1) && et < currentTime) {
2451  instance++;
2452  sct2e_c(sclkSpCode, sclkdp[instance], &et);
2453  }
2454 
2455  if (instance > 0) instance--;
2456  sct2e_c(sclkSpCode, sclkdp[instance], &et);
2457 
2458  while (instance < (ninstances - 1) && et < observEnd) {
2459  p_cacheTime.push_back(et - p_timeBias);
2460  instance++;
2461  sct2e_c(sclkSpCode, sclkdp[instance], &et);
2462  }
2463  p_cacheTime.push_back(et - p_timeBias);
2464 
2465  if (!observationSpansToNextSegment) {
2466  timeLoaded = true;
2468  break;
2469  }
2470  else {
2471  currentTime = segStopEt;
2472  }
2473  }
2474  }
2475  dafcs_c(handle); // Continue search in daf last searched
2476  daffna_c(&found); // Find next forward array in current daf
2477  }
2478  }
2479  else if (count == 0 && p_source != Nadir && p_minimizeCache == Yes) {
2480  QString msg = "No camera kernels loaded...Unable to determine time cache to downsize";
2481  throw IException(IException::User, msg, _FILEINFO_);
2482  }
2483 
2484  // Load times according to cache size (body rotations) -- handle first round of type 5 ck case
2485  // and multiple ck case --Load a time for every line scan line and downsize later
2486  if (! (timeLoaded || (p_cacheTime.size() > 1))) {
2487  double cacheSlope = 0.0;
2488  if (p_fullCacheSize > 1)
2489  cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) / (double)(p_fullCacheSize - 1);
2490  for (int i = 0; i < p_fullCacheSize; i++)
2491  p_cacheTime.push_back(p_fullCacheStartTime + (double) i * cacheSlope);
2492  if (p_source == Nadir) {
2493  p_minimizeCache = No;
2494  }
2495  }
2497  }
2498 
2499 
2507  std::vector<double> SpiceRotation::GetFullCacheTime() {
2508 
2509  // No time cache was initialized -- throw an error
2510  if (p_fullCacheSize < 1) {
2511  QString msg = "Time cache not available -- rerun spiceinit";
2512  throw IException(IException::User, msg, _FILEINFO_);
2513  }
2514 
2515  std::vector<double> fullCacheTime;
2516  double cacheSlope = 0.0;
2517  if (p_fullCacheSize > 1) {
2518  cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) / (double)(p_fullCacheSize - 1);
2519  }
2520 
2521  for (int i = 0; i < p_fullCacheSize; i++)
2522  fullCacheTime.push_back(p_fullCacheStartTime + (double) i * cacheSlope);
2523 
2524  return fullCacheTime;
2525  }
2526 
2527 
2539  void SpiceRotation::FrameTrace(double et) {
2541  // The code for this method was extracted from the Naif routine rotget written by N.J. Bachman &
2542  // W.L. Taber (JPL)
2543  int center;
2544  FrameType type;
2545  int typid;
2546  SpiceBoolean found;
2547  int frmidx; // Frame chain index for current frame
2548  SpiceInt nextFrame; // Naif frame code of next frame
2550  std::vector<int> frameCodes;
2551  std::vector<int> frameTypes;
2552  frameCodes.push_back(p_constantFrames[0]);
2553 
2554  while (frameCodes[frameCodes.size()-1] != J2000Code) {
2555  frmidx = frameCodes.size() - 1;
2556  // First get the frame type (Note:: we may also need to save center if we use dynamic frames)
2557  // (Another note: the type returned is the Naif from type. This is not quite the same as the
2558  // SpiceRotation enumerated FrameType. The SpiceRotation FrameType differentiates between
2559  // pck types. FrameTypes of 2, 6, and 7 will all be considered to be Naif frame type 2. The
2560  // logic for FrameTypes in this method is correct for all types except type 7. Current pck
2561  // do not exercise this option. Should we ever use pck with a target body not referenced to
2562  // the J2000 frame and epoch, both this method and loadPCFromSpice will need to be modified.
2563  frinfo_c((SpiceInt) frameCodes[frmidx],
2564  (SpiceInt *) &center,
2565  (SpiceInt *) &type,
2566  (SpiceInt *) &typid, &found);
2567 
2568  if (!found) {
2569 
2570  if (p_source == Nadir) {
2571  frameTypes.push_back(0);
2572  break;
2573  }
2574 
2575  QString msg = "The frame" + toString((int) frameCodes[frmidx]) + " is not supported by Naif";
2577  }
2578 
2579  double matrix[3][3];
2580 
2581  // To get the next link in the frame chain, use the frame type
2582  if (type == INERTL || type == PCK) {
2583  nextFrame = J2000Code;
2584  }
2585  else if (type == CK) {
2586  ckfrot_((SpiceInt *) &typid, &et, (double *) matrix, &nextFrame, (logical *) &found);
2587 
2588  if (!found) {
2589 
2590  if (p_source == Nadir) {
2591  frameTypes.push_back(0);
2592  break;
2593  }
2594 
2595  QString msg = "The ck rotation from frame " + toString(frameCodes[frmidx]) + " can not "
2596  + "be found due to no pointing available at requested time or a problem with the "
2597  + "frame";
2599  }
2600  }
2601  else if (type == TK) {
2602  tkfram_((SpiceInt *) &typid, (double *) matrix, &nextFrame, (logical *) &found);
2603  if (!found) {
2604  QString msg = "The tk rotation from frame " + toString(frameCodes[frmidx]) +
2605  " can not be found";
2607  }
2608  }
2609  else if (type == DYN) {
2610  //
2611  // Unlike the other frame classes, the dynamic frame evaluation
2612  // routine ZZDYNROT requires the input frame ID rather than the
2613  // dynamic frame class ID. ZZDYNROT also requires the center ID
2614  // we found via the FRINFO call.
2615 
2616  zzdynrot_((SpiceInt *) &typid, (SpiceInt *) &center, &et, (double *) matrix, &nextFrame);
2617  }
2618 
2619  else {
2620  QString msg = "The frame " + toString(frameCodes[frmidx]) +
2621  " has a type " + toString(type) + " not supported by your version of Naif Spicelib."
2622  + "You need to update.";
2624 
2625  }
2626  frameCodes.push_back(nextFrame);
2627  frameTypes.push_back(type);
2628  }
2629 
2630  if ((int) frameCodes.size() == 1 && p_source != Nadir) { // Must be Sky
2631  p_constantFrames.push_back(frameCodes[0]);
2632  p_timeFrames.push_back(frameCodes[0]);
2633  return;
2634  }
2635 
2636  int nConstants = 0;
2637  p_constantFrames.clear();
2638  while (frameTypes[nConstants] == TK && nConstants < (int) frameTypes.size()) nConstants++;
2639 
2640 
2641  for (int i = 0; i <= nConstants; i++) {
2642  p_constantFrames.push_back(frameCodes[i]);
2643  }
2644 
2645  if (p_source != Nadir) {
2646  for (int i = nConstants; i < (int) frameCodes.size(); i++) {
2647  p_timeFrames.push_back(frameCodes[i]);
2648  }
2649  }
2650  else {
2651  // Nadir rotation is from spacecraft to J2000
2652  p_timeFrames.push_back(frameCodes[nConstants]);
2653  p_timeFrames.push_back(J2000Code);
2654  }
2656  }
2657 
2658 
2664  std::vector<double> SpiceRotation::Matrix() {
2666  std::vector<double> TJ;
2667  TJ.resize(9);
2668  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], (SpiceDouble( *) [3]) &TJ[0]);
2670  return TJ;
2671  }
2672 
2673 
2679  std::vector<double> SpiceRotation::ConstantRotation() {
2681  std::vector<double> q;
2682  q.resize(4);
2683  m2q_c((SpiceDouble( *)[3]) &p_TC[0], &q[0]);
2685  return q;
2686  }
2687 
2688 
2694  std::vector<double> &SpiceRotation::ConstantMatrix() {
2695  return p_TC;
2696  }
2697 
2698 
2704  void SpiceRotation::SetConstantMatrix(std::vector<double> constantMatrix) {
2705  p_TC = constantMatrix;
2706  return;
2707  }
2708 
2714  std::vector<double> SpiceRotation::TimeBasedRotation() {
2716  std::vector<double> q;
2717  q.resize(4);
2718  m2q_c((SpiceDouble( *)[3]) &p_CJ[0], &q[0]);
2720  return q;
2721  }
2722 
2723 
2729  std::vector<double> &SpiceRotation::TimeBasedMatrix() {
2730  return p_CJ;
2731  }
2732 
2733 
2739  void SpiceRotation::SetTimeBasedMatrix(std::vector<double> timeBasedMatrix) {
2740  p_CJ = timeBasedMatrix;
2741  return;
2742  }
2743 
2744 
2751  FrameTrace(et);
2752  // Get constant rotation which applies in all cases
2753  int targetFrame = p_constantFrames[0];
2754  int fromFrame = p_timeFrames[0];
2755  p_TC.resize(9);
2756  refchg_((SpiceInt *) &fromFrame, (SpiceInt *) &targetFrame, &et, (doublereal *) &p_TC[0]);
2757  // Transpose to obtain row-major order
2758  xpose_c((SpiceDouble( *)[3]) &p_TC[0], (SpiceDouble( *)[3]) &p_TC[0]);
2759  }
2760 
2761 
2783 
2784  // Make sure the angles have been fit to polynomials so we can calculate the derivative
2785  if (p_source < PolyFunction ) {
2786  QString msg = "The SpiceRotation pointing angles must be fit to polynomials in order to ";
2787  msg += "compute angular velocity.";
2789  }
2790  if (p_source == PckPolyFunction) {
2791  QString msg = "Planetary angular velocity must be fit computed with PCK polynomials ";
2793  }
2794  std::vector<double> dCJdt;
2795  dCJdt.resize(9);
2796 
2797  switch (m_frameType) {
2798  // Treat all cases the same except for target body rotations
2799  case UNKNOWN:
2800  case INERTL:
2801  case TK:
2802  case DYN:
2803  case CK:
2804  DCJdt(dCJdt);
2805  break;
2806  case PCK:
2807  case BPC:
2808  case NOTJ2000PCK:
2809  break;
2810  }
2811  double omega[3][3];
2812  mtxm_c((SpiceDouble( *)[3]) &dCJdt[0], (SpiceDouble( *)[3]) &p_CJ[0], omega);
2813  p_av[0] = omega[2][1];
2814  p_av[1] = omega[0][2];
2815  p_av[2] = omega[1][0];
2817  }
2818 
2819 
2826  void SpiceRotation::DCJdt(std::vector<double> &dCJ) {
2828 
2829  // Get the rotation angles and axes
2830  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
2831  int axes[3] = {p_axis1, p_axis2, p_axis3};
2832 
2833  double dmatrix[3][3];
2834  double dangle;
2835  double wmatrix[3][3]; // work matrix
2836  dCJ.assign(9, 0.);
2837 
2838  for (int angleIndex = 0; angleIndex < 3; angleIndex++) {
2839  drotat_(&(angles[angleIndex]), (integer *) axes + angleIndex, (doublereal *) dmatrix);
2840  // Transpose to obtain row-major order
2841  xpose_c(dmatrix, dmatrix);
2842 
2843  // To get the derivative of the polynomial fit to the angle with respect to time
2844  // first create the function object for this angle and load its coefficients
2846  function.SetCoefficients(p_coefficients[angleIndex]);
2847 
2848  // Evaluate the derivative of function at p_et
2849  // dangle = function.DerivativeVar((p_et - p_baseTime) / p_timeScale);
2850  dangle = function.DerivativeVar((p_et - p_baseTime) / p_timeScale) / p_timeScale;
2851 
2852  // Multiply dangle to complete dmatrix
2853  for (int row = 0; row < 3; row++) {
2854  for (int col = 0; col < 3; col++) {
2855  dmatrix[row][col] *= dangle;
2856  }
2857  }
2858  // Apply the other 2 angles and chain them all together
2859  switch (angleIndex) {
2860  case 0:
2861  rotmat_c(dmatrix, angles[1], axes[1], dmatrix);
2862  rotmat_c(dmatrix, angles[2], axes[2], dmatrix);
2863  break;
2864  case 1:
2865  rotate_c(angles[0], axes[0], wmatrix);
2866  mxm_c(dmatrix, wmatrix, dmatrix);
2867  rotmat_c(dmatrix, angles[2], axes[2], dmatrix);
2868  break;
2869  case 2:
2870  rotate_c(angles[0], axes[0], wmatrix);
2871  rotmat_c(wmatrix, angles[1], axes[1], wmatrix);
2872  mxm_c(dmatrix, wmatrix, dmatrix);
2873  break;
2874  }
2875  int i, j;
2876  for (int index = 0; index < 9; index++) {
2877  i = index / 3;
2878  j = index % 3;
2879  dCJ[index] += dmatrix[i][j];
2880  }
2881  }
2882 
2884  }
2885 
2886 
2892  std::vector<double> SpiceRotation::StateTJ() {
2893  std::vector<double> stateTJ(36);
2894 
2895  // Build the state matrix for the time-based rotation from the matrix and angulary velocity
2896  double stateCJ[6][6];
2897  rav2xf_c(&p_CJ[0], &p_av[0], stateCJ);
2898 // (SpiceDouble (*) [3]) &p_CJ[0]
2899  int irow = 0;
2900  int jcol = 0;
2901  int vpos = 0;
2902 
2903  for (int row = 3; row < 6; row++) {
2904  irow = row - 3;
2905  vpos = irow * 3;
2906 
2907  for (int col = 0; col < 3; col++) {
2908  jcol = col + 3;
2909  // Fill the upper left corner
2910  stateTJ[irow*6 + col] = p_TC[vpos] * stateCJ[0][col] + p_TC[vpos+1] * stateCJ[1][col]
2911  + p_TC[vpos+2] * stateCJ[2][col];
2912  // Fill the lower left corner
2913  stateTJ[row*6 + col] = p_TC[vpos] * stateCJ[3][col] + p_TC[vpos+1] * stateCJ[4][col]
2914  + p_TC[vpos+2] * stateCJ[5][col];
2915  // Fill the upper right corner
2916  stateTJ[irow*6 + jcol] = 0;
2917  // Fill the lower right corner
2918  stateTJ[row*6 +jcol] = stateTJ[irow*6 + col];
2919  }
2920  }
2921  return stateTJ;
2922  }
2923 
2924 
2935  std::vector<double> SpiceRotation::Extrapolate(double timeEt) {
2937 
2938  if (!p_hasAngularVelocity) return p_CJ;
2939 
2940  double diffTime = timeEt - p_et;
2941  std::vector<double> CJ(9, 0.0);
2942  double dmat[3][3];
2943 
2944  // Create a rotation matrix for the axis and magnitude of the angular velocity multiplied by
2945  // the time difference
2946  axisar_c((SpiceDouble *) &p_av[0], diffTime*vnorm_c((SpiceDouble *) &p_av[0]), dmat);
2947 
2948  // Rotate from the current time to the desired time assuming constant angular velocity
2949  mxm_c(dmat, (SpiceDouble *) &p_CJ[0], (SpiceDouble( *)[3]) &CJ[0]);
2951  return CJ;
2952  }
2953 
2954 
2962  void SpiceRotation::SetFullCacheParameters(double startTime, double endTime, int cacheSize) {
2963  // Save full cache parameters
2964  p_fullCacheStartTime = startTime;
2965  p_fullCacheEndTime = endTime;
2966  p_fullCacheSize = cacheSize;
2967  }
2968 
2969 
2978  // Get a count of all the loaded kernels
2979  SpiceInt count;
2980  ktotal_c("PCK", &count);
2981 
2982  // Define some Naif constants
2983  int FILESIZ = 128;
2984  int TYPESIZ = 32;
2985  int SOURCESIZ = 128;
2986  SpiceChar file[FILESIZ];
2987  SpiceChar filetype[TYPESIZ];
2988  SpiceChar source[SOURCESIZ];
2989  SpiceInt handle;
2990  SpiceBoolean found;
2991 
2992  // Get the name of each loaded kernel. The accuracy of this test depends on the use of the
2993  // "bpc" suffix to name a binary pck. If a binary bpc does not have the "bpc" suffix, it will not
2994  // be found by this test. This test was suggested by Boris Semenov at Naif.
2995  for (SpiceInt knum = 0; knum < count; knum++){
2996  kdata_c(knum, "PCK", FILESIZ, TYPESIZ, SOURCESIZ, file, filetype, source, &handle, &found);
2997 
2998  // search file for "bpc"
2999  if (strstr(file, "bpc") != NULL) {
3000  m_frameType = BPC;
3001  }
3002  }
3003  }
3004 
3005 
3014  SpiceInt frameCode = p_constantFrames[0];
3015  SpiceBoolean found;
3016  SpiceInt centerBodyCode;
3017  SpiceInt frameClass;
3018  SpiceInt classId;
3019  frinfo_c(frameCode, &centerBodyCode, &frameClass, &classId, &found);
3020 
3021  if (found) {
3022  if (frameClass == 2 || (centerBodyCode > 0 && frameClass != 3)) {
3023  m_frameType = PCK;
3024  // Load the PC information while it is available and set member values
3025  loadPCFromSpice(centerBodyCode);
3026  }
3027  else if (p_constantFrames.size() > 1) {
3028  for (std::vector<int>::size_type idx = 1; idx < p_constantFrames.size(); idx++) {
3029  frameCode = p_constantFrames[idx];
3030  frinfo_c(frameCode, &centerBodyCode, &frameClass, &classId, &found);
3031  if (frameClass == 3) m_frameType = CK;
3032  }
3033  }
3034  else {
3035  switch (frameClass) {
3036  case 1:
3037  m_frameType = INERTL;
3038  break;
3039  // case 2: handled in first if block
3040  case 3:
3041  m_frameType = CK;
3042  break;
3043  case 4:
3044  m_frameType = TK;
3045  break;
3046  case 5:
3047  m_frameType = DYN;
3048  break;
3049  default:
3050  m_frameType = UNKNOWN;
3051  }
3052  }
3053  }
3054  }
3055 
3056 
3066  // If the cache has only one rotation, set it
3068 
3069  if (p_cache.size() == 1) {
3070  /* p_quaternion = p_cache[0];*/
3071  p_CJ = p_cache[0];
3072 // p_CJ = p_quaternion.ToMatrix();
3073 
3074  if (p_hasAngularVelocity) {
3075  p_av = p_cacheAv[0];
3076  }
3077 
3078  }
3079 
3080  // Otherwise determine the interval to interpolate
3081  else {
3082  std::vector<double>::iterator pos;
3083  pos = upper_bound(p_cacheTime.begin(), p_cacheTime.end(), p_et);
3084 
3085  int cacheIndex;
3086 
3087  if (pos != p_cacheTime.end()) {
3088  cacheIndex = distance(p_cacheTime.begin(), pos);
3089  cacheIndex--;
3090  }
3091  else {
3092  cacheIndex = p_cacheTime.size() - 2;
3093  }
3094 
3095  if (cacheIndex < 0) cacheIndex = 0;
3096 
3097  // Interpolate the rotation
3098  double mult = (p_et - p_cacheTime[cacheIndex]) /
3099  (p_cacheTime[cacheIndex+1] - p_cacheTime[cacheIndex]);
3100  /* Quaternion Q2 (p_cache[cacheIndex+1]);
3101  Quaternion Q1 (p_cache[cacheIndex]);*/
3102  std::vector<double> CJ2(p_cache[cacheIndex+1]);
3103  std::vector<double> CJ1(p_cache[cacheIndex]);
3104  SpiceDouble J2J1[3][3];
3105  mtxm_c((SpiceDouble( *)[3]) &CJ2[0], (SpiceDouble( *)[3]) &CJ1[0], J2J1);
3106  SpiceDouble axis[3];
3107  SpiceDouble angle;
3108  raxisa_c(J2J1, axis, &angle);
3109  SpiceDouble delta[3][3];
3110  axisar_c(axis, angle * (SpiceDouble)mult, delta);
3111  mxmt_c((SpiceDouble *) &CJ1[0], delta, (SpiceDouble( *) [3]) &p_CJ[0]);
3112 
3113  if (p_hasAngularVelocity) {
3114  double v1[3], v2[3]; // Vectors surrounding desired time
3115  vequ_c((SpiceDouble *) &p_cacheAv[cacheIndex][0], v1);
3116  vequ_c((SpiceDouble *) &p_cacheAv[cacheIndex+1][0], v2);
3117  vscl_c((1. - mult), v1, v1);
3118  vscl_c(mult, v2, v2);
3119  vadd_c(v1, v2, (SpiceDouble *) &p_av[0]);
3120  }
3121  }
3123  }
3124 
3125 
3132  // TODO what about spk time bias and mission setting of light time corrections
3133  // That information has only been passed to the SpicePosition class and
3134  // is not available to this class, but probably should be applied to the
3135  // spkez call.
3136 
3137  // Make sure the constant frame is loaded. This method also does the frame trace.
3139  if (p_timeFrames.size() == 0) InitConstantRotation(p_et);
3140 
3141  SpiceDouble stateJ[6]; // Position and velocity vector in J2000
3142  SpiceDouble lt;
3143  SpiceInt spkCode = p_constantFrames[0] / 1000;
3144  spkez_c((SpiceInt) spkCode, p_et, "J2000", "LT+S",
3145  (SpiceInt) p_targetCode, stateJ, &lt);
3146  // reverse the position to be relative to the spacecraft. This may be
3147  // a mission dependent value and possibly the sense of the velocity as well.
3148  SpiceDouble sJ[3], svJ[3];
3149  vpack_c(-1 * stateJ[0], -1 * stateJ[1], -1 * stateJ[2], sJ);
3150  vpack_c(stateJ[3], stateJ[4], stateJ[5], svJ);
3151  twovec_c(sJ,
3152  p_axisP,
3153  svJ,
3154  p_axisV,
3155  (SpiceDouble( *)[3]) &p_CJ[0]);
3157  }
3158 
3159 
3172  SpiceInt j2000 = J2000Code;
3173 
3174  SpiceDouble time = p_et + p_timeBias;
3175 
3176  // Make sure the constant frame is loaded. This method also does the frame trace.
3177  if (p_timeFrames.size() == 0) InitConstantRotation(p_et);
3178  int toFrame = p_timeFrames[0];
3179 
3180  // First try getting the entire state matrix (6x6), which includes CJ and the angular velocity
3181  double stateCJ[6][6];
3182  frmchg_((integer *) &j2000, (integer *) &toFrame, &time, (doublereal *) stateCJ);
3183 
3184  // If Naif fails attempting to get the state matrix, assume the angular velocity vector is
3185  // not available and just get the rotation matrix. First turn off Naif error reporting and
3186  // return any error without printing them.
3187  SpiceBoolean ckfailure = failed_c();
3188  reset_c(); // Reset Naif error system to allow caller to recover
3189 
3190  if (!ckfailure) {
3191  xpose6_c(stateCJ, stateCJ);
3192  xf2rav_c(stateCJ, (SpiceDouble( *)[3]) &p_CJ[0], (SpiceDouble *) &p_av[0]);
3193  p_hasAngularVelocity = true;
3194  }
3195  else {
3196  refchg_((integer *) &j2000, (integer *) &toFrame, &time, (SpiceDouble *) &p_CJ[0]);
3197 
3198  if (failed_c()) {
3199  char naifstr[64];
3200  getmsg_c("SHORT", sizeof(naifstr), naifstr);
3201  reset_c(); // Reset Naif error system to allow caller to recover
3202 
3203  if (eqstr_c(naifstr, "SPICE(UNKNOWNFRAME)")) {
3204  Isis::IString msg = Isis::IString((int) p_constantFrames[0]) + " is an unrecognized " +
3205  "reference frame code. Has the mission frames kernel been loaded?";
3206  throw IException(IException::Io, msg, _FILEINFO_);
3207  }
3208  else {
3209  Isis::IString msg = "No pointing available at requested time [" +
3210  Isis::IString(p_et + p_timeBias) + "] for frame code [" +
3211  Isis::IString((int) p_constantFrames[0]) + "]";
3212  throw IException(IException::Io, msg, _FILEINFO_);
3213  }
3214  }
3215 
3216  // Transpose to obtain row-major order
3217  xpose_c((SpiceDouble( *)[3]) &p_CJ[0], (SpiceDouble( *)[3]) &p_CJ[0]);
3218  }
3220  }
3221 
3222 
3229  std::vector<double> SpiceRotation::EvaluatePolyFunction() {
3233 
3234  // Load the functions with the coefficients
3235  function1.SetCoefficients(p_coefficients[0]);
3236  function2.SetCoefficients(p_coefficients[1]);
3237  function3.SetCoefficients(p_coefficients[2]);
3238 
3239  std::vector<double> rtime;
3240  rtime.push_back((p_et - p_baseTime) / p_timeScale);
3241  std::vector<double> angles;
3242  angles.push_back(function1.Evaluate(rtime));
3243  angles.push_back(function2.Evaluate(rtime));
3244  angles.push_back(function3.Evaluate(rtime));
3245 
3246  // Get the first angle back into the range Naif expects [-180.,180.]
3247  if (angles[0] <= -1 * pi_c()) {
3248  angles[0] += twopi_c();
3249  }
3250  else if (angles[0] > pi_c()) {
3251  angles[0] -= twopi_c();
3252  }
3253  return angles;
3254  }
3255 
3256 
3268 
3269  // Load the functions with the coefficients
3270  function1.SetCoefficients(p_coefficients[0]);
3271  function2.SetCoefficients(p_coefficients[1]);
3272  function3.SetCoefficients(p_coefficients[2]);
3273 
3274  std::vector<double> rtime;
3275  rtime.push_back((p_et - p_baseTime) / p_timeScale);
3276  double angle1 = function1.Evaluate(rtime);
3277  double angle2 = function2.Evaluate(rtime);
3278  double angle3 = function3.Evaluate(rtime);
3279 
3280  // Get the first angle back into the range Naif expects [-180.,180.]
3281  if (angle1 < -1 * pi_c()) {
3282  angle1 += twopi_c();
3283  }
3284  else if (angle1 > pi_c()) {
3285  angle1 -= twopi_c();
3286  }
3287 
3288  eul2m_c((SpiceDouble) angle3, (SpiceDouble) angle2, (SpiceDouble) angle1,
3290  (SpiceDouble( *)[3]) &p_CJ[0]);
3291 
3292  if (p_hasAngularVelocity) {
3293  if ( p_degree == 0)
3294  p_av = p_cacheAv[0];
3295  else
3296  ComputeAv();
3297  }
3299  }
3300 
3301 
3311  std::vector<double> cacheAngles(3);
3312  std::vector<double> cacheVelocity(3);
3313  cacheAngles = Angles(p_axis3, p_axis2, p_axis1);
3314  cacheVelocity = p_av;
3316  std::vector<double> polyAngles(3);
3317  // The decomposition fails because the angles are outside the valid range for Naif
3318  // polyAngles = Angles(p_axis3, p_axis2, p_axis1);
3319  polyAngles = EvaluatePolyFunction();
3320  std::vector<double> polyVelocity(3);
3321  polyVelocity = p_av;
3322  std::vector<double> angles(3);
3323 
3324  for (int index = 0; index < 3; index++) {
3325  angles[index] = cacheAngles[index] + polyAngles[index];
3326  p_av[index] += cacheVelocity[index];
3327  }
3328 
3329  if (angles[0] <= -1 * pi_c()) {
3330  angles[0] += twopi_c();
3331  }
3332  else if (angles[0] > pi_c()) {
3333  angles[0] -= twopi_c();
3334  }
3335 
3336  if (angles[2] <= -1 * pi_c()) {
3337  angles[2] += twopi_c();
3338  }
3339  else if (angles[2] > pi_c()) {
3340  angles[2] -= twopi_c();
3341  }
3342 
3343  eul2m_c((SpiceDouble) angles[2], (SpiceDouble) angles[1], (SpiceDouble) angles[0],
3345  (SpiceDouble( *)[3]) &p_CJ[0]);
3346  }
3347 
3348 
3355  // Set the scales
3356  double dTime = p_et / 86400.; // seconds to days conversion
3357  double centTime = dTime / 36525; // seconds to Julian centuries conversion
3358  double secondsPerJulianCentury = 86400. * 36525.;
3359 
3360  // Calculate the quadratic part of the expression for the angles in degrees
3361  // Note: phi = ra + 90. and delta = 90 - dec for comparing results with older Isis 2 data
3362  Angle ra = m_raPole[0] + centTime*(m_raPole[1] + m_raPole[2]*centTime);
3363  Angle dec = m_decPole[0] + centTime*(m_decPole[1] + m_decPole[2]*centTime);
3364  Angle pm = m_pm[0] + dTime*(m_pm[1] + m_pm[2]*dTime);
3365  Angle dra = (m_raPole[1] + 2.*m_raPole[2]*centTime) / secondsPerJulianCentury;
3366  Angle ddec = (m_decPole[1] + 2.*m_decPole[2]*centTime) / secondsPerJulianCentury;
3367  Angle dpm = (m_pm[1] + 2.*m_pm[2]*dTime) / 86400;
3368 
3369  // Now add the nutation/precession (trig part) adjustment to the expression if any
3370  int numNutPrec = (int) m_raNutPrec.size();
3371  Angle theta;
3372  double dtheta;
3373  double costheta;
3374  double sintheta;
3375 
3376  for (int ia = 0; ia < numNutPrec; ia++) {
3377  theta = m_sysNutPrec0[ia] + m_sysNutPrec1[ia]*centTime;
3378  dtheta = m_sysNutPrec1[ia].degrees() * DEG2RAD;
3379  costheta = cos(theta.radians());
3380  sintheta = sin(theta.radians());
3381  ra += Angle(m_raNutPrec[ia] * sintheta, Angle::Degrees);
3382  dec += Angle(m_decNutPrec[ia] * costheta, Angle::Degrees);
3383  pm += Angle(m_pmNutPrec[ia] * sintheta, Angle::Degrees);
3384  dra += Angle(m_raNutPrec[ia] * dtheta / secondsPerJulianCentury * costheta, Angle::Degrees);
3385  ddec -= Angle(m_decNutPrec[ia] * dtheta / secondsPerJulianCentury * sintheta, Angle::Degrees);
3386  dpm += Angle(m_pmNutPrec[ia] * dtheta / secondsPerJulianCentury * costheta, Angle::Degrees);
3387  }
3388 
3389  // Get pm in correct range
3390  pm = Angle(fmod(pm.degrees(), 360), Angle::Degrees); // Get pm back into the domain range
3391 
3392  if (ra.radians() < -1 * pi_c()) {
3393  ra += Angle(twopi_c(), Angle::Radians);
3394  }
3395  else if (ra.radians() > pi_c()) {
3396  ra -= Angle(twopi_c(), Angle::Radians);
3397  }
3398 
3399  if (pm.radians() < -1 * pi_c()) {
3400  pm += Angle(twopi_c(), Angle::Radians);
3401  }
3402  else if (pm.radians() > pi_c()) {
3403  pm -= Angle(twopi_c(), Angle::Radians);
3404  }
3405 
3406  // Convert to Euler angles to get the matrix
3407  SpiceDouble phi = ra.radians() + HALFPI;
3408  SpiceDouble delta = HALFPI - dec.radians();
3409  SpiceDouble w = pm.radians();
3410  SpiceDouble dphi = dra.radians();
3411  SpiceDouble ddelta = -ddec.radians();
3412  SpiceDouble dw = dpm.radians();
3413 
3414  // Load the Euler angles and their derivatives into an array and create the state matrix
3415  SpiceDouble angsDangs[6];
3416  SpiceDouble BJs[6][6];
3417  vpack_c(w, delta, phi, angsDangs);
3418  vpack_c(dw, ddelta, dphi, &angsDangs[3]);
3419  eul2xf_c (angsDangs, p_axis3, p_axis2, p_axis1, BJs);
3420 
3421  // Decompose the state matrix to the rotation and its angular velocity
3422  xf2rav_c(BJs, (SpiceDouble( *)[3]) &p_CJ[0], (SpiceDouble *) &p_av[0]);
3423  }
3424 }
Table Cache(const QString &tableName)
Return a table with J2000 to reference rotations.
int Records() const
Returns the number of records.
Definition: Table.cpp:224
Isis specific code for binary pck.
Provide operations for quaternion arithmetic.
Definition: Quaternion.h:52
void SetOverrideBaseTime(double baseTime, double timeScale)
Set an override base time to be used with observations on scanners to allow all images in an observat...
void MinimizeCache(DownsizeStatus status)
Set the downsize status to minimize cache.
std::vector< double > EvaluatePolyFunction()
Evaluate the polynomial fit function for the three pointing angles for the current ephemeris time...
double DPckPolynomial(PartialType partialVar, const int coeffIndex)
Evaluate the derivative of the fit polynomial defined by the given coefficients with respect to the c...
std::vector< double > p_cacheTime
iTime for corresponding rotation
std::vector< double > p_CJ
Rotation matrix from J2000 to first constant rotation.
void SetCoefficients(const std::vector< double > &coefs)
Set the coefficients for the equation.
std::vector< double > Matrix()
Return the full rotation TJ as a matrix.
std::vector< Angle > m_pm
Coefficients of a quadratic polynomial fitting pole pm.
void SetPolynomialDegree(int degree)
Set the degree of the polynomials to be fit to the three camera angles for the time period covered by...
std::vector< double > m_decNutPrec
Coefficients of pole decliniation nut/prec terms.
int p_axis3
Axis of rotation for angle 3 of rotation.
Utility class for creating and using cartesean line equations.
Definition: LineEquation.h:44
void GetPolynomial(std::vector< double > &abcAng1, std::vector< double > &abcAng2, std::vector< double > &abcAng3)
Return the coefficients of a polynomial fit to each of the three camera angles for the time period co...
void loadPCFromTable(const PvlObject &Label)
Initialize planetary orientation constants from an cube body rotation label.
int p_targetCode
For computing Nadir rotation only.
void SetAxes(int axis1, int axis2, int axis3)
Set the axes of rotation for decomposition of a rotation matrix into 3 angles.
bool m_tOrientationAvailable
Target orientation constants are available.
double radians() const
Convert an angle to a double.
Definition: Angle.h:243
double Intercept()
Compute the intercept of the line.
std::vector< double > m_raNutPrec
Coefficients of pole right ascension nut/prec terms.
int p_degree
Degree of fit polynomial for angles.
void LoadCache(double startTime, double endTime, int size)
Cache J2000 rotation quaternion over a time range.
void setEphemerisTimePolyFunction()
When setting the ephemeris time, updates the rotation according to a polynomial that defines the thre...
Nth degree Polynomial with one variable.
int p_axis1
Axis of rotation for angle 1 of rotation.
void SetConstantMatrix(std::vector< double > constantMatrix)
Set the constant 3x3 rotation TC matrix from a vector of length 9.
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:108
const double HALFPI
The mathematical constant PI/2.
Definition: Constants.h:57
void usePckPolynomial()
Set the coefficients of a polynomial fit to each of the three planet angles for the time period cover...
bool p_hasAngularVelocity
Flag indicating whether the rotation includes angular velocity.
void SetEphemerisTime(double et)
Return the J2000 to reference frame quaternion at given time.
std::vector< double > p_TC
Rotation matrix from first constant rotation (after all time-based rotations in frame chain from J200...
std::vector< Angle > m_sysNutPrec0
Constants of planetary system nut/prec periods.
std::vector< double > AngularVelocity()
Accessor method to get the angular velocity.
DownsizeStatus p_minimizeCache
Status of downsizing the cache (set to No to ignore)
void LoadTimeCache()
Load the time cache.
int Frame()
Accessor method that returns the frame code.
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
void getPckPolynomial(std::vector< Angle > &raCoeff, std::vector< Angle > &decCoeff, std::vector< Angle > &pmCoeff)
Return the coefficients of a polynomial fit to each of the three planet angles.
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
A type of error that occurred when performing an actual I/O operation.
Definition: IException.h:171
std::vector< double > GetCenterAngles()
Return the camera angles at the center time of the observation.
std::vector< double > TimeBasedRotation()
Return time-based 3x3 rotation CJ matrix as a quaternion.
void setPckPolynomial(const std::vector< Angle > &raCoeff, const std::vector< Angle > &decCoeff, const std::vector< Angle > &pmCoeff)
Set the coefficients of a polynomial fit to each of the three planet angles for the time period cover...
double WrapAngle(double compareAngle, double angle)
Wrap the input angle to keep it within 2pi radians of the angle to compare.
std::vector< double > & TimeBasedMatrix()
Return time-based 3x3 rotation CJ matrix as a vector of length 9.
std::vector< Angle > poleRaCoefs()
Return the coefficients used to calculate the target body pole ra.
SpiceRotation(int frameCode)
Construct an empty SpiceRotation class using a valid Naif frame code to set up for getting rotation f...
bool hasKeyword(const QString &kname, FindOptions opts) const
See if a keyword is in the current PvlObject, or deeper inside other PvlObjects and Pvlgroups within ...
Definition: PvlObject.cpp:207
Isis specific code for unknown frame type.
int p_axis2
Axis of rotation for angle 2 of rotation.
std::vector< double > Extrapolate(double timeEt)
Extrapolate pointing for a given time assuming a constant angular velocity.
double p_fullCacheStartTime
Initial requested starting time of cache.
PCK frame not referenced to J2000.
std::vector< double > p_av
Angular velocity for rotation at time p_et.
FrameType
Enumeration for the frame type of the rotation.
void SetFrame(int frameCode)
Change the frame to the given frame code.
void loadPCFromSpice(int CenterBodyCode)
Initialize planetary orientation constants from Spice PCK.
double p_overrideTimeScale
Value set by caller to override computed time scale.
DownsizeStatus
Status of downsizing the cache.
int size() const
Returns the number of values stored in this keyword.
Definition: PvlKeyword.h:141
bool p_noOverride
Flag to compute base time;.
double GetBaseTime()
Accessor method to get the rotation base time.
std::vector< double > poleDecNutPrecCoefs()
Return the coefficients used to calculate the target body pole dec nut/prec coefficients.
bool p_degreeApplied
Flag indicating whether or not a polynomial of degree p_degree has been created and used to fill the ...
void checkForBinaryPck()
Check loaded pck to see if any are binary and set frame type to indicate binary pck.
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:73
bool IsCached() const
Checks if the cache is empty.
void CacheLabel(Table &table)
Add labels to a SpiceRotation table.
std::vector< double > Angles(int axis3, int axis2, int axis1)
Return the camera angles (right ascension, declination, and twist) for the time-based matrix CJ...
std::vector< double > ReferenceVector(const std::vector< double > &jVec)
Given a direction vector in J2000, return a reference frame direction.
double p_baseTime
Base time used in fit equations.
int p_axisP
The axis defined by the spacecraft vector for defining a nadir rotation.
bool p_matrixSet
Flag indicating p_TJ has been set.
void SetTimeBias(double timeBias)
Apply a time bias when invoking SetEphemerisTime method.
PvlKeyword & findKeyword(const QString &kname, FindOptions opts)
Finds a keyword in the current PvlObject, or deeper inside other PvlObjects and Pvlgroups within this...
Definition: PvlObject.cpp:148
Source p_source
The source of the rotation data.
std::vector< double > GetFullCacheTime()
Return full listing (cache) of original time coverage requested.
double p_et
Current ephemeris time.
std::vector< int > TimeFrameChain()
Accessor method to get the frame chain for the rotation (begins in J2000).
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
With respect to Twist or Prime Meridian Rotation.
double p_overrideBaseTime
Value set by caller to override computed base time.
See Naif Frames.req document for.
void SetTimeBasedMatrix(std::vector< double > timeBasedMatrix)
Set the time-based 3x3 rotation CJ matrix from a vector of length 9.
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
A single keyword-value pair.
Definition: PvlKeyword.h:98
std::vector< std::vector< double > > p_cache
Cached rotations, stored as rotation matrix from J2000 to 1st constant frame (CJ) or coefficients of ...
void SetFullCacheParameters(double startTime, double endTime, int cacheSize)
Set the full cache time parameters.
Do not downsize the cache.
void setEphemerisTimePolyFunctionOverSpice()
When setting the ephemeris time, updates the rotation state based on a polynomial fit over spice kern...
Kernels plus nth degree polynomial.
std::vector< Angle > sysNutPrecConstants()
Return the constants used to calculate the target body system nut/prec angles.
Source
The rotation can come from one of 3 places for an Isis cube.
std::vector< std::vector< double > > p_cacheAv
Cached angular velocities for corresponding rotactions in p_cache.
FrameType getFrameType()
Accessor method to get the rotation frame type.
std::vector< Angle > pmCoefs()
Return the coefficients used to calculate the target body prime meridian.
double EphemerisTime() const
Accessor method to get current ephemeris time.
void FrameTrace(double et)
Compute frame trace chain from target frame to J2000.
Obtain SPICE rotation information for a body.
Generic least square fitting class.
Definition: LeastSquares.h:115
Quaternion p_quaternion
Quaternion for J2000 to reference rotation at et.
With respect to Declination.
The values in the field are 8 byte doubles.
Definition: TableField.h:70
PartialType
This enumeration indicates whether the partial derivative is taken with respect to Right Ascension...
double p_timeBias
iTime bias when reading kernels
std::vector< int > ConstantFrameChain()
Accessor method to get the frame chain for the constant part of the rotation (ends in target) ...
std::vector< double > pmNutPrecCoefs()
Return the coefficients used to calculate the target body pm nut/prec coefficients.
const double DEG2RAD
Multiplier for converting from degrees to radians.
Definition: Constants.h:59
void ComputeBaseTime()
Compute the base time using cached times.
void setEphemerisTimeNadir()
When setting the ephemeris time, uses spacecraft nadir source to update the rotation state...
int p_fullCacheSize
Initial requested cache size.
void InitConstantRotation(double et)
Initialize the constant rotation.
double DPolynomial(const int coeffIndex)
Evaluate the derivative of the fit polynomial defined by the given coefficients with respect to the c...
void ReloadCache()
Cache J2000 rotation over existing cached time range using polynomials.
void ComputeAv()
Compute the angular velocity from the time-based functions fit to the pointing angles This method com...
Defines an angle and provides unit conversions.
Definition: Angle.h:62
void AddKnown(const std::vector< double > &input, double expected, double weight=1.0)
Invoke this method for each set of knowns.
double Coefficient(int i) const
Returns the ith coefficient.
Directly from the kernels.
void DCJdt(std::vector< double > &dRJ)
Compute the derivative of the 3x3 rotation matrix CJ with respect to time.
void SetSource(Source source)
Resets the source of the rotation to the given value.
std::vector< double > m_pmNutPrec
Coefficients of prime meridian nut/prec terms.
std::vector< double > ToMatrix()
Converts quaternion to 3x3 rotational matrix.
Definition: Quaternion.cpp:91
Downsize the cache.
std::vector< double > toJ2000Partial(const std::vector< double > &lookT, PartialType partialVar, int coeffIndex)
Given a direction vector in the reference frame, compute the derivative with respect to one of the co...
std::vector< double > StateTJ()
State matrix (6x6) for rotating state vectors from J2000 to target frame.
void setFrameType()
Set the frame type (m_frameType).
Class for storing Table blobs information.
Definition: Table.h:77
std::vector< double > J2000Vector(const std::vector< double > &rVec)
Given a direction vector in the reference frame, return a J2000 direction.
std::vector< Angle > m_sysNutPrec1
Linear terms of planetary system nut/prec periods.
Cache is downsized.
std::vector< Angle > sysNutPrecCoefs()
Return the coefficients used to calculate the target body system nut/prec angles. ...
double Evaluate(const std::vector< double > &vars)
Compute the equation using the input variables.
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
Definition: NaifStatus.cpp:43
FrameType m_frameType
The type of rotation frame.
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
std::vector< double > p_coefficients[3]
Coefficients defining functions fit to 3 pointing angles.
std::vector< double > GetQuaternion() const
Return the quaternion as a vector.
Definition: Quaternion.h:70
int Fields() const
Returns the number of fields that are currently in the record.
Definition: TableRecord.cpp:94
From nth degree polynomial.
std::vector< double > & ConstantMatrix()
Return the constant 3x3 rotation TC matrix as a vector of length 9.
Obtain SPICE information for a spacecraft.
Definition: Spice.h:294
Class for storing an Isis::Table&#39;s field information.
Definition: TableField.h:63
PvlObject & Label()
Accessor method that returns a PvlObject containing the Blob label.
Definition: Blob.cpp:167
Radians are generally used in mathematical equations, 0-2*PI is one circle, however these are more di...
Definition: Angle.h:80
void setEphemerisTimeSpice()
When setting the ephemeris time, updates the rotation state based on data read directly from NAIF ker...
int p_axisV
The axis defined by the velocity vector for defining a nadir rotation.
std::vector< Angle > poleDecCoefs()
Return the coefficients used to calculate the target body pole dec.
double GetTimeScale()
Accessor method to get the rotation time scale.
std::vector< double > poleRaNutPrecCoefs()
Return the coefficients used to calculate the target body pole ra nut/prec coefficients.
void setEphemerisTimeMemcache()
Updates rotation state based on the rotation cache.
double p_fullCacheEndTime
Initial requested ending time of cache.
void setEphemerisTimePckPolyFunction()
When setting the ephemeris time, updates the rotation state based on the PcK polynomial.
void SetPolynomial(const Source type=PolyFunction)
Set the coefficients of a polynomial fit to each of the three camera angles for the time period cover...
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
std::vector< int > p_constantFrames
Chain of Naif frame codes in constant rotation TC.
double Slope()
Compute the slope of the line.
double p_timeScale
Time scale used in fit equations.
int Solve(Isis::LeastSquares::SolveMethod method=SVD)
After all the data has been registered through AddKnown, invoke this method to solve the system of eq...
bool HasAngularVelocity()
Checks whether the rotation has angular velocities.
void SetAngles(std::vector< double > angles, int axis3, int axis2, int axis1)
Set the rotation angles (phi, delta, and w) for the current time to define the time-based matrix CJ...
Table LineCache(const QString &tableName)
Return a table with J2000 to reference rotations.
std::vector< int > p_timeFrames
Chain of Naif frame codes in time-based rotation CJ.
std::vector< double > ConstantRotation()
Return the constant 3x3 rotation TC matrix as a quaternion.
int Coefficients() const
Returns the number of coefficients for the equation.
Definition: BasisFunction.h:80
Quadratic polynomial function with linear trignometric terms.
virtual ~SpiceRotation()
Destructor for SpiceRotation object.
Source GetSource()
Accessor method to get the rotation source.
std::vector< double > ToReferencePartial(std::vector< double > &lookJ, PartialType partialVar, int coeffIndex)
Compute the derivative with respect to one of the coefficients in the angle polynomial fit equation o...
std::vector< Angle > m_raPole
Coefficients of a quadratic polynomial fitting pole ra.
With respect to Right Ascension.
std::vector< Angle > m_decPole
Coefficients of a quadratic polynomial fitting pole dec.