USGS

Isis 3.0 Object Programmers' Reference

Home

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 
12 #include "BasisFunction.h"
13 #include "IException.h"
14 #include "IString.h"
15 #include "LeastSquares.h"
16 #include "LineEquation.h"
17 #include "NaifStatus.h"
18 #include "PolynomialUnivariate.h"
19 #include "Quaternion.h"
20 #include "Table.h"
21 #include "TableField.h"
22 
23 // Declarations for bindings for Naif Spicelib routines that do not have
24 // a wrapper
25 extern int refchg_(integer *frame1, integer *frame2, doublereal *et,
26  doublereal *rotate);
27 extern int frmchg_(integer *frame1, integer *frame2, doublereal *et,
28  doublereal *rotate);
29 extern int invstm_(doublereal *mat, doublereal *invmat);
30 // Temporary declarations for bindings for Naif supportlib routines
31 // These three declarations should be removed once supportlib is in Isis3
32 extern int ck3sdn(double sdntol, bool avflag, int *nrec,
33  double *sclkdp, double *quats, double *avvs,
34  int nints, double *starts, double *dparr,
35  int *intarr);
36 
37 namespace Isis {
46  p_constantFrames.push_back(frameCode);
47  p_timeBias = 0.0;
48  p_source = Spice;
49  p_CJ.resize(9);
50  p_matrixSet = false;
51  p_et = -DBL_MAX;
52  p_degree = 2;
53  p_degreeApplied = false;
54  p_noOverride = true;
55  p_axis1 = 3;
56  p_axis2 = 1;
57  p_axis3 = 3;
58  p_minimizeCache = No;
59  p_hasAngularVelocity = false;
60  p_av.resize(3);
63  p_fullCacheSize = 0;
64  }
65 
74  SpiceRotation::SpiceRotation(int frameCode, int targetCode) {
76 
77  p_constantFrames.push_back(frameCode);
78  p_targetCode = targetCode;
79  p_timeBias = 0.0;
80  p_source = Nadir;
81  p_CJ.resize(9);
82  p_matrixSet = false;
83  p_et = -DBL_MAX;
84  p_axisP = 3;
85  p_degree = 2;
86  p_degreeApplied = false;
87  p_noOverride = true;
88  p_axis1 = 3;
89  p_axis2 = 1;
90  p_axis3 = 3;
91  p_minimizeCache = No;
92  p_hasAngularVelocity = false;
93  p_av.resize(3);
96  p_fullCacheSize = 0;
97 
98  // Determine the axis for the velocity vector
99  QString key = "INS" + toString(frameCode) + "_TRANSX";
100  SpiceDouble transX[2];
101  SpiceInt number;
102  SpiceBoolean found;
103  //Read starting at element 1 (skipping element 0)
104  gdpool_c(key.toAscii().data(), 1, 2, &number, transX, &found);
105 
106  if (!found) {
107  QString msg = "Cannot find [" + key + "] in text kernels";
108  throw IException(IException::Io, msg, _FILEINFO_);
109  }
110 
111  p_axisV = 2;
112  if (transX[0] < transX[1]) p_axisV = 1;
113 
115  }
116 
117 
122  p_cacheTime = rotToCopy.p_cacheTime;
123  p_cache = rotToCopy.p_cache;
124  p_cacheAv = rotToCopy.p_cacheAv;
125  p_av = rotToCopy.p_av;
126  p_degree = rotToCopy.p_degree;
127  p_axis1 = rotToCopy.p_axis1;
128  p_axis2 = rotToCopy.p_axis2;
129  p_axis3 = rotToCopy.p_axis3;
130 
132  p_timeFrames = rotToCopy.p_timeFrames;
133  p_timeBias = rotToCopy.p_timeBias;
134 
135  p_et = rotToCopy.p_et;
136  p_quaternion = rotToCopy.p_quaternion;
137  p_matrixSet = rotToCopy.p_matrixSet;
138  p_source = rotToCopy.p_source;
139  p_axisP = rotToCopy.p_axisP;
140  p_axisV = rotToCopy.p_axisV;
141  p_targetCode = rotToCopy.p_targetCode;
142  p_baseTime = rotToCopy.p_baseTime;
143  p_timeScale = rotToCopy.p_timeScale;
144  p_degreeApplied = rotToCopy.p_degreeApplied;
145 
146 // for (std::vector<double>::size_type i = 0; i < rotToCopy.p_coefficients[0].size(); i++)
147  for (int i = 0; i < 3; i++)
148  p_coefficients[i] = rotToCopy.p_coefficients[i];
149 
150  p_noOverride = rotToCopy.p_noOverride;
153  p_minimizeCache = rotToCopy.p_minimizeCache;
156  p_fullCacheSize = rotToCopy.p_fullCacheSize;
157  p_TC = rotToCopy.p_TC;
158 
159  p_CJ = rotToCopy.p_CJ;
160  p_degree = rotToCopy.p_degree;
162 
163  }
164 
165 
170 
176  void SpiceRotation::SetFrame(int frameCode) {
177  p_constantFrames[0] = frameCode;
178  }
179 
187  return p_constantFrames[0];
188  }
189 
202  void SpiceRotation::SetTimeBias(double timeBias) {
203  p_timeBias = timeBias;
204  }
205 
219 
220  // Save the time
221  if (p_et == et) return;
222  p_et = et;
223 
224  // Read from the cache
225  if (p_source == Memcache) {
226  SetEphemerisTimeMemcache();
227  }
228 
229  // Apply coefficients defining a function for each of the three camera angles and angular velocity if available
230  else if (p_source == PolyFunction) {
231  SetEphemerisTimePolyFunction();
232  }
233  // Apply coefficients defining a function for each of the three camera angles and angular velocity if available
234  else if (p_source == PolyFunctionOverSpice) {
235  SetEphemerisTimePolyFunctionOverSpice();
236  }
237  // Read from the kernel
238  else if (p_source == Spice) {
239  SetEphemerisTimeSpice();
240  // Retrieve the J2000 (code=1) to reference rotation matrix
241  }
242 
243  // Compute from Nadir
244  else {
245  SetEphemerisTimeNadir();
246  }
247 
248  // Set the quaternion for this rotation
249 // p_quaternion.Set ( p_CJ );
250  }
251 
257  return p_et;
258  }
259 
264  bool SpiceRotation::IsCached() const {
265  return (p_cache.size() > 0);
266  }
267 
272  void SpiceRotation::MinimizeCache(DownsizeStatus status) {
273  p_minimizeCache = status;
274  }
275 
289  void SpiceRotation::LoadCache(double startTime, double endTime, int size) {
290 
291  // Check for valid arguments
292  if (size <= 0) {
293  QString msg = "Argument cacheSize must not be less or equal to zero";
295  }
296 
297  if (startTime > endTime) {
298  QString msg = "Argument startTime must be less than or equal to endTime";
300  }
301 
302  if ((startTime != endTime) && (size == 1)) {
303  QString msg = "Cache size must be more than 1 if startTime endTime differ";
305  }
306 
307  // Make sure cache isn't already loaded
308  if (p_source == Memcache) {
309  QString msg = "A SpiceRotation cache has already been created";
311  }
312 
313  // Save full cache parameterssetis
314  p_fullCacheStartTime = startTime;
315  p_fullCacheEndTime = endTime;
316  p_fullCacheSize = size;
317 
318  // Make sure the constant frame is loaded. This method also does the frame trace.
319  if (p_timeFrames.size() == 0) InitConstantRotation(startTime);
320 
321  LoadTimeCache();
322  int cacheSize = p_cacheTime.size();
323 
324  // Loop and load the cache
325  for (int i = 0; i < cacheSize; i++) {
326  double et = p_cacheTime[i];
327  SetEphemerisTime(et);
328  p_cache.push_back(p_CJ);
329  if (p_hasAngularVelocity) p_cacheAv.push_back(p_av);
330  }
331  p_source = Memcache;
332 
333 // Downsize already loaded caches (both time and quats)
334  if (p_minimizeCache == Yes && cacheSize > 5) {
335  LoadTimeCache();
336  }
337  }
338 
339 
353  void SpiceRotation::LoadCache(double time) {
354  LoadCache(time, time, 1);
355  }
356 
378  // Clear any existing cached data to make it reentrant (KJB 2011-07-20).
379  p_timeFrames.clear();
380  p_TC.clear();
381  p_cache.clear();
382  p_cacheTime.clear();
383  p_cacheAv.clear();
384  p_hasAngularVelocity = false;
385 
386  // Load the constant and time-based frame traces and the constant rotation
387  if (table.Label().hasKeyword("TimeDependentFrames")) {
388  PvlKeyword labelTimeFrames = table.Label()["TimeDependentFrames"];
389  for (int i = 0; i < labelTimeFrames.size(); i++) {
390  p_timeFrames.push_back(toInt(labelTimeFrames[i]));
391  }
392  }
393  else {
394  p_timeFrames.push_back(p_constantFrames[0]);
395  p_timeFrames.push_back(J2000Code);
396  }
397 
398  if (table.Label().hasKeyword("ConstantRotation")) {
399  PvlKeyword labelConstantFrames = table.Label()["ConstantFrames"];
400  p_constantFrames.clear();
401 
402  for (int i = 0; i < labelConstantFrames.size(); i++) {
403  p_constantFrames.push_back(toInt(labelConstantFrames[i]));
404  }
405  PvlKeyword labelConstantRotation = table.Label()["ConstantRotation"];
406 
407  for (int i = 0; i < labelConstantRotation.size(); i++) {
408  p_TC.push_back(toDouble(labelConstantRotation[i]));
409  }
410  }
411  else {
412  p_TC.resize(9);
413  ident_c((SpiceDouble( *)[3]) &p_TC[0]);
414  }
415 
416  // Load the full cache time information from the label if available
417  if (table.Label().hasKeyword("CkTableStartTime")) {
418  p_fullCacheStartTime = toDouble(table.Label().findKeyword("CkTableStartTime")[0]);
419  }
420  if (table.Label().hasKeyword("CkTableEndTime")) {
421  p_fullCacheEndTime = toDouble(table.Label().findKeyword("CkTableEndTime")[0]);
422  }
423  if (table.Label().hasKeyword("CkTableOriginalSize")) {
424  p_fullCacheSize = toInt(table.Label().findKeyword("CkTableOriginalSize")[0]);
425  }
426 
427  int recFields = table[0].Fields();
428 
429  // Loop through and move the table to the cache. Retrieve the first record to
430  // establish the type of cache and then use the appropriate loop.
431 
432  // list table of quaternion and time
433  if (recFields == 5) {
434  for (int r = 0; r < table.Records(); r++) {
435  TableRecord &rec = table[r];
436 
437  if (rec.Fields() != recFields) {
438  // throw and error
439  }
440 
441  std::vector<double> j2000Quat;
442  j2000Quat.push_back((double)rec[0]);
443  j2000Quat.push_back((double)rec[1]);
444  j2000Quat.push_back((double)rec[2]);
445  j2000Quat.push_back((double)rec[3]);
446 
447  Quaternion q(j2000Quat);
448  std::vector<double> CJ = q.ToMatrix();
449  p_cache.push_back(CJ);
450  p_cacheTime.push_back((double)rec[4]);
451  }
452  p_source = Memcache;
453  }
454 
455  // list table of quaternion, angular velocity vector, and time
456  else if (recFields == 8) {
457  for (int r = 0; r < table.Records(); r++) {
458  TableRecord &rec = table[r];
459 
460  if (rec.Fields() != recFields) {
461  // throw and error
462  }
463 
464  std::vector<double> j2000Quat;
465  j2000Quat.push_back((double)rec[0]);
466  j2000Quat.push_back((double)rec[1]);
467  j2000Quat.push_back((double)rec[2]);
468  j2000Quat.push_back((double)rec[3]);
469 
470 
471  Quaternion q(j2000Quat);
472  std::vector<double> CJ = q.ToMatrix();
473  p_cache.push_back(CJ);
474 
475  std::vector<double> av;
476  av.push_back((double)rec[4]);
477  av.push_back((double)rec[5]);
478  av.push_back((double)rec[6]);
479  p_cacheAv.push_back(av);
480 
481  p_cacheTime.push_back((double)rec[7]);
482  p_hasAngularVelocity = true;
483  }
484  p_source = Memcache;
485  }
486 
487  // coefficient table for angle1, angle2, and angle3
488  else if (recFields == 3) {
489  std::vector<double> coeffAng1, coeffAng2, coeffAng3;
490 
491  for (int r = 0; r < table.Records() - 1; r++) {
492  TableRecord &rec = table[r];
493 
494  if (rec.Fields() != recFields) {
495  // throw an error
496  }
497  coeffAng1.push_back((double)rec[0]);
498  coeffAng2.push_back((double)rec[1]);
499  coeffAng3.push_back((double)rec[2]);
500  }
501 
502  // Take care of time parameters
503  TableRecord &rec = table[table.Records()-1];
504  double baseTime = (double)rec[0];
505  double timeScale = (double)rec[1];
506  double degree = (double)rec[2];
507  SetPolynomialDegree((int) degree);
508  SetOverrideBaseTime(baseTime, timeScale);
509  SetPolynomial(coeffAng1, coeffAng2, coeffAng3);
511  if (degree > 0) p_hasAngularVelocity = true;
512  if (degree == 0 && p_cacheAv.size() > 0) p_hasAngularVelocity = true;
513  }
514  else {
515  QString msg = "Expecting either three, five, or eight fields in the SpiceRotation table";
517  }
518  }
519 
520 
521 
530  // Save current et
531  double et = p_et;
532  p_et = -DBL_MAX;
533 
534  if (p_source == PolyFunction) {
535  // Clear existing matrices from cache
536  p_cacheTime.clear();
537  p_cache.clear();
538 
539  // Clear the angular velocity cache if we can calculate it instead. It can't be calculated for
540  // functions of degree 0 (framing cameras), so keep the original av. It is better than nothing.
541  if (p_degree > 0 && p_cacheAv.size() > 1) p_cacheAv.clear();
542 
543  // Load the time cache first
544  p_minimizeCache = No;
545  LoadTimeCache();
546 
547  if (p_fullCacheSize > 1) {
548  // Load the matrix and av caches
549  for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
550  SetEphemerisTime(p_cacheTime.at(pos));
551  p_cache.push_back(p_CJ);
552  p_cacheAv.push_back(p_av);
553  }
554  }
555  else {
556  // Load the matrix for the single updated time instance
558  p_cache.push_back(p_CJ);
559  }
560  }
561  else if (p_source == PolyFunctionOverSpice) {
562  SpiceRotation tempRot(*this);
563 
564  std::vector<double>::size_type maxSize = p_fullCacheSize;
565 
566  // Clear the existing caches
567  p_cache.clear();
568  p_cacheTime.clear();
569  p_cacheAv.clear();
570 
571  // Reload the time cache first
572  p_minimizeCache = No;
573  LoadTimeCache();
574 
575  for (std::vector<double>::size_type pos = 0; pos < maxSize; pos++) {
576  tempRot.SetEphemerisTime(p_cacheTime.at(pos));
577  p_cache.push_back(tempRot.TimeBasedMatrix());
578  if (p_hasAngularVelocity) p_cacheAv.push_back(tempRot.AngularVelocity());
579  }
580  }
581  else { //(p_source < PolyFunction)
582  QString msg = "The SpiceRotation has not yet been fit to a function";
584  }
585 
586  // Set source to cache and reset current et
587  // Make sure source is Memcache now
588  p_source = Memcache;
589  p_et = -DBL_MAX;
590  SetEphemerisTime(et);
591  }
592 
593 
594 
603  Table SpiceRotation::LineCache(const QString &tableName) {
604 
605  // Apply the function and fill the caches
607 
608  if (p_source != Memcache) {
609  QString msg = "Only cached rotations can be returned as a line cache of quaternions and time";
611  }
612  // Load the table and return it to caller
613  return Cache(tableName);
614  }
615 
616 
617 
634  Table SpiceRotation::Cache(const QString &tableName) {
635  // First handle conversion of PolyFunctionOverSpiceConstant
636  // by converting it to the full Memcache and try to downsize it
638  LineCache(tableName);
639 
640  //std::cout << "Full cache size is " << p_cache.size() << endl;
641 
642  p_minimizeCache = Yes;
643  LoadTimeCache();
644 
645  //std::cout << "Minimized cache size is " << p_cache.size() << endl;
646  }
647 
648  // Load the list of rotations and their corresponding times
649  if (p_source == Memcache) {
650  TableField q0("J2000Q0", TableField::Double);
651  TableField q1("J2000Q1", TableField::Double);
652  TableField q2("J2000Q2", TableField::Double);
653  TableField q3("J2000Q3", TableField::Double);
655 
656  TableRecord record;
657  record += q0;
658  record += q1;
659  record += q2;
660  record += q3;
661  int timePos = 4;
662 
663  if (p_hasAngularVelocity) {
664  TableField av1("AV1", TableField::Double);
665  TableField av2("AV2", TableField::Double);
666  TableField av3("AV3", TableField::Double);
667  record += av1;
668  record += av2;
669  record += av3;
670  timePos = 7;
671  }
672 
673  record += t;
674  Table table(tableName, record);
675 
676  for (int i = 0; i < (int)p_cache.size(); i++) {
677  Quaternion q(p_cache[i]);
678  std::vector<double> v = q.GetQuaternion();
679  record[0] = v[0];
680  record[1] = v[1];
681  record[2] = v[2];
682  record[3] = v[3];
683 
684  if (p_hasAngularVelocity) {
685  record[4] = p_cacheAv[i][0];
686  record[5] = p_cacheAv[i][1];
687  record[6] = p_cacheAv[i][2];
688  }
689 
690  record[timePos] = p_cacheTime[i];
691  table += record;
692  }
693 
694  CacheLabel(table);
695  return table;
696  }
697  // Just load the position for the single epoch
698  else if (p_source == PolyFunction && p_degree == 0 && p_fullCacheSize == 1) {
699  return LineCache(tableName);
700  }
701  // Load the coefficients for the curves fit to the 3 camera angles
702  else if (p_source == PolyFunction) {
703  TableField angle1("J2000Ang1", TableField::Double);
704  TableField angle2("J2000Ang2", TableField::Double);
705  TableField angle3("J2000Ang3", TableField::Double);
706 
707  TableRecord record;
708  record += angle1;
709  record += angle2;
710  record += angle3;
711 
712  Table table(tableName, record);
713 
714  for (int cindex = 0; cindex < p_degree + 1; cindex++) {
715  record[0] = p_coefficients[0][cindex];
716  record[1] = p_coefficients[1][cindex];
717  record[2] = p_coefficients[2][cindex];
718  table += record;
719  }
720 
721  // Load one more table entry with the time adjustments for the fit equation
722  // t = (et - baseTime)/ timeScale
723  record[0] = p_baseTime;
724  record[1] = p_timeScale;
725  record[2] = (double) p_degree;
726 
727  table += record;
728  CacheLabel(table);
729  return table;
730  }
731  else {
732  // throw an error -- should not get here -- invalid Spice Source
733  QString msg = "To create table source of data must be either Memcache or PolyFunction";
735  }
736  }
737 
738 
739 
748  // Load the constant and time-based frame traces and the constant rotation
749  // into the table as labels
750  if (p_timeFrames.size() > 1) {
751  table.Label() += PvlKeyword("TimeDependentFrames");
752 
753  for (int i = 0; i < (int) p_timeFrames.size(); i++) {
754  table.Label()["TimeDependentFrames"].addValue(toString(p_timeFrames[i]));
755  }
756  }
757 
758  if (p_constantFrames.size() > 1) {
759  table.Label() += PvlKeyword("ConstantFrames");
760 
761  for (int i = 0; i < (int) p_constantFrames.size(); i++) {
762  table.Label()["ConstantFrames"].addValue(toString(p_constantFrames[i]));
763  }
764 
765  table.Label() += PvlKeyword("ConstantRotation");
766 
767  for (int i = 0; i < (int) p_TC.size(); i++) {
768  table.Label()["ConstantRotation"].addValue(toString(p_TC[i]));
769  }
770  }
771 
772  // Write original time coverage
773  if (p_fullCacheStartTime != 0) {
774  table.Label() += PvlKeyword("CkTableStartTime");
775  table.Label()["CkTableStartTime"].addValue(toString(p_fullCacheStartTime));
776  }
777  if (p_fullCacheEndTime != 0) {
778  table.Label() += PvlKeyword("CkTableEndTime");
779  table.Label()["CkTableEndTime"].addValue(toString(p_fullCacheEndTime));
780  }
781  if (p_fullCacheSize != 0) {
782  table.Label() += PvlKeyword("CkTableOriginalSize");
783  table.Label()["CkTableOriginalSize"].addValue(toString(p_fullCacheSize));
784  }
786  }
787 
788 
792  std::vector<double> SpiceRotation::GetCenterAngles() {
793  // Compute the center time
794  double etCenter = (p_fullCacheEndTime + p_fullCacheStartTime) / 2.;
795  SetEphemerisTime(etCenter);
796 
797  return Angles(p_axis3, p_axis2, p_axis1);
798  }
799 
800 
806  std::vector<double> SpiceRotation::Angles(int axis3, int axis2, int axis1) {
808 
809  SpiceDouble ang1, ang2, ang3;
810  m2eul_c((SpiceDouble *) &p_CJ[0], axis3, axis2, axis1, &ang3, &ang2, &ang1);
811 
812  std::vector<double> angles;
813  angles.push_back(ang1);
814  angles.push_back(ang2);
815  angles.push_back(ang3);
816 
818  return angles;
819  }
820 
824  std::vector<double> SpiceRotation::AngularVelocity() {
825  return p_av;
826  }
827 
834  return p_constantFrames;
835  }
836 
841  std::vector<int> SpiceRotation::TimeFrameChain() {
842  return p_timeFrames;
843  }
844 
850  return p_hasAngularVelocity;
851  }
852 
859  std::vector<double> SpiceRotation::J2000Vector(const std::vector<double>& rVec) {
861 
862  std::vector<double> jVec;
863 
864  if (rVec.size() == 3) {
865  double TJ[3][3];
866  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], TJ);
867  jVec.resize(3);
868  mtxv_c(TJ, (SpiceDouble *) &rVec[0], (SpiceDouble *) &jVec[0]);
869  }
870  else if (rVec.size() == 6) {
871  // See Naif routine frmchg for the format of the state matrix. The constant rotation, TC,
872  // has a derivative with respect to time of I.
873  if (!p_hasAngularVelocity) {
874  // throw an error
875  }
876  std::vector<double> stateTJ(36);
877  stateTJ = StateTJ();
878 
879  // Now invert (inverse of a state matrix is NOT simply the transpose)
880  xpose6_c(&stateTJ[0], (SpiceDouble( *) [6]) &stateTJ[0]);
881  double stateJT[6][6];
882  invstm_((doublereal *) &stateTJ[0], (doublereal *) stateJT);
883  xpose6_c(stateJT, stateJT);
884  jVec.resize(6);
885 
886  mxvg_c(stateJT, (SpiceDouble *) &rVec[0], 6, 6, (SpiceDouble *) &jVec[0]);
887  }
888 
890  return (jVec);
891  }
892 
893 
901  std::vector<double>
902  SpiceRotation::ReferenceVector(const std::vector<double>& jVec) {
904 
905  std::vector<double> rVec(3);
906 
907  if (jVec.size() == 3) {
908  double TJ[3][3];
909  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], TJ);
910  rVec.resize(3);
911  mxv_c(TJ, (SpiceDouble *) &jVec[0], (SpiceDouble *) &rVec[0]);
912  }
913  else if (jVec.size() == 6) {
914  // See Naif routine frmchg for the format of the state matrix. The constant rotation, TC,
915  // has a derivative with respect to time of I.
916  if (!p_hasAngularVelocity) {
917  // throw an error
918  }
919  std::vector<double> stateTJ(36);
920  stateTJ = StateTJ();
921  rVec.resize(6);
922  mxvg_c((SpiceDouble *) &stateTJ[0], (SpiceDouble *) &jVec[0], 6, 6, (SpiceDouble *) &rVec[0]);
923  }
924 
926  return (rVec);
927  }
928 
929 
940  std::vector<double> coeffAng1, coeffAng2, coeffAng3;
941 
942  // Rotation is already stored as a polynomial -- throw an error
943  if (p_source == PolyFunction) {
944  // Nothing to do
945  return;
946 // QString msg = "Rotation already fit to a polynomial -- spiceint first to refit";
947 // throw IException(IException::User,msg,_FILEINFO_);
948  }
949 
950  // Adjust degree of polynomial on available data
951  if (p_cache.size() == 1) {
952  p_degree = 0;
953  }
954  else if (p_cache.size() == 2) {
955  p_degree = 1;
956  }
957 
958  //Check for polynomial over original pointing constant and initialize coefficients
959  if (type == PolyFunctionOverSpice) {
960  coeffAng1.assign(p_degree + 1, 0.);
961  coeffAng2.assign(p_degree + 1, 0.);
962  coeffAng3.assign(p_degree + 1, 0.);
963  SetPolynomial(coeffAng1, coeffAng2, coeffAng3, type);
964  return;
965  }
966 
970  //
971  LeastSquares *fitAng1 = new LeastSquares(function1);
972  LeastSquares *fitAng2 = new LeastSquares(function2);
973  LeastSquares *fitAng3 = new LeastSquares(function3);
974 
975  // Compute the base time
976  ComputeBaseTime();
977  std::vector<double> time;
978 
979  if (p_cache.size() == 1) {
980  double t = p_cacheTime.at(0);
981  SetEphemerisTime(t);
982  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
983  coeffAng1.push_back(angles[0]);
984  coeffAng2.push_back(angles[1]);
985  coeffAng3.push_back(angles[2]);
986  }
987  else if (p_cache.size() == 2) {
988 // Load the times and get the corresponding rotation angles
989  p_degree = 1;
990  double t1 = p_cacheTime.at(0);
991  SetEphemerisTime(t1);
992  t1 -= p_baseTime;
993  t1 = t1 / p_timeScale;
994  std::vector<double> angles1 = Angles(p_axis3, p_axis2, p_axis1);
995  double t2 = p_cacheTime.at(1);
996  SetEphemerisTime(t2);
997  t2 -= p_baseTime;
998  t2 = t2 / p_timeScale;
999  std::vector<double> angles2 = Angles(p_axis3, p_axis2, p_axis1);
1000  angles2[0] = WrapAngle(angles1[0], angles2[0]);
1001  angles2[2] = WrapAngle(angles1[2], angles2[2]);
1002  double slope[3];
1003  double intercept[3];
1004 
1005 // Compute the linear equation for each angle and save them
1006  for (int angleIndex = 0; angleIndex < 3; angleIndex++) {
1007  Isis::LineEquation angline(t1, angles1[angleIndex], t2, angles2[angleIndex]);
1008  slope[angleIndex] = angline.Slope();
1009  intercept[angleIndex] = angline.Intercept();
1010  }
1011  coeffAng1.push_back(intercept[0]);
1012  coeffAng1.push_back(slope[0]);
1013  coeffAng2.push_back(intercept[1]);
1014  coeffAng2.push_back(slope[1]);
1015  coeffAng3.push_back(intercept[2]);
1016  coeffAng3.push_back(slope[2]);
1017  }
1018  else {
1019  // Load the known values to compute the fit equation
1020  double start1 = 0.; // value of 1st angle1 in cache
1021  double start3 = 0.; // value of 1st angle1 in cache
1022 
1023  for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
1024  double t = p_cacheTime.at(pos);
1025  time.push_back((t - p_baseTime) / p_timeScale);
1026  SetEphemerisTime(t);
1027  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1028 
1029 // Fix 180/-180 crossovers on angles 1 and 3 before doing fit.
1030  if (pos == 0) {
1031  start1 = angles[0];
1032  start3 = angles[2];
1033  }
1034  else {
1035  angles[0] = WrapAngle(start1, angles[0]);
1036  angles[2] = WrapAngle(start3, angles[2]);
1037  }
1038 
1039  fitAng1->AddKnown(time, angles[0]);
1040  fitAng2->AddKnown(time, angles[1]);
1041  fitAng3->AddKnown(time, angles[2]);
1042  time.clear();
1043 
1044  }
1045  //Solve the equations for the coefficients
1046  fitAng1->Solve();
1047  fitAng2->Solve();
1048  fitAng3->Solve();
1049 
1050  // Delete the least squares objects now that we have all the coefficients
1051  delete fitAng1;
1052  delete fitAng2;
1053  delete fitAng3;
1054 
1055  // For now assume all three angles are fit to a polynomial. Later they may
1056  // each be fit to a unique basis function.
1057  // Fill the coefficient vectors
1058 
1059  for (int i = 0; i < function1.Coefficients(); i++) {
1060  coeffAng1.push_back(function1.Coefficient(i));
1061  coeffAng2.push_back(function2.Coefficient(i));
1062  coeffAng3.push_back(function3.Coefficient(i));
1063  }
1064 
1065  }
1066 
1067  // Now that the coefficients have been calculated set the polynomial with them
1068  SetPolynomial(coeffAng1, coeffAng2, coeffAng3);
1069 
1071  return;
1072  }
1073 
1074 
1075 
1088  void SpiceRotation::SetPolynomial(const std::vector<double>& coeffAng1,
1089  const std::vector<double>& coeffAng2,
1090  const std::vector<double>& coeffAng3,
1091  const Source type) {
1092 
1097 
1098  // Load the functions with the coefficients
1099  function1.SetCoefficients(coeffAng1);
1100  function2.SetCoefficients(coeffAng2);
1101  function3.SetCoefficients(coeffAng3);
1102 
1103  // Compute the base time
1104  ComputeBaseTime();
1105 
1106  // Save the current coefficients
1107  p_coefficients[0] = coeffAng1;
1108  p_coefficients[1] = coeffAng2;
1109  p_coefficients[2] = coeffAng3;
1110 
1111  // Set the flag indicating p_degree has been applied to the camera angles, the
1112  // coefficients of the polynomials have been saved, and the cache reloaded from
1113  // the polynomials
1114  p_degreeApplied = true;
1115  // p_source = PolyFunction;
1116  p_source = type;
1117 
1118  // Update the current rotation
1119  double et = p_et;
1120  p_et = -DBL_MAX;
1121  SetEphemerisTime(et);
1122 
1124  return;
1125  }
1126 
1127 
1128 
1140  void SpiceRotation::GetPolynomial(std::vector<double>& coeffAng1,
1141  std::vector<double>& coeffAng2,
1142  std::vector<double>& coeffAng3) {
1143  coeffAng1 = p_coefficients[0];
1144  coeffAng2 = p_coefficients[1];
1145  coeffAng3 = p_coefficients[2];
1146 
1147  return;
1148  }
1149 
1150 
1151 
1154  if (p_noOverride) {
1155  p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1157  // Take care of case where 1st and last times are the same
1158  if (p_timeScale == 0) p_timeScale = 1.0;
1159  }
1160  else {
1163  }
1164 
1165  return;
1166  }
1167 
1168 
1175  void SpiceRotation::SetOverrideBaseTime(double baseTime, double timeScale) {
1176  p_overrideBaseTime = baseTime;
1177  p_overrideTimeScale = timeScale;
1178  p_noOverride = false;
1179  return;
1180  }
1181 
1182 
1183 
1193  double SpiceRotation::DPolynomial(const int coeffIndex) {
1194  double derivative;
1195  double time = (p_et - p_baseTime) / p_timeScale;
1196 
1197  if (coeffIndex > 0 && coeffIndex <= p_degree) {
1198  derivative = pow(time, coeffIndex);
1199  }
1200  else if (coeffIndex == 0) {
1201  derivative = 1;
1202  }
1203  else {
1204  Isis::IString msg = "Coeff index, " + Isis::IString(coeffIndex) + " exceeds degree of polynomial";
1206  }
1207  return derivative;
1208  }
1209 
1210 
1211 
1225  std::vector<double>
1226  SpiceRotation::ToReferencePartial(std::vector<double>& lookJ,
1227  SpiceRotation::PartialType partialVar, int coeffIndex) {
1229  //**TODO** To save time possibly save partial matrices
1230 
1231  // Get the rotation angles and form the derivative matrix for the partialVar
1232  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1233  int angleIndex = partialVar;
1234  int axes[3] = {p_axis1, p_axis2, p_axis3};
1235  double angle = angles.at(angleIndex);
1236 
1237  double dmatrix[3][3];
1238  drotat_(&angle, (integer *) axes + angleIndex, (doublereal *) dmatrix);
1239  // Transpose to obtain row-major order
1240  xpose_c(dmatrix, dmatrix);
1241 
1242  // Get the derivative of the polynomial with respect to partialVar
1243 
1244  double dpoly = DPolynomial(coeffIndex);
1245 
1246  // Multiply dpoly to complete dmatrix
1247  for (int row = 0; row < 3; row++) {
1248  for (int col = 0; col < 3; col++) {
1249  dmatrix[row][col] *= dpoly;
1250  }
1251  }
1252  // Apply the other 2 angles and chain them all together
1253  double dCJ[3][3];
1254  switch(angleIndex) {
1255  case 0:
1256  rotmat_c(dmatrix, angles[1], axes[1], dCJ);
1257  rotmat_c(dCJ, angles[2], axes[2], dCJ);
1258  break;
1259  case 1:
1260  rotate_c(angles[0], axes[0], dCJ);
1261  mxm_c(dmatrix, dCJ, dCJ);
1262  rotmat_c(dCJ, angles[2], axes[2], dCJ);
1263  break;
1264  case 2:
1265  rotate_c(angles[0], axes[0], dCJ);
1266  rotmat_c(dCJ, angles[1], axes[1], dCJ);
1267  mxm_c(dmatrix, dCJ, dCJ);
1268  break;
1269  }
1270 
1271  // Multiply the constant matrix to rotate to target frame
1272  double dTJ[3][3];
1273  mxm_c((SpiceDouble *) &p_TC[0], dCJ[0], dTJ);
1274 
1275  // Finally rotate the J2000 vector with the derivative matrix, dTJ
1276  std::vector<double> lookdT(3);
1277 
1278  mxv_c(dTJ, (const SpiceDouble *) &lookJ[0], (SpiceDouble *) &lookdT[0]);
1279 
1281  return lookdT;
1282  }
1283 
1284 
1293  double SpiceRotation::WrapAngle(double compareAngle, double angle) {
1295  double diff1 = compareAngle - angle;
1296 
1297  if (diff1 < -1 * pi_c()) {
1298  angle -= twopi_c();
1299  }
1300  else if (diff1 > pi_c()) {
1301  angle += twopi_c();
1302  }
1303 
1305  return angle;
1306  }
1307 
1320  // Adjust the degree for the data
1321  if (p_fullCacheSize == 1) {
1322  degree = 0;
1323  }
1324  else if (p_fullCacheSize == 2) {
1325  degree = 1;
1326  }
1327  // If polynomials have not been applied yet then simply set the degree and return
1328  if (!p_degreeApplied) {
1329  p_degree = degree;
1330  }
1331 
1332  // Otherwise the existing polynomials need to be either expanded ...
1333  else if (p_degree < degree) { // (increase the number of terms)
1334  std::vector<double> coefAngle1(p_coefficients[0]),
1335  coefAngle2(p_coefficients[1]),
1336  coefAngle3(p_coefficients[2]);
1337 
1338  for (int icoef = p_degree + 1; icoef <= degree; icoef++) {
1339  coefAngle1.push_back(0.);
1340  coefAngle2.push_back(0.);
1341  coefAngle3.push_back(0.);
1342  }
1343  p_degree = degree;
1344  SetPolynomial(coefAngle1, coefAngle2, coefAngle3, p_source);
1345  }
1346  // ... or reduced (decrease the number of terms)
1347  else if (p_degree > degree) {
1348  std::vector<double> coefAngle1(degree + 1),
1349  coefAngle2(degree + 1),
1350  coefAngle3(degree + 1);
1351 
1352  for (int icoef = 0; icoef <= degree; icoef++) {
1353  coefAngle1[icoef] = p_coefficients[0][icoef];
1354  coefAngle2[icoef] = p_coefficients[1][icoef];
1355  coefAngle3[icoef] = p_coefficients[2][icoef];
1356  }
1357  p_degree = degree;
1358  SetPolynomial(coefAngle1, coefAngle2, coefAngle3, p_source);
1359  }
1360  }
1361 
1367  return p_source;
1368  }
1369 
1375  p_source = source;
1376  return;
1377  }
1378 
1384  return p_baseTime;
1385  }
1386 
1392  return p_timeScale;
1393  }
1394 
1404  void SpiceRotation::SetAxes(int axis1, int axis2, int axis3) {
1405  if (axis1 < 1 || axis2 < 1 || axis3 < 1 || axis1 > 3 || axis2 > 3 || axis3 > 3) {
1406  QString msg = "A rotation axis is outside the valid range of 1 to 3";
1408  }
1409  p_axis1 = axis1;
1410  p_axis2 = axis2;
1411  p_axis3 = axis3;
1412  }
1413 
1420  int count = 0;
1421 
1422  double observStart = p_fullCacheStartTime + p_timeBias;
1423  double observEnd = p_fullCacheEndTime + p_timeBias;
1424  double currentTime = observStart; // Added 12-03-2009 to allow observations to cross segment boundaries
1425  bool timeLoaded = false;
1426 
1427  // Get number of ck loaded for this rotation. This method assumes only one SpiceRotation
1428  // object is loaded.
1430  ktotal_c("ck", (SpiceInt *) &count);
1431 
1432  // Downsize the loaded cache
1433  if ((p_source == Memcache) && p_minimizeCache == Yes) {
1434  // Multiple ck case, type 5 ck case, or PolyFunctionOverSpice
1435  // final step -- downsize loaded cache and reload
1436 
1437  if (p_fullCacheSize != (int) p_cache.size()) {
1438 
1439  Isis::IString msg = "Full cache size does NOT match cache size in LoadTimeCache -- should never happen";
1441  }
1442 
1443  SpiceDouble timeSclkdp[p_fullCacheSize];
1444  SpiceDouble quats[p_fullCacheSize][4];
1445  double avvs[p_fullCacheSize][3];// Angular velocity vector
1446 
1447  // We will treat et as the sclock time and avoid converting back and forth
1448  for (int r = 0; r < p_fullCacheSize; r++) {
1449  timeSclkdp[r] = p_cacheTime[r];
1450  SpiceDouble CJ[9] = { p_cache[r][0], p_cache[r][1], p_cache[r][2],
1451  p_cache[r][3], p_cache[r][4], p_cache[r][5],
1452  p_cache[r][6], p_cache[r][7], p_cache[r][8]
1453  };
1454  m2q_c(CJ, quats[r]);
1455  if (p_hasAngularVelocity) {
1456  vequ_c((SpiceDouble *) &p_cacheAv[r][0], avvs[r]);
1457  }
1458  }
1459 
1460  double cubeStarts = timeSclkdp[0]; //,timsSclkdp[ckBlob.Records()-1] };
1461  double radTol = 0.000000017453; //.000001 degrees Make this instrument dependent TODO
1462  SpiceInt avflag = 1; // Don't use angular velocity for now
1463  SpiceInt nints = 1; // Always make an observation a single interpolation interval
1464  double dparr[p_fullCacheSize]; // Double precision work array
1465  SpiceInt intarr[p_fullCacheSize]; // Integer work array
1466  SpiceInt sizOut = p_fullCacheSize; // Size of downsized cache
1467 
1468  ck3sdn(radTol, avflag, (int *) &sizOut, timeSclkdp, (doublereal *) quats,
1469  (SpiceDouble *) avvs, nints, &cubeStarts, dparr, (int *) intarr);
1470 
1471  // Clear full cache and load with downsized version
1472  p_cacheTime.clear();
1473  p_cache.clear();
1474  p_cacheAv.clear();
1475  std::vector<double> av;
1476  av.resize(3);
1477 
1478  for (int r = 0; r < sizOut; r++) {
1479  SpiceDouble et;
1480  // sct2e_c(spcode, timeSclkdp[r], &et);
1481  et = timeSclkdp[r];
1482  p_cacheTime.push_back(et);
1483  std::vector<double> CJ(9);
1484  q2m_c(quats[r], (SpiceDouble( *)[3]) &CJ[0]);
1485  p_cache.push_back(CJ);
1486  vequ_c(avvs[r], (SpiceDouble *) &av[0]);
1487  p_cacheAv.push_back(av);
1488  }
1489 
1490  timeLoaded = true;
1491  p_minimizeCache = Done;
1492  }
1493  else if (count == 1 && p_minimizeCache == Yes) {
1494  // case of a single ck -- read instances and data straight from kernel for given time range
1495  SpiceInt handle;
1496 
1497  // Define some Naif constants
1498  int FILESIZ = 128;
1499  int TYPESIZ = 32;
1500  int SOURCESIZ = 128;
1501 // double DIRSIZ = 100;
1502 
1503  SpiceChar file[FILESIZ];
1504  SpiceChar filtyp[TYPESIZ];
1505  SpiceChar source[SOURCESIZ];
1506 
1507  SpiceBoolean found;
1508  bool observationSpansToNextSegment = false;
1509 
1510  double segStartEt;
1511  double segStopEt;
1512 
1513  kdata_c(0, "ck", FILESIZ, TYPESIZ, SOURCESIZ, file, filtyp, source, &handle, &found);
1514  dafbfs_c(handle);
1515  daffna_c(&found);
1516  int spCode = ((int)(p_constantFrames[0] / 1000)) * 1000;
1517 
1518  while (found) {
1519  observationSpansToNextSegment = false;
1520  double sum[10]; // daf segment summary
1521  double dc[2]; // segment starting and ending times in tics
1522  SpiceInt ic[6]; // segment summary values:
1523  // instrument code for platform,
1524  // reference frame code,
1525  // data type,
1526  // velocity flag,
1527  // offset to quat 1,
1528  // offset to end.
1529  dafgs_c(sum);
1530  dafus_c(sum, (SpiceInt) 2, (SpiceInt) 6, dc, ic);
1531 
1532  // Don't read type 5 ck here
1533  if (ic[2] == 5) break;
1534 //
1535  // Check times for type 3 ck segment if spacecraft matches
1536  if (ic[0] == spCode && ic[2] == 3) {
1537  sct2e_c((int) spCode / 1000, dc[0], &segStartEt);
1538  sct2e_c((int) spCode / 1000, dc[1], &segStopEt);
1540  double et;
1541 
1542  // Get times for this segment
1543  if (currentTime >= segStartEt && currentTime <= segStopEt) {
1544 
1545  // Check for a gap in the time coverage by making sure the time span of the observation does not
1546  // cross a segment unless the next segment starts where the current one ends
1547  if (observationSpansToNextSegment && currentTime > segStartEt) {
1548  QString msg = "Observation crosses segment boundary--unable to interpolate pointing";
1550  }
1551  if (observEnd > segStopEt) {
1552  observationSpansToNextSegment = true;
1553  }
1554 
1555  // Extract necessary header parameters
1556  int dovelocity = ic[3];
1557  int end = ic[5];
1558  double val[2];
1559  dafgda_c(handle, end - 1, end, val);
1560 // int nints = (int) val[0];
1561  int ninstances = (int) val[1];
1562  int numvel = dovelocity * 3;
1563  int quatnoff = ic[4] + (4 + numvel) * ninstances - 1;
1564 // int nrdir = (int) (( ninstances - 1 ) / DIRSIZ); /* sclkdp directory records */
1565  int sclkdp1off = quatnoff + 1;
1566  int sclkdpnoff = sclkdp1off + ninstances - 1;
1567 // int start1off = sclkdpnoff + nrdir + 1;
1568 // int startnoff = start1off + nints - 1;
1569  int sclkSpCode = spCode / 1000;
1570 
1571  // Now get the times
1572  std::vector<double> sclkdp(ninstances);
1573  dafgda_c(handle, sclkdp1off, sclkdpnoff, (SpiceDouble *) &sclkdp[0]);
1574 
1575  int instance = 0;
1576  sct2e_c(sclkSpCode, sclkdp[0], &et);
1577 
1578  while (instance < (ninstances - 1) && et < currentTime) {
1579  instance++;
1580  sct2e_c(sclkSpCode, sclkdp[instance], &et);
1581  }
1582 
1583  if (instance > 0) instance--;
1584  sct2e_c(sclkSpCode, sclkdp[instance], &et);
1585 
1586  while (instance < (ninstances - 1) && et < observEnd) {
1587  p_cacheTime.push_back(et - p_timeBias);
1588  instance++;
1589  sct2e_c(sclkSpCode, sclkdp[instance], &et);
1590  }
1591  p_cacheTime.push_back(et - p_timeBias);
1592 
1593  if (!observationSpansToNextSegment) {
1594  timeLoaded = true;
1595  p_minimizeCache = Done;
1596  break;
1597  }
1598  else {
1599  currentTime = segStopEt;
1600  }
1601  }
1602  }
1603  dafcs_c(handle); // Continue search in daf last searched
1604  daffna_c(&found); // Find next forward array in current daf
1605  }
1606  }
1607  else if (count == 0 && p_source != Nadir && p_minimizeCache == Yes) {
1608  QString msg = "No camera kernels loaded...Unable to determine time cache to downsize";
1609  throw IException(IException::User, msg, _FILEINFO_);
1610  }
1611 
1612  // Load times according to cache size (body rotations) -- handle first round of type 5 ck case and multiple ck case --
1613  // Load a time for every line scan line and downsize later
1614  if (!timeLoaded) {
1615  double cacheSlope = 0.0;
1616  if (p_fullCacheSize > 1)
1617  cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) / (double)(p_fullCacheSize - 1);
1618  for (int i = 0; i < p_fullCacheSize; i++)
1619  p_cacheTime.push_back(p_fullCacheStartTime + (double) i * cacheSlope);
1620  if (p_source == Nadir) p_minimizeCache = No;
1621  }
1623  }
1624 
1628  std::vector<double> SpiceRotation::GetFullCacheTime() {
1629 
1630  // No time cache was initialized -- throw an error
1631  if (p_fullCacheSize < 1) {
1632  QString msg = "Time cache not available -- rerun spiceinit";
1633  throw IException(IException::User, msg, _FILEINFO_);
1634  }
1635 
1636  std::vector<double> fullCacheTime;
1637  double cacheSlope = 0.0;
1638  if (p_fullCacheSize > 1) cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) / (double)(p_fullCacheSize - 1);
1639 
1640  for (int i = 0; i < p_fullCacheSize; i++)
1641  fullCacheTime.push_back(p_fullCacheStartTime + (double) i * cacheSlope);
1642 
1643  return fullCacheTime;
1644  }
1645 
1648  void SpiceRotation::FrameTrace(double et) {
1650 // The code for this method was extracted from the Naif routine rotget written by N.J. Bachman & W.L. Taber (JPL)
1651  int center;
1652  NaifFrameType type;
1653  int typid;
1654  SpiceBoolean found;
1655  int frmidx; // Frame chain index for current frame
1656  SpiceInt nextFrame; // Naif frame code of next frame
1658  std::vector<int> frameCodes;
1659  std::vector<int> frameTypes;
1660  frameCodes.push_back(p_constantFrames[0]);
1661 
1662  while (frameCodes[frameCodes.size()-1] != J2000Code) {
1663  frmidx = frameCodes.size() - 1;
1664  // First get the frame type (Note:: we may also need to save center if we use dynamic frames)
1665  frinfo_c((SpiceInt) frameCodes[frmidx], (SpiceInt *) &center, (SpiceInt *) &type, (SpiceInt *) &typid, &found);
1666 
1667  if (!found) {
1668 
1669  if (p_source == Nadir) {
1670  frameTypes.push_back(0);
1671  break;
1672  }
1673 
1674  QString msg = "The frame" + toString((int) frameCodes[frmidx]) + " is not supported by Naif";
1676  }
1677 
1678  double matrix[3][3];
1679 
1680  // To get the next link in the frame chain, use the frame type
1681  if (type == INERTL || type == PCK) {
1682  nextFrame = J2000Code;
1683  }
1684  else if (type == CK) {
1685  ckfrot_((SpiceInt *) &typid, &et, (double *) matrix, &nextFrame, (logical *) &found);
1686 
1687  if (!found) {
1688 
1689  if (p_source == Nadir) {
1690  frameTypes.push_back(0);
1691  break;
1692  }
1693 
1694  QString msg = "The ck rotation from frame " + toString(frameCodes[frmidx]) + " can not be found"
1695  + " due to no pointing available at requested time or a problem with the frame";
1697  }
1698  }
1699  else if (type == TK) {
1700  tkfram_((SpiceInt *) &typid, (double *) matrix, &nextFrame, (logical *) &found);
1701  if (!found) {
1702  QString msg = "The tk rotation from frame " + toString(frameCodes[frmidx]) + " can not be found";
1704  }
1705  }
1706  else if (type == DYN) {
1707  //
1708  // Unlike the other frame classes, the dynamic frame evaluation
1709  // routine ZZDYNROT requires the input frame ID rather than the
1710  // dynamic frame class ID. ZZDYNROT also requires the center ID
1711  // we found via the FRINFO call.
1712 
1713  zzdynrot_((SpiceInt *) &typid, (SpiceInt *) &center, &et, (double *) matrix, &nextFrame);
1714  }
1715 
1716  else {
1717  QString msg = "The frame " + toString(frameCodes[frmidx]) +
1718  " has a type " + toString(type) + " not supported by your version of Naif Spicelib." +
1719  "You need to update.";
1721 
1722  }
1723  frameCodes.push_back(nextFrame);
1724  frameTypes.push_back(type);
1725  }
1726 
1727  if ((int) frameCodes.size() == 1 && p_source != Nadir) { // Must be Sky
1728  p_constantFrames.push_back(frameCodes[0]);
1729  p_timeFrames.push_back(frameCodes[0]);
1730  return;
1731  }
1732 
1733  int nConstants = 0;
1734  p_constantFrames.clear();
1735  while (frameTypes[nConstants] == TK && nConstants < (int) frameTypes.size()) nConstants++;
1736 
1737 
1738  for (int i = 0; i <= nConstants; i++) {
1739  p_constantFrames.push_back(frameCodes[i]);
1740  }
1741 
1742  if (p_source != Nadir) {
1743  for (int i = nConstants; i < (int) frameCodes.size(); i++) {
1744  p_timeFrames.push_back(frameCodes[i]);
1745  }
1746  }
1747  else {
1748  // Nadir rotation is from spacecraft to J2000
1749  p_timeFrames.push_back(frameCodes[nConstants]);
1750  p_timeFrames.push_back(J2000Code);
1751  }
1753  }
1754 
1755 
1759  std::vector<double> SpiceRotation::Matrix() {
1761  std::vector<double> TJ;
1762  TJ.resize(9);
1763  mxm_c((SpiceDouble *) &p_TC[0], (SpiceDouble *) &p_CJ[0], (SpiceDouble( *) [3]) &TJ[0]);
1765  return TJ;
1766  }
1767 
1773  std::vector<double> SpiceRotation::ConstantRotation() {
1775  std::vector<double> q;
1776  q.resize(4);
1777  m2q_c((SpiceDouble( *)[3]) &p_TC[0], &q[0]);
1779  return q;
1780  }
1781 
1787  std::vector<double> &SpiceRotation::ConstantMatrix() {
1788  return p_TC;
1789  }
1790 
1796  void SpiceRotation::SetConstantMatrix(std::vector<double> constantMatrix) {
1797  p_TC = constantMatrix;
1798  return;
1799  }
1800 
1806  std::vector<double> SpiceRotation::TimeBasedRotation() {
1807  std::vector<double> q;
1808  q.resize(4);
1809  m2q_c((SpiceDouble( *)[3]) &p_CJ[0], &q[0]);
1810  return q;
1811  }
1812 
1818  std::vector<double> &SpiceRotation::TimeBasedMatrix() {
1819  return p_CJ;
1820  }
1821 
1827  void SpiceRotation::SetTimeBasedMatrix(std::vector<double> timeBasedMatrix) {
1828  p_CJ = timeBasedMatrix;
1829  return;
1830  }
1831 
1836  FrameTrace(et);
1837  // Get constant rotation which applies in all cases
1838  int targetFrame = p_constantFrames[0];
1839  int fromFrame = p_timeFrames[0];
1840  p_TC.resize(9);
1841  refchg_((SpiceInt *) &fromFrame, (SpiceInt *) &targetFrame, &et, (doublereal *) &p_TC[0]);
1842  // Transpose to obtain row-major order
1843  xpose_c((SpiceDouble( *)[3]) &p_TC[0], (SpiceDouble( *)[3]) &p_TC[0]);
1844  }
1845 
1846 
1847 
1867 
1868  // Make sure the angles have been fit to polynomials
1869  if (p_source < PolyFunction) {
1870  QString msg = "The SpiceRotation pointing angles must be fit to polynomials in order to compute angular velocity";
1872  }
1873 
1874  std::vector<double> dCJdt;
1875  dCJdt.resize(9);
1876  DCJdt(dCJdt);
1877  double omega[3][3];
1878  mtxm_c((SpiceDouble( *)[3]) &dCJdt[0], (SpiceDouble( *)[3]) &p_CJ[0], omega);
1879  p_av[0] = omega[2][1];
1880  p_av[1] = omega[0][2];
1881  p_av[2] = omega[1][0];
1882  }
1883 
1884 
1893  void SpiceRotation::DCJdt(std::vector<double> &dCJ) {
1895 
1896  // Get the rotation angles and axes
1897  std::vector<double> angles = Angles(p_axis3, p_axis2, p_axis1);
1898  int axes[3] = {p_axis1, p_axis2, p_axis3};
1899 
1900  double dmatrix[3][3];
1901  double dangle;
1902  double wmatrix[3][3]; // work matrix
1903  dCJ.assign(9, 0.);
1904 
1905  for (int angleIndex = 0; angleIndex < 3; angleIndex++) {
1906  drotat_(&(angles[angleIndex]), (integer *) axes + angleIndex, (doublereal *) dmatrix);
1907  // Transpose to obtain row-major order
1908  xpose_c(dmatrix, dmatrix);
1909 
1910  // To get the derivative of the polynomial fit to the angle with respect to time
1911  // first create the function object for this angle and load its coefficients
1913  function.SetCoefficients(p_coefficients[angleIndex]);
1914 
1915  // Evaluate the derivative of function at p_et
1916  // dangle = function.DerivativeVar((p_et - p_baseTime) / p_timeScale);
1917  dangle = function.DerivativeVar((p_et - p_baseTime) / p_timeScale) / p_timeScale;
1918 
1919  // Multiply dangle to complete dmatrix
1920  for (int row = 0; row < 3; row++) {
1921  for (int col = 0; col < 3; col++) {
1922  dmatrix[row][col] *= dangle;
1923  }
1924  }
1925  // Apply the other 2 angles and chain them all together
1926  switch(angleIndex) {
1927  case 0:
1928  rotmat_c(dmatrix, angles[1], axes[1], dmatrix);
1929  rotmat_c(dmatrix, angles[2], axes[2], dmatrix);
1930  break;
1931  case 1:
1932  rotate_c(angles[0], axes[0], wmatrix);
1933  mxm_c(dmatrix, wmatrix, dmatrix);
1934  rotmat_c(dmatrix, angles[2], axes[2], dmatrix);
1935  break;
1936  case 2:
1937  rotate_c(angles[0], axes[0], wmatrix);
1938  rotmat_c(wmatrix, angles[1], axes[1], wmatrix);
1939  mxm_c(dmatrix, wmatrix, dmatrix);
1940  break;
1941  }
1942  int i, j;
1943  for (int index = 0; index < 9; index++) {
1944  i = index / 3;
1945  j = index % 3;
1946  dCJ[index] += dmatrix[i][j];
1947  }
1948  }
1949 
1951  }
1952 
1953 
1956  std::vector<double> SpiceRotation::StateTJ() {
1957  std::vector<double> stateTJ(36);
1958 
1959  // Build the state matrix for the time-based rotation from the matrix and angulary velocity
1960  double stateCJ[6][6];
1961  rav2xf_c(&p_CJ[0], &p_av[0], stateCJ);
1962 // (SpiceDouble (*) [3]) &p_CJ[0]
1963  int irow = 0;
1964  int jcol = 0;
1965  int vpos = 0;
1966 
1967  for (int row = 3; row < 6; row++) {
1968  irow = row - 3;
1969  vpos = irow * 3;
1970 
1971  for (int col = 0; col < 3; col++) {
1972  jcol = col + 3;
1973  // Fill the upper left corner
1974  stateTJ[irow*6 + col] = p_TC[vpos] * stateCJ[0][col] + p_TC[vpos+1] * stateCJ[1][col] + p_TC[vpos+2] * stateCJ[2][col];
1975  // Fill the lower left corner
1976  stateTJ[row*6 + col] = p_TC[vpos] * stateCJ[3][col] + p_TC[vpos+1] * stateCJ[4][col] + p_TC[vpos+2] * stateCJ[5][col];
1977  // Fill the upper right corner
1978  stateTJ[irow*6 + jcol] = 0;
1979  // Fill the lower right corner
1980  stateTJ[row*6 +jcol] = stateTJ[irow*6 + col];
1981  }
1982  }
1983  return stateTJ;
1984  }
1985 
1986 
1996  std::vector<double> SpiceRotation::Extrapolate(double timeEt) {
1998 
1999  if (!p_hasAngularVelocity) return p_CJ;
2000 
2001  double diffTime = timeEt - p_et;
2002  std::vector<double> CJ(9, 0.0);
2003  double dmat[3][3];
2004 
2005  // Create a rotation matrix for the axis and magnitude of the angular velocity * the time difference
2006  axisar_c((SpiceDouble *) &p_av[0], diffTime*vnorm_c((SpiceDouble *) &p_av[0]), dmat);
2007 
2008  // Rotate from the current time to the desired time assuming constant angular velocity
2009  mxm_c(dmat, (SpiceDouble *) &p_CJ[0], (SpiceDouble( *)[3]) &CJ[0]);
2010  return CJ;
2011  }
2012 
2013 
2021  void SpiceRotation::SetFullCacheParameters(double startTime, double endTime, int cacheSize) {
2022  // Save full cache parameters
2023  p_fullCacheStartTime = startTime;
2024  p_fullCacheEndTime = endTime;
2025  p_fullCacheSize = cacheSize;
2026  }
2027 
2028  void SpiceRotation::SetEphemerisTimeMemcache() {
2029  // If the cache has only one rotation, set it
2030 
2031  if (p_cache.size() == 1) {
2032  /* p_quaternion = p_cache[0];*/
2033  p_CJ = p_cache[0];
2034 // p_CJ = p_quaternion.ToMatrix();
2035 
2036  if (p_hasAngularVelocity) {
2037  p_av = p_cacheAv[0];
2038  }
2039 
2040  }
2041 
2042  // Otherwise determine the interval to interpolate
2043  else {
2044  std::vector<double>::iterator pos;
2045  pos = upper_bound(p_cacheTime.begin(), p_cacheTime.end(), p_et);
2046 
2047  int cacheIndex;
2048 
2049  if (pos != p_cacheTime.end()) {
2050  cacheIndex = distance(p_cacheTime.begin(), pos);
2051  cacheIndex--;
2052  }
2053  else {
2054  cacheIndex = p_cacheTime.size() - 2;
2055  }
2056 
2057  if (cacheIndex < 0) cacheIndex = 0;
2058 
2059  // Interpolate the rotation
2060  double mult = (p_et - p_cacheTime[cacheIndex]) /
2061  (p_cacheTime[cacheIndex+1] - p_cacheTime[cacheIndex]);
2062  /* Quaternion Q2 (p_cache[cacheIndex+1]);
2063  Quaternion Q1 (p_cache[cacheIndex]);*/
2064  std::vector<double> CJ2(p_cache[cacheIndex+1]);
2065  std::vector<double> CJ1(p_cache[cacheIndex]);
2066  SpiceDouble J2J1[3][3];
2067  mtxm_c((SpiceDouble( *)[3]) &CJ2[0], (SpiceDouble( *)[3]) &CJ1[0], J2J1);
2068  SpiceDouble axis[3];
2069  SpiceDouble angle;
2070  raxisa_c(J2J1, axis, &angle);
2071  SpiceDouble delta[3][3];
2072  axisar_c(axis, angle * (SpiceDouble)mult, delta);
2073  mxmt_c((SpiceDouble *) &CJ1[0], delta, (SpiceDouble( *) [3]) &p_CJ[0]);
2074 
2075  if (p_hasAngularVelocity) {
2076  double v1[3], v2[3]; // Vectors surrounding desired time
2077  vequ_c((SpiceDouble *) &p_cacheAv[cacheIndex][0], v1);
2078  vequ_c((SpiceDouble *) &p_cacheAv[cacheIndex+1][0], v2);
2079  vscl_c((1. - mult), v1, v1);
2080  vscl_c(mult, v2, v2);
2081  vadd_c(v1, v2, (SpiceDouble *) &p_av[0]);
2082  }
2083  }
2084  }
2085 
2086 
2087  void SpiceRotation::SetEphemerisTimeNadir() {
2088  // TODO what about spk time bias and mission setting of light time corrections
2089  // That information has only been passed to the SpicePosition class and
2090  // is not available to this class, but probably should be applied to the
2091  // spkez call.
2092 
2093  // Make sure the constant frame is loaded. This method also does the frame trace.
2094  if (p_timeFrames.size() == 0) InitConstantRotation(p_et);
2095 
2096  SpiceDouble stateJ[6]; // Position and velocity vector in J2000
2097  SpiceDouble lt;
2098  SpiceInt spkCode = p_constantFrames[0] / 1000;
2099  spkez_c((SpiceInt) spkCode, p_et, "J2000", "LT+S",
2100  (SpiceInt) p_targetCode, stateJ, &lt);
2101  // reverse the position to be relative to the spacecraft. This may be
2102  // a mission dependent value and possibly the sense of the velocity as well.
2103  SpiceDouble sJ[3], svJ[3];
2104  vpack_c(-1 * stateJ[0], -1 * stateJ[1], -1 * stateJ[2], sJ);
2105  vpack_c(stateJ[3], stateJ[4], stateJ[5], svJ);
2106  twovec_c(sJ,
2107  p_axisP,
2108  svJ,
2109  p_axisV,
2110  (SpiceDouble( *)[3]) &p_CJ[0]);
2111  }
2112 
2113 
2114  void SpiceRotation::SetEphemerisTimeSpice() {
2115  SpiceInt j2000 = J2000Code;
2116 
2117  SpiceDouble time = p_et + p_timeBias;
2118 
2119  // Make sure the constant frame is loaded. This method also does the frame trace.
2120  if (p_timeFrames.size() == 0) InitConstantRotation(p_et);
2121  int toFrame = p_timeFrames[0];
2122 
2123  // First try getting the entire state matrix (6x6), which includes CJ and the angular velocity
2124  double stateCJ[6][6];
2125  frmchg_((integer *) &j2000, (integer *) &toFrame, &time, (doublereal *) stateCJ);
2126 
2127  // If Naif fails attempting to get the state matrix, assume the angular velocity vector is
2128  // not available and just get the rotation matrix. First turn off Naif error reporting and
2129  // return any error without printing them.
2130  SpiceBoolean ckfailure = failed_c();
2131  reset_c(); // Reset Naif error system to allow caller to recover
2132 
2133  if (!ckfailure) {
2134  xpose6_c(stateCJ, stateCJ);
2135  xf2rav_c(stateCJ, (SpiceDouble( *)[3]) &p_CJ[0], (SpiceDouble *) &p_av[0]);
2136  p_hasAngularVelocity = true;
2137  }
2138  else {
2139  refchg_((integer *) &j2000, (integer *) &toFrame, &time, (SpiceDouble *) &p_CJ[0]);
2140 
2141  if (failed_c()) {
2142  char naifstr[64];
2143  getmsg_c("SHORT", sizeof(naifstr), naifstr);
2144  reset_c(); // Reset Naif error system to allow caller to recover
2145 
2146  if (eqstr_c(naifstr, "SPICE(UNKNOWNFRAME)")) {
2147  Isis::IString msg = Isis::IString((int) p_constantFrames[0]) + " is an unrecognized " +
2148  "reference frame code. Has the mission frames kernel been loaded?";
2149  throw IException(IException::Io, msg, _FILEINFO_);
2150  }
2151  else {
2152  Isis::IString msg = "No pointing available at requested time [" +
2153  Isis::IString(p_et + p_timeBias) + "] for frame code [" +
2154  Isis::IString((int) p_constantFrames[0]) + "]";
2155  throw IException(IException::Io, msg, _FILEINFO_);
2156  }
2157  }
2158 
2159  // Transpose to obtain row-major order
2160  xpose_c((SpiceDouble( *)[3]) &p_CJ[0], (SpiceDouble( *)[3]) &p_CJ[0]);
2161  }
2162  }
2163 
2164 
2165  std::vector<double> SpiceRotation::EvaluatePolyFunction() {
2169 
2170  // Load the functions with the coefficients
2171  function1.SetCoefficients(p_coefficients[0]);
2172  function2.SetCoefficients(p_coefficients[1]);
2173  function3.SetCoefficients(p_coefficients[2]);
2174 
2175  std::vector<double> rtime;
2176  rtime.push_back((p_et - p_baseTime) / p_timeScale);
2177  std::vector<double> angles;
2178  angles.push_back(function1.Evaluate(rtime));
2179  angles.push_back(function2.Evaluate(rtime));
2180  angles.push_back(function3.Evaluate(rtime));
2181 
2182  // Get the first angle back into the range Naif expects [-180.,180.]
2183  if (angles[0] < -1 * pi_c()) {
2184  angles[0] += twopi_c();
2185  }
2186  else if (angles[0] > pi_c()) {
2187  angles[0] -= twopi_c();
2188  }
2189  return angles;
2190  }
2191 
2192 
2193  void SpiceRotation::SetEphemerisTimePolyFunction() {
2197 
2198  // Load the functions with the coefficients
2199  function1.SetCoefficients(p_coefficients[0]);
2200  function2.SetCoefficients(p_coefficients[1]);
2201  function3.SetCoefficients(p_coefficients[2]);
2202 
2203  std::vector<double> rtime;
2204  rtime.push_back((p_et - p_baseTime) / p_timeScale);
2205  double angle1 = function1.Evaluate(rtime);
2206  double angle2 = function2.Evaluate(rtime);
2207  double angle3 = function3.Evaluate(rtime);
2208 
2209  // Get the first angle back into the range Naif expects [-180.,180.]
2210  if (angle1 < -1 * pi_c()) {
2211  angle1 += twopi_c();
2212  }
2213  else if (angle1 > pi_c()) {
2214  angle1 -= twopi_c();
2215  }
2216 
2217  eul2m_c((SpiceDouble) angle3, (SpiceDouble) angle2, (SpiceDouble) angle1,
2219  (SpiceDouble( *)[3]) &p_CJ[0]);
2220 
2221  if (p_hasAngularVelocity) {
2222  if ( p_degree == 0)
2223  p_av = p_cacheAv[0];
2224  else
2225  ComputeAv();
2226  }
2227  }
2228 
2229 
2230  void SpiceRotation::SetEphemerisTimePolyFunctionOverSpice() {
2231  SetEphemerisTimeMemcache();
2232  std::vector<double> cacheAngles(3);
2233  std::vector<double> cacheVelocity(3);
2234  cacheAngles = Angles(p_axis3, p_axis2, p_axis1);
2235  cacheVelocity = p_av;
2236  SetEphemerisTimePolyFunction();
2237  std::vector<double> polyAngles(3);
2238  // The decomposition fails because the angles are outside the valid range for Naif
2239  // polyAngles = Angles(p_axis3, p_axis2, p_axis1);
2240  polyAngles = EvaluatePolyFunction();
2241  std::vector<double> polyVelocity(3);
2242  polyVelocity = p_av;
2243  std::vector<double> angles(3);
2244 
2245  for (int index = 0; index < 3; index++) {
2246  angles[index] = cacheAngles[index] + polyAngles[index];
2247  p_av[index] += cacheVelocity[index];
2248  }
2249 
2250  if (angles[0] < -1 * pi_c()) {
2251  angles[0] += twopi_c();
2252  }
2253  else if (angles[0] > pi_c()) {
2254  angles[0] -= twopi_c();
2255  }
2256 
2257  if (angles[2] < -1 * pi_c()) {
2258  angles[2] += twopi_c();
2259  }
2260  else if (angles[2] > pi_c()) {
2261  angles[2] -= twopi_c();
2262  }
2263 
2264  eul2m_c((SpiceDouble) angles[2], (SpiceDouble) angles[1], (SpiceDouble) angles[0],
2266  (SpiceDouble( *)[3]) &p_CJ[0]);
2267  }
2268 
2269 
2270 }