USGS

Isis 3.0 Object Programmers' Reference

Home

SpicePosition.cpp
1 #include "SpicePosition.h"
2 
3 #include <algorithm>
4 #include <cfloat>
5 #include <iomanip>
6 
7 #include <QString>
8 #include <QStringList>
9 #include <QChar>
10 
11 #include "BasisFunction.h"
12 #include "IException.h"
13 #include "LeastSquares.h"
14 #include "LineEquation.h"
15 #include "NaifStatus.h"
16 #include "NumericalApproximation.h"
17 #include "PolynomialUnivariate.h"
18 #include "TableField.h"
19 
20 namespace Isis {
29  SpicePosition::SpicePosition(int targetCode, int observerCode) {
30  init(targetCode, observerCode, false);
31  }
32 
62  SpicePosition::SpicePosition(int targetCode, int observerCode,
63  bool swapObserverTarget) {
64  init(targetCode, observerCode, swapObserverTarget);
65  }
66 
79  void SpicePosition::init(int targetCode, int observerCode,
80  const bool &swapObserverTarget) {
81 
82  p_aberrationCorrection = "LT+S";
83  p_baseTime = 0.;
84  p_coefficients[0].clear();
85  p_coefficients[1].clear();
86  p_coefficients[2].clear();
87  p_coordinate.resize(3);
88  p_degree = 2;
89  p_degreeApplied = false;
90  p_et = -DBL_MAX;
93  p_fullCacheSize = 0;
94  p_hasVelocity = false;
95 
96  p_override = NoOverrides;
97  p_source = Spice;
98 
99  p_timeBias = 0.0;
100  p_timeScale = 1.;
101  p_velocity.resize(3);
102  p_xhermite = NULL;
103  p_yhermite = NULL;
104  p_zhermite = NULL;
105 
106  m_swapObserverTarget = swapObserverTarget;
107  m_lt = 0.0;
108 
109  // Determine observer/target ordering
110  if ( m_swapObserverTarget ) {
111  // New/improved settings.. Results in vector negation in SetEphemerisTimeSpice
112  p_targetCode = observerCode;
113  p_observerCode = targetCode;
114  }
115  else {
116  // Traditional settings
117  p_targetCode = targetCode;
118  p_observerCode = observerCode;
119  }
120  }
121 
122 
127  ClearCache();
128  }
129 
130 
143  void SpicePosition::SetTimeBias(double timeBias) {
144  p_timeBias = timeBias;
145  }
146 
154  double SpicePosition::GetTimeBias() const {
155  return (p_timeBias);
156  }
157 
179  void SpicePosition::SetAberrationCorrection(const QString &correction) {
180  QString abcorr(correction);
181  abcorr.remove(QChar(' '));
182  abcorr = abcorr.toUpper();
183  QStringList validAbcorr;
184  validAbcorr << "NONE" << "LT" << "LT+S" << "CN" << "CN+S"
185  << "XLT" << "XLT+S" << "XCN" << "XCN+S";
186 
187  if (validAbcorr.indexOf(abcorr) >= 0) {
188  p_aberrationCorrection = abcorr;
189  }
190  else {
191  QString msg = "Invalid abberation correction [" + correction + "]";
193  }
194  }
195 
207  return (p_aberrationCorrection);
208  }
209 
224  return (m_lt);
225  }
226 
242  const std::vector<double> &SpicePosition::SetEphemerisTime(double et) {
244 
245  // Save the time
246  if(et == p_et) return p_coordinate;
247  p_et = et;
248 
249  // Read from the cache
250  if(p_source == Memcache) {
252  }
253  else if(p_source == HermiteCache) {
255  }
256  else if(p_source == PolyFunction) {
258  }
261  }
262  else { // Read from the kernel
264  }
265 
267 
268  // Return the coordinate
269  return p_coordinate;
270  }
271 
272 
287  void SpicePosition::LoadCache(double startTime, double endTime, int size) {
288  // Make sure cache isn't already loaded
289  if(p_source == Memcache || p_source == HermiteCache) {
290  QString msg = "A SpicePosition cache has already been created";
292  }
293 
294  if(startTime > endTime) {
295  QString msg = "Argument startTime must be less than or equal to endTime";
297  }
298 
299  if((startTime != endTime) && (size == 1)) {
300  QString msg = "Cache size must be more than 1 if startTime endTime differ";
302  }
303 
304  // Save full cache parameters
305  p_fullCacheStartTime = startTime;
306  p_fullCacheEndTime = endTime;
307  p_fullCacheSize = size;
308  LoadTimeCache();
309 
310  // Loop and load the cache
311  for(int i = 0; i < size; i++) {
312  double et = p_cacheTime[i];
313  SetEphemerisTime(et);
314  p_cache.push_back(p_coordinate);
316  }
317 
318  p_source = Memcache;
319  }
320 
321 
335  void SpicePosition::LoadCache(double time) {
336  LoadCache(time, time, 1);
337  }
338 
339 
363 
364  // Make sure cache isn't alread loaded
365  if(p_source == Memcache || p_source == HermiteCache) {
366  QString msg = "A SpicePosition cache has already been created";
368  }
369 
370  // Load the full cache time information from the label if available
371  if(table.Label().hasKeyword("SpkTableStartTime")) {
372  p_fullCacheStartTime = toDouble(table.Label().findKeyword("SpkTableStartTime")[0]);
373  }
374  if(table.Label().hasKeyword("SpkTableEndTime")) {
375  p_fullCacheEndTime = toDouble(table.Label().findKeyword("SpkTableEndTime")[0]);
376  }
377  if(table.Label().hasKeyword("SpkTableOriginalSize")) {
378  p_fullCacheSize = toDouble(table.Label().findKeyword("SpkTableOriginalSize")[0]);
379  }
380 
381 
382  // set source type by table's label keyword
383  if(!table.Label().hasKeyword("CacheType")) {
384  p_source = Memcache;
385  }
386  else if(table.Label().findKeyword("CacheType")[0] == "Linear") {
387  p_source = Memcache;
388  }
389  else if(table.Label().findKeyword("CacheType")[0] == "HermiteSpline") {
391  p_overrideTimeScale = 1.;
392  p_override = ScaleOnly;
393  }
394  else if(table.Label().findKeyword("CacheType")[0] == "PolyFunction") {
396  }
397  else {
399  "Invalid value for CacheType keyword in the table "
400  + table.Name(),
401  _FILEINFO_);
402  }
403 
404  // Loop through and move the table to the cache
405  if (p_source != PolyFunction) {
406  for (int r = 0; r < table.Records(); r++) {
407  TableRecord &rec = table[r];
408  if (rec.Fields() == 7) {
409  p_hasVelocity = true;
410  }
411  else if (rec.Fields() == 4) {
412  p_hasVelocity = false;
413  }
414  else {
415  QString msg = "Expecting four or seven fields in the SpicePosition table";
417  }
418 
419  std::vector<double> j2000Coord;
420  j2000Coord.push_back((double)rec[0]);
421  j2000Coord.push_back((double)rec[1]);
422  j2000Coord.push_back((double)rec[2]);
423  int inext = 3;
424 
425  p_cache.push_back(j2000Coord);
426  if (p_hasVelocity) {
427  std::vector<double> j2000Velocity;
428  j2000Velocity.push_back((double)rec[3]);
429  j2000Velocity.push_back((double)rec[4]);
430  j2000Velocity.push_back((double)rec[5]);
431  inext = 6;
432 
433  p_cacheVelocity.push_back(j2000Velocity);
434  }
435  p_cacheTime.push_back((double)rec[inext]);
436  }
437  }
438  else {
439  // Coefficient table for postion coordinates x, y, and z
440  std::vector<double> coeffX, coeffY, coeffZ;
441 
442  for (int r = 0; r < table.Records() - 1; r++) {
443  TableRecord &rec = table[r];
444 
445  if(rec.Fields() != 3) {
446  // throw an error
447  }
448  coeffX.push_back((double)rec[0]);
449  coeffY.push_back((double)rec[1]);
450  coeffZ.push_back((double)rec[2]);
451  }
452  // Take care of function time parameters
453  TableRecord &rec = table[table.Records()-1];
454  double baseTime = (double)rec[0];
455  double timeScale = (double)rec[1];
456  double degree = (double)rec[2];
457  SetPolynomialDegree((int) degree);
458  SetOverrideBaseTime(baseTime, timeScale);
459  SetPolynomial(coeffX, coeffY, coeffZ);
460  if (degree > 0) p_hasVelocity = true;
461  if(degree == 0 && p_cacheVelocity.size() > 0) p_hasVelocity = true;
462  }
463  }
464 
465 
479  Table SpicePosition::Cache(const QString &tableName) {
481  LineCache(tableName);
482  // TODO Figure out how to get the tolerance -- for now hard code .01
483  Memcache2HermiteCache(0.01);
484 
485  //std::cout << "Cache size is " << p_cache.size();
486 
487  }
488 
489  // record to be added to table
490  TableRecord record;
491 
492  if (p_source != PolyFunction) {
493  // add x,y,z position labels to record
494  TableField x("J2000X", TableField::Double);
495  TableField y("J2000Y", TableField::Double);
496  TableField z("J2000Z", TableField::Double);
497  record += x;
498  record += y;
499  record += z;
500 
501  if (p_hasVelocity) {
502  // add x,y,z velocity labels to record
503  TableField vx("J2000XV", TableField::Double);
504  TableField vy("J2000YV", TableField::Double);
505  TableField vz("J2000ZV", TableField::Double);
506  record += vx;
507  record += vy;
508  record += vz;
509  }
510  // add time label to record
512  record += t;
513 
514  // create output table
515  Table table(tableName, record);
516 
517  int inext = 0;
518 
519  for (int i = 0; i < (int)p_cache.size(); i++) {
520  record[inext++] = p_cache[i][0]; // record[0]
521  record[inext++] = p_cache[i][1]; // record[1]
522  record[inext++] = p_cache[i][2]; // record[2]
523  if (p_hasVelocity) {
524  record[inext++] = p_cacheVelocity[i][0]; // record[3]
525  record[inext++] = p_cacheVelocity[i][1]; // record[4]
526  record[inext++] = p_cacheVelocity[i][2]; // record[5]
527  }
528  record[inext] = p_cacheTime[i]; // record[6]
529  table += record;
530 
531  inext = 0;
532  }
533  CacheLabel(table);
534  return table;
535  }
536 
537  else if(p_source == PolyFunction && p_degree == 0 && p_fullCacheSize == 1)
538  // Just load the position for the single epoch
539  return LineCache(tableName);
540 
541  // Load the coefficients for the curves fit to the 3 camera angles
542  else if (p_source == PolyFunction) {
543  // PolyFunction case
544  TableField spacecraftX("J2000SVX", TableField::Double);
545  TableField spacecraftY("J2000SVY", TableField::Double);
546  TableField spacecraftZ("J2000SVZ", TableField::Double);
547 
548  TableRecord record;
549  record += spacecraftX;
550  record += spacecraftY;
551  record += spacecraftZ;
552 
553  Table table(tableName, record);
554 
555  for(int cindex = 0; cindex < p_degree + 1; cindex++) {
556  record[0] = p_coefficients[0][cindex];
557  record[1] = p_coefficients[1][cindex];
558  record[2] = p_coefficients[2][cindex];
559  table += record;
560  }
561 
562  // Load one more table entry with the time adjustments for the fit equation
563  // t = (et - baseTime)/ timeScale
564  record[0] = p_baseTime;
565  record[1] = p_timeScale;
566  record[2] = (double) p_degree;
567 
568  CacheLabel(table);
569  table += record;
570  return table;
571  }
572 
573  else {
575  "Cannot create Table, no Cache is loaded.",
576  _FILEINFO_);
577  }
578 
579  }
580 
581 
582 
590  // determine type of table to return
591  QString tabletype = "";
592 
593  if(p_source == Memcache) {
594  tabletype = "Linear";
595  }
596  else if(p_source == HermiteCache) {
597  tabletype = "HermiteSpline";
598  }
599  else {
600  tabletype = "PolyFunction";
601  }
602 
603  table.Label() += PvlKeyword("CacheType", tabletype);
604 
605  // Write original time coverage
606  if(p_fullCacheStartTime != 0) {
607  table.Label() += PvlKeyword("SpkTableStartTime");
608  table.Label()["SpkTableStartTime"].addValue(toString(p_fullCacheStartTime));
609  }
610  if(p_fullCacheEndTime != 0) {
611  table.Label() += PvlKeyword("SpkTableEndTime");
612  table.Label()["SpkTableEndTime"].addValue(toString(p_fullCacheEndTime));
613  }
614  if(p_fullCacheSize != 0) {
615  table.Label() += PvlKeyword("SpkTableOriginalSize");
616  table.Label()["SpkTableOriginalSize"].addValue(toString(p_fullCacheSize));
617  }
618  }
619 
620 
621 
635  Table SpicePosition::LineCache(const QString &tableName) {
636 
637  // Apply the function and fill the caches
639 
640  if(p_source != Memcache) {
641  QString msg = "Only cached positions can be returned as a line cache of positions and time";
643  }
644  // Load the table and return it to caller
645  return Cache(tableName);
646  }
647 
648 
649 
662 
663  // Save current et
664  double et = p_et;
665 
666  // Make sure source is a function
667  if(p_source < HermiteCache) {
668  QString msg = "The SpicePosition has not yet been fit to a function";
670  }
671 
672  // Clear existing positions from thecache
673  p_cacheTime.clear();
674  p_cache.clear();
675 
676  // Clear the velocity cache if we can calculate it instead. It can't be calculated for
677  // functions of degree 0 (framing cameras), so keep the original velocity. It is better than nothing.
678  if (p_degree > 0 && p_cacheVelocity.size() > 1) p_cacheVelocity.clear();
679 
680  // Load the time cache first
681  LoadTimeCache();
682 
683  if (p_fullCacheSize > 1) {
684  // Load the positions and velocity caches
685  p_et = -DBL_MAX; // Forces recalculation in SetEphemerisTime
686 
687  for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
688  // p_et = p_cacheTime.at(pos);
689  SetEphemerisTime(p_cacheTime.at(pos));
690  p_cache.push_back(p_coordinate);
691  p_cacheVelocity.push_back(p_velocity);
692  }
693  }
694  else {
695  // Load the position for the single updated time instance
696  p_et = p_cacheTime[0];
698  p_cache.push_back(p_coordinate);
699  }
700 
701  // Set source to cache and reset current et
702  p_source = Memcache;
703  p_et = -DBL_MAX;
704  SetEphemerisTime(et);
705 
707  }
708 
709 
720  Table SpicePosition::LoadHermiteCache(const QString &tableName) {
721  // find the first and last time values
722  double firstTime = p_fullCacheStartTime;
723  double lastTime = p_fullCacheEndTime;
724  int cacheTimeSize = (int) p_fullCacheSize;
725 
726  // Framing cameras are already cached and don't need to be reduced.
727  if(cacheTimeSize == 1) return Cache(tableName);
728 
729  // Load the polynomial functions
733  function1.SetCoefficients(p_coefficients[0]);
734  function2.SetCoefficients(p_coefficients[1]);
735  function3.SetCoefficients(p_coefficients[2]);
736 
737  // Make sure a polynomial function is already loaded
738  if(p_source != PolyFunction) {
739  QString msg = "A SpicePosition polynomial function has not been created yet";
741  }
742 
743  // Clear existing coordinates from cache
744  ClearCache();
745 
746  // Set velocity vector to true since it is calculated
747  p_hasVelocity = true;
748 
749 // std::cout <<"time cache size is " << p_cacheTime.size() << std::endl;
750 
751  // Calculate new postition coordinates from polynomials fit to coordinates &
752  // fill cache
753 // std::cout << "Before" << std::endl;
754 // double savetime = p_cacheTime.at(0);
755 // SetEphemerisTime(savetime);
756 // std::cout << " at time " << savetime << std::endl;
757 // for (int i=0; i<3; i++) {
758 // std::cout << p_coordinate[i] << std::endl;
759 // }
760 
761 
762  // find time for the extremum of each polynomial
763  // since this is only a 2nd degree polynomial,
764  // finding these extrema is simple
765  double b1 = function1.Coefficient(1);
766  double c1 = function1.Coefficient(2);
767  double extremumXtime = -b1 / (2.*c1) + p_baseTime; // extremum is time value for root of 1st derivative
768  // make sure we are in the domain
769  if(extremumXtime < firstTime) {
770  extremumXtime = firstTime;
771  }
772  if(extremumXtime > lastTime) {
773  extremumXtime = lastTime;
774  }
775 
776  double b2 = function2.Coefficient(1);
777  double c2 = function2.Coefficient(2);
778  double extremumYtime = -b2 / (2.*c2) + p_baseTime;
779  // make sure we are in the domain
780  if(extremumYtime < firstTime) {
781  extremumYtime = firstTime;
782  }
783  if(extremumYtime > lastTime) {
784  extremumYtime = lastTime;
785  }
786 
787  double b3 = function3.Coefficient(1);
788  double c3 = function3.Coefficient(2);
789  double extremumZtime = -b3 / (2.*c3) + p_baseTime;
790  // make sure we are in the domain
791  if(extremumZtime < firstTime) {
792  extremumZtime = firstTime;
793  }
794  if(extremumZtime > lastTime) {
795  extremumZtime = lastTime;
796  }
797 
798  // refill the time vector
799  p_cacheTime.clear();
800  p_cacheTime.push_back(firstTime);
801  p_cacheTime.push_back(extremumXtime);
802  p_cacheTime.push_back(extremumYtime);
803  p_cacheTime.push_back(extremumZtime);
804  p_cacheTime.push_back(lastTime);
805  // we don't know order of extrema, so sort
806  sort(p_cacheTime.begin(), p_cacheTime.end());
807  // in case an extremum is an endpoint
808  std::vector <double>::iterator it = unique(p_cacheTime.begin(), p_cacheTime.end());
809  p_cacheTime.resize(it - p_cacheTime.begin());
810 
811  if(p_cacheTime.size() == 2) {
812  p_cacheTime.clear();
813  p_cacheTime.push_back(firstTime);
814  p_cacheTime.push_back((firstTime + lastTime) / 2);
815  p_cacheTime.push_back(lastTime);
816  }
817 
818  // add positions and velocities for these times
819  for(int i = 0; i < (int) p_cacheTime.size(); i++) {
820  // x,y,z positions
821  std::vector <double> time;
822  time.push_back(p_cacheTime[i] - p_baseTime);
823  p_coordinate[0] = function1.Evaluate(time);
824  p_coordinate[1] = function2.Evaluate(time);
825  p_coordinate[2] = function3.Evaluate(time);
826  p_cache.push_back(p_coordinate);
827 
828  // x,y,z velocities
829  p_velocity[0] = b1 + 2 * c1 * (p_cacheTime[i] - p_baseTime);
830  p_velocity[1] = b2 + 2 * c2 * (p_cacheTime[i] - p_baseTime);
831  p_velocity[2] = b3 + 2 * c3 * (p_cacheTime[i] - p_baseTime);
832  p_cacheVelocity.push_back(p_velocity);
833  }
834 
836  double et = p_et;
837  p_et = -DBL_MAX;
838  SetEphemerisTime(et);
839 
840  return Cache(tableName);
841  }
842 
843 
851  std::vector<double> XC, YC, ZC;
852 
853  // Check to see if the position is already a Polynomial Function
854  if (p_source == PolyFunction)
855  return;
856 
857  // Adjust the degree of the polynomial to the available data
858  if (p_cache.size() == 1) {
859  p_degree = 0;
860  }
861  else if (p_cache.size() == 2) {
862  p_degree = 1;
863  }
864 
865  //Check for polynomial over Hermite constant and initialize coefficients
866  if (type == PolyFunctionOverHermiteConstant) {
867  XC.assign(p_degree + 1, 0.);
868  YC.assign(p_degree + 1, 0.);
869  ZC.assign(p_degree + 1, 0.);
870  SetPolynomial(XC, YC, ZC, type);
871  return;
872  }
873 
877 
878  // Compute the base time
879  ComputeBaseTime();
880  std::vector<double> time;
881 
882  if(p_cache.size() == 1) {
883  double t = p_cacheTime.at(0);
884  SetEphemerisTime(t);
885  XC.push_back(p_coordinate[0]);
886  YC.push_back(p_coordinate[1]);
887  ZC.push_back(p_coordinate[2]);
888  }
889  else if(p_cache.size() == 2) {
890 // Load the times and get the corresponding coordinates
891  double t1 = p_cacheTime.at(0);
892  SetEphemerisTime(t1);
893  std::vector<double> coord1 = p_coordinate;
894  t1 = (t1 - p_baseTime) / p_timeScale;
895  double t2 = p_cacheTime.at(1);
896  SetEphemerisTime(t2);
897  std::vector<double> coord2 = p_coordinate;
898  t2 = (t2 - p_baseTime) / p_timeScale;
899  double slope[3];
900  double intercept[3];
901 
902 // Compute the linear equation for each coordinate and save them
903  for(int cIndex = 0; cIndex < 3; cIndex++) {
904  Isis::LineEquation posline(t1, coord1[cIndex], t2, coord2[cIndex]);
905  slope[cIndex] = posline.Slope();
906  intercept[cIndex] = posline.Intercept();
907  }
908  XC.push_back(intercept[0]);
909  XC.push_back(slope[0]);
910  YC.push_back(intercept[1]);
911  YC.push_back(slope[1]);
912  ZC.push_back(intercept[2]);
913  ZC.push_back(slope[2]);
914  }
915  else {
916  LeastSquares *fitX = new LeastSquares(function1);
917  LeastSquares *fitY = new LeastSquares(function2);
918  LeastSquares *fitZ = new LeastSquares(function3);
919 
920  // Load the known values to compute the fit equation
921  for(std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
922  double t = p_cacheTime.at(pos);
923  time.push_back( (t - p_baseTime) / p_timeScale);
924  SetEphemerisTime(t);
925  std::vector<double> coord = p_coordinate;
926 
927  fitX->AddKnown(time, coord[0]);
928  fitY->AddKnown(time, coord[1]);
929  fitZ->AddKnown(time, coord[2]);
930  time.clear();
931  }
932  //Solve the equations for the coefficients
933  fitX->Solve();
934  fitY->Solve();
935  fitZ->Solve();
936 
937  // Delete the least squares objects now that we have all the coefficients
938  delete fitX;
939  delete fitY;
940  delete fitZ;
941 
942  // For now assume all three coordinates are fit to a polynomial function.
943  // Later they may each be fit to a unique basis function.
944  // Fill the coefficient vectors
945 
946  for(int i = 0; i < function1.Coefficients(); i++) {
947  XC.push_back(function1.Coefficient(i));
948  YC.push_back(function2.Coefficient(i));
949  ZC.push_back(function3.Coefficient(i));
950  }
951 
952  }
953 
954  // Now that the coefficients have been calculated set the polynomial with them
955  SetPolynomial(XC, YC, ZC);
956  return;
957  }
958 
959 
960 
973  void SpicePosition::SetPolynomial(const std::vector<double>& XC,
974  const std::vector<double>& YC,
975  const std::vector<double>& ZC,
976  const Source type) {
977 
981 
982  // Load the functions with the coefficients
983  function1.SetCoefficients(XC);
984  function2.SetCoefficients(YC);
985  function3.SetCoefficients(ZC);
986 
987  // Compute the base time
988  ComputeBaseTime();
989 
990  // Save the current coefficients
991  p_coefficients[0] = XC;
992  p_coefficients[1] = YC;
993  p_coefficients[2] = ZC;
994 
995  // Set the flag indicating p_degree has been applied to the spacecraft
996  // positions and the coefficients of the polynomials have been saved.
997  p_degreeApplied = true;
998 
999  // Reset the interpolation source
1000  p_source = type;
1001 
1002  // Update the current position
1003  double et = p_et;
1004  p_et = -DBL_MAX;
1005  SetEphemerisTime(et);
1006 
1007  return;
1008  }
1009 
1010 
1011 
1022  void SpicePosition::GetPolynomial(std::vector<double>& XC,
1023  std::vector<double>& YC,
1024  std::vector<double>& ZC) {
1025  XC = p_coefficients[0];
1026  YC = p_coefficients[1];
1027  ZC = p_coefficients[2];
1028 
1029  return;
1030  }
1031 
1032 
1033 
1036  if(p_override == NoOverrides) {
1037  p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1039  }
1040  else if (p_override == ScaleOnly) {
1041  p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1043  }
1044  else {
1047  }
1048 
1049  // Take care of case where 1st and last times are the same
1050  if(p_timeScale == 0) p_timeScale = 1.0;
1051  return;
1052  }
1053 
1054 
1066  void SpicePosition::SetOverrideBaseTime(double baseTime, double timeScale) {
1067  p_overrideBaseTime = baseTime;
1068  p_overrideTimeScale = timeScale;
1069  p_override = BaseAndScale;
1070  return;
1071  }
1072 
1073 
1074 
1087  SpicePosition::PartialType partialVar, int coeffIndex) {
1088  // Start with a zero vector since the derivative of the other coordinates
1089  // with respect to the partial var will be 0.
1090  std::vector<double> coordinate(3, 0);
1091 
1092  // Get the index of the coordinate to update with the partial derivative
1093  int coordIndex = partialVar;
1094 
1095 // if(coeffIndex > 2) {
1096 // QString msg = "SpicePosition only supports up to a 2nd order fit for the spacecraft position";
1097 // throw IException(IException::Programmer, msg, _FILEINFO_);
1098 // }
1099 //
1100  // Reset the coordinate to its derivative
1101  coordinate[coordIndex] = DPolynomial(coeffIndex);
1102  return coordinate;
1103  }
1104 
1105 
1106 
1127  std::vector<double> SpicePosition::VelocityPartial(
1128  SpicePosition::PartialType partialVar, int coeffIndex) {
1129  // Start with a zero vector since the derivative of the other coordinates with
1130  // respect to the partial var will be 0.
1131  std::vector<double> dvelocity(3, 0);
1132 
1133  // Get the index of the coordinate to update with the partial derivative
1134  int coordIndex = partialVar;
1135 
1136  double time = (p_et - p_baseTime) / p_timeScale;
1137  double derivative = 0.;
1138 
1139  // Handle arithmetic failures
1140  double Epsilon = 1.0e-15;
1141  if (fabs(time) <= Epsilon) time = 0.0;
1142 
1143  // Only compute for coefficient index > 0 to avoid problems like 0 ** -1
1144  if (coeffIndex > 0)
1145  derivative = coeffIndex * pow(time, coeffIndex-1) / p_timeScale;
1146 
1147  // Reset the velocity coordinate to its derivative
1148  dvelocity[coordIndex] = derivative;
1149  return dvelocity;
1150  }
1151 
1152 
1163  double SpicePosition::DPolynomial(const int coeffIndex) {
1164  double derivative = 1.;
1165  double time = (p_et - p_baseTime) / p_timeScale;
1166 
1167  if(coeffIndex > 0 && coeffIndex <= p_degree) {
1168  derivative = pow(time, coeffIndex);
1169  }
1170  else if(coeffIndex == 0) {
1171  derivative = 1;
1172  }
1173  else {
1174  Isis::IString msg = "Coeff index, " + Isis::IString(coeffIndex) +
1175  " exceeds degree of polynomial";
1177  }
1178 
1179  return derivative;
1180  }
1181 
1186  const std::vector<double> &SpicePosition::Velocity() {
1187  if(p_hasVelocity) {
1188  return p_velocity;
1189  }
1190  else {
1191  QString msg = "No velocity vector available";
1193  }
1194  }
1195 
1196 
1197 
1212  // If the cache has only one position return it
1213  if(p_cache.size() == 1) {
1214  p_coordinate[0] = p_cache[0][0];
1215  p_coordinate[1] = p_cache[0][1];
1216  p_coordinate[2] = p_cache[0][2];
1217 
1218  if(p_hasVelocity) {
1219  p_velocity[0] = p_cacheVelocity[0][0];
1220  p_velocity[1] = p_cacheVelocity[0][1];
1221  p_velocity[2] = p_cacheVelocity[0][2];
1222  }
1223  }
1224 
1225  else {
1226  // Otherwise determine the interval to interpolate
1227  std::vector<double>::iterator pos;
1228  pos = upper_bound(p_cacheTime.begin(), p_cacheTime.end(), p_et);
1229 
1230  int cacheIndex;
1231  if(pos != p_cacheTime.end()) {
1232  cacheIndex = distance(p_cacheTime.begin(), pos);
1233  cacheIndex--;
1234  }
1235  else {
1236  cacheIndex = p_cacheTime.size() - 2;
1237  }
1238 
1239  if(cacheIndex < 0) cacheIndex = 0;
1240 
1241  // Interpolate the coordinate
1242  double mult = (p_et - p_cacheTime[cacheIndex]) /
1243  (p_cacheTime[cacheIndex+1] - p_cacheTime[cacheIndex]);
1244  std::vector<double> p2 = p_cache[cacheIndex+1];
1245  std::vector<double> p1 = p_cache[cacheIndex];
1246 
1247  p_coordinate[0] = (p2[0] - p1[0]) * mult + p1[0];
1248  p_coordinate[1] = (p2[1] - p1[1]) * mult + p1[1];
1249  p_coordinate[2] = (p2[2] - p1[2]) * mult + p1[2];
1250 
1251  if(p_hasVelocity) {
1252  p2 = p_cacheVelocity[cacheIndex+1];
1253  p1 = p_cacheVelocity[cacheIndex];
1254  p_velocity[0] = (p2[0] - p1[0]) * mult + p1[0];
1255  p_velocity[1] = (p2[1] - p1[1]) * mult + p1[1];
1256  p_velocity[2] = (p2[2] - p1[2]) * mult + p1[2];
1257  }
1258  }
1259  }
1260 
1261 
1262 
1275 
1276  // what if framing camera???
1277 
1278  // On the first SetEphemerisTime, create our splines. Later calls should
1279  // reuse these splines.
1280  if(p_xhermite == NULL) {
1281  p_overrideTimeScale = 1.;
1282  p_override = ScaleOnly;
1283  ComputeBaseTime();
1284 
1291 
1292  vector<double> xvalues(p_cache.size());
1293  vector<double> xhermiteYValues(p_cache.size());
1294  vector<double> yhermiteYValues(p_cache.size());
1295  vector<double> zhermiteYValues(p_cache.size());
1296 
1297  vector<double> xhermiteVelocities(p_cache.size());
1298  vector<double> yhermiteVelocities(p_cache.size());
1299  vector<double> zhermiteVelocities(p_cache.size());
1300 
1301  for(unsigned int i = 0; i < p_cache.size(); i++) {
1302  vector<double> &cache = p_cache[i];
1303  double &cacheTime = p_cacheTime[i];
1304  xvalues[i] = (cacheTime - p_baseTime) / p_timeScale;
1305 
1306  xhermiteYValues[i] = cache[0];
1307  yhermiteYValues[i] = cache[1];
1308  zhermiteYValues[i] = cache[2];
1309 
1310  if(p_hasVelocity) { // Line scan camera
1311  vector<double> &cacheVelocity = p_cacheVelocity[i];
1312  xhermiteVelocities[i] = cacheVelocity[0];
1313  yhermiteVelocities[i] = cacheVelocity[1];
1314  zhermiteVelocities[i] = cacheVelocity[2];
1315  }
1316  else { // Not line scan camera
1317  throw IException(IException::Io, "No velcities available.",
1318  _FILEINFO_);
1319  }
1320  }
1321 
1322  p_xhermite->AddData(xvalues, xhermiteYValues);
1323  p_xhermite->AddCubicHermiteDeriv(xhermiteVelocities);
1324 
1325  p_yhermite->AddData(xvalues, yhermiteYValues);
1326  p_yhermite->AddCubicHermiteDeriv(yhermiteVelocities);
1327 
1328  p_zhermite->AddData(xvalues, zhermiteYValues);
1329  p_zhermite->AddCubicHermiteDeriv(zhermiteVelocities);
1330  }
1331 
1332  // Next line added 07/13/2009 to prevent Camera unit test from bombing
1333  // because time is outside domain. DAC Also added etype to Evaluate calls
1336 
1337  vector<double> &coordinate = p_coordinate;
1338  double sTime = (p_et - p_baseTime) / p_timeScale;
1339  coordinate[0] = p_xhermite->Evaluate(sTime, etype);
1340  coordinate[1] = p_yhermite->Evaluate(sTime, etype);
1341  coordinate[2] = p_zhermite->Evaluate(sTime, etype);
1342 
1343  vector<double> &velocity = p_velocity;
1344  velocity[0] = p_xhermite->EvaluateCubicHermiteFirstDeriv(sTime);
1345  velocity[1] = p_yhermite->EvaluateCubicHermiteFirstDeriv(sTime);
1346  velocity[2] = p_zhermite->EvaluateCubicHermiteFirstDeriv(sTime);
1347  }
1348 
1349 
1350 
1363  // Create the empty functions
1367 
1368  // Load the coefficients to define the functions
1369  functionX.SetCoefficients(p_coefficients[0]);
1370  functionY.SetCoefficients(p_coefficients[1]);
1371  functionZ.SetCoefficients(p_coefficients[2]);
1372 
1373  // Normalize the time
1374  std::vector<double> rtime;
1375  rtime.push_back((p_et - p_baseTime) / p_timeScale);
1376 
1377  // Evaluate the polynomials at current et to get position;
1378  p_coordinate[0] = functionX.Evaluate(rtime);
1379  p_coordinate[1] = functionY.Evaluate(rtime);
1380  p_coordinate[2] = functionZ.Evaluate(rtime);
1381 
1382  if(p_hasVelocity) {
1383 
1384  if( p_degree == 0)
1386  else
1387  p_velocity[0] = ComputeVelocityInTime(WRT_X);
1388  p_velocity[1] = ComputeVelocityInTime(WRT_Y);
1389  p_velocity[2] = ComputeVelocityInTime(WRT_Z);
1390 
1391 // p_velocity[0] = functionX.DerivativeVar(rtime[0]);
1392 // p_velocity[1] = functionY.DerivativeVar(rtime[0]);
1393 // p_velocity[2] = functionZ.DerivativeVar(rtime[0]);
1394  }
1395  }
1396 
1397 
1411  std::vector<double> hermiteCoordinate = p_coordinate;
1412 
1413 // std::cout << hermiteCoordinate << std::endl;
1414 
1415  std::vector<double> hermiteVelocity = p_velocity;
1417 
1418  for (int index = 0; index < 3; index++) {
1419  p_coordinate[index] += hermiteCoordinate[index];
1420  p_velocity[index] += hermiteVelocity[index];
1421  }
1422  }
1423 
1424 
1441 
1442  double state[6];
1443  bool hasVelocity;
1444  double lt;
1445 
1446  // NOTE: Prefer method getter access as some are virtualized and portray
1447  // the appropriate internal representation!!!
1449  "J2000", GetAberrationCorrection(), state, hasVelocity,
1450  lt);
1451 
1452  // Set the internal state
1453  setStateVector(state, hasVelocity);
1454  setLightTime(lt);
1455  return;
1456  }
1457 
1458 
1471  void SpicePosition::Memcache2HermiteCache(double tolerance) {
1472  if(p_source == HermiteCache) {
1473  return;
1474  }
1475  else if(p_source != Memcache) {
1477  "Source type is not Memcache, cannot convert.",
1478  _FILEINFO_);
1479  }
1480 
1481  // make sure base time is set before it is needed
1482  p_overrideTimeScale = 1.;
1483  p_override = ScaleOnly;
1484  ComputeBaseTime();
1485 
1486  // find current size of cache
1487  int n = p_cacheTime.size() - 1;
1488 
1489  // create 3 starting values for the new table
1490  vector <int> inputIndices;
1491  inputIndices.push_back(0);
1492  inputIndices.push_back(n / 2);
1493  inputIndices.push_back(n);
1494 
1495  // find all indices needed to make a hermite table within the appropriate tolerance
1496  vector <int> indexList = HermiteIndices(tolerance, inputIndices);
1497 
1498  // remove all lines from cache vectors that are not in the index list????
1499  for(int i = n; i >= 0; i--) {
1500  if(!binary_search(indexList.begin(), indexList.end(), i)) {
1501  p_cache.erase(p_cache.begin() + i);
1502  p_cacheTime.erase(p_cacheTime.begin() + i);
1503  p_cacheVelocity.erase(p_cacheVelocity.begin() + i);
1504  }
1505  }
1506 
1508  }
1509 
1531  std::vector<int> SpicePosition::HermiteIndices(double tolerance, std::vector <int> indexList) {
1532 
1533  unsigned int n = indexList.size();
1534  double sTime;
1535 
1539  for(unsigned int i = 0; i < indexList.size(); i++) {
1540  sTime = (p_cacheTime[indexList[i]] - p_baseTime) / p_timeScale;
1541  xhermite.AddData(sTime, p_cache[indexList[i]][0]); // add time, x-position to x spline
1542  yhermite.AddData(sTime, p_cache[indexList[i]][1]); // add time, y-position to y spline
1543  zhermite.AddData(sTime, p_cache[indexList[i]][2]); // add time, z-position to z spline
1544 
1545  if(p_hasVelocity) { // Line scan camera
1546  xhermite.AddCubicHermiteDeriv(p_cacheVelocity[i][0]); // add x-velocity to x spline
1547  yhermite.AddCubicHermiteDeriv(p_cacheVelocity[i][1]); // add y-velocity to y spline
1548  zhermite.AddCubicHermiteDeriv(p_cacheVelocity[i][2]); // add z-velocity to z spline
1549  }
1550  else { // Not line scan camera
1551  throw IException(IException::Io, "No velcities available.", _FILEINFO_);
1552  // xhermite.AddCubicHermiteDeriv(0.0); // spacecraft didn't move => velocity = 0
1553  // yhermite.AddCubicHermiteDeriv(0.0); // spacecraft didn't move => velocity = 0
1554  // zhermite.AddCubicHermiteDeriv(0.0); // spacecraft didn't move => velocity = 0
1555  }
1556  }
1557 
1558  // loop through the saved indices from the end
1559  for(unsigned int i = indexList.size() - 1; i > 0; i--) {
1560  double xerror = 0;
1561  double yerror = 0;
1562  double zerror = 0;
1563 
1564  // check every value of the original kernel values within interval
1565  for(int line = indexList[i-1] + 1; line < indexList[i]; line++) {
1566  sTime = (p_cacheTime[line] - p_baseTime) / p_timeScale;
1567 
1568  // find the errors at each value
1569  xerror = fabs(xhermite.Evaluate(sTime) - p_cache[line][0]);
1570  yerror = fabs(yhermite.Evaluate(sTime) - p_cache[line][1]);
1571  zerror = fabs(zhermite.Evaluate(sTime) - p_cache[line][2]);
1572 
1573  if(xerror > tolerance || yerror > tolerance || zerror > tolerance) {
1574  // if any error is greater than tolerance, no need to continue looking, break
1575  break;
1576  }
1577  }
1578 
1579  if(xerror < tolerance && yerror < tolerance && zerror < tolerance) {
1580  // if errors are less than tolerance after looping interval, no new point is necessary
1581  continue;
1582  }
1583  else {
1584  // if any error is greater than tolerance, add midpoint of interval to indexList vector
1585  indexList.push_back((indexList[i] + indexList[i-1]) / 2);
1586  }
1587  }
1588 
1589  if(indexList.size() > n) {
1590  sort(indexList.begin(), indexList.end());
1591  indexList = HermiteIndices(tolerance, indexList);
1592  }
1593  return indexList;
1594  }
1595 
1596 
1601  p_cache.clear();
1602  p_cacheVelocity.clear();
1603  p_cacheTime.clear();
1604 
1605  if(p_xhermite) {
1606  delete p_xhermite;
1607  p_xhermite = NULL;
1608  }
1609 
1610  if(p_yhermite) {
1611  delete p_xhermite;
1612  p_xhermite = NULL;
1613  }
1614 
1615  if(p_zhermite) {
1616  delete p_xhermite;
1617  p_xhermite = NULL;
1618  }
1619  }
1620 
1621 
1622 
1632  // Adjust the degree for the data
1633  if(p_fullCacheSize == 1) {
1634  degree = 0;
1635  }
1636  else if(p_fullCacheSize == 2) {
1637  degree = 1;
1638  }
1639  // If polynomials have not been applied yet simply set the degree and return
1640  if(!p_degreeApplied) {
1641  p_degree = degree;
1642  }
1643 
1644  // Otherwise the existing polynomials need to be either expanded ...
1645  else if(p_degree < degree) { // (increase the number of terms)
1646  std::vector<double> coefX(p_coefficients[0]),
1647  coefY(p_coefficients[1]),
1648  coefZ(p_coefficients[2]);
1649 
1650  for(int icoef = p_degree + 1; icoef <= degree; icoef++) {
1651  coefX.push_back(0.);
1652  coefY.push_back(0.);
1653  coefZ.push_back(0.);
1654  }
1655  p_degree = degree;
1656  SetPolynomial(coefX, coefY, coefZ);
1657  }
1658  // ... or reduced (decrease the number of terms)
1659  else if(p_degree > degree) {
1660  std::vector<double> coefX(degree + 1),
1661  coefY(degree + 1),
1662  coefZ(degree + 1);
1663 
1664  for(int icoef = 0; icoef <= degree; icoef++) {
1665  coefX.push_back(p_coefficients[0][icoef]);
1666  coefY.push_back(p_coefficients[1][icoef]);
1667  coefZ.push_back(p_coefficients[2][icoef]);
1668  }
1669  p_degree = degree;
1670  SetPolynomial(coefX, coefY, coefZ);
1671  }
1672  }
1673 
1674 
1675 
1688  p_source = Spice;
1689  ClearCache();
1690  LoadCache(table);
1691  }
1692 
1698  // Loop and load the time cache
1699  double cacheSlope = 0.0;
1700 
1701  if(p_fullCacheSize > 1)
1702  cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) /
1703  (double)(p_fullCacheSize - 1);
1704 
1705  for(int i = 0; i < p_fullCacheSize; i++) {
1706  double et = p_fullCacheStartTime + (double) i * cacheSlope;
1707  p_cacheTime.push_back(et);
1708  }
1709  }
1710 
1714  const std::vector<double> &SpicePosition::GetCenterCoordinate() {
1715  // Compute the center time
1716  double etCenter = (p_fullCacheEndTime + p_fullCacheStartTime) / 2.;
1717  SetEphemerisTime(etCenter);
1718 
1719  return Coordinate();
1720  }
1721 
1722 
1731  double SpicePosition::ComputeVelocityInTime(PartialType var) {
1732  double velocity = 0.;
1733 
1734  for (int icoef=1; icoef <= p_degree; icoef++) {
1735  velocity += icoef*p_coefficients[var][icoef]*pow((p_et - p_baseTime), (icoef - 1))
1736  / pow(p_timeScale, icoef);
1737  }
1738 
1739  return velocity;
1740  }
1741 
1742 
1754  std::vector<double> SpicePosition::Extrapolate(double timeEt) {
1755  if (!p_hasVelocity) return p_coordinate;
1756 
1757  double diffTime = timeEt - p_et;
1758  std::vector<double> extrapPos(3, 0.);
1759  extrapPos[0] = p_coordinate[0] + diffTime*p_velocity[0];
1760  extrapPos[1] = p_coordinate[1] + diffTime*p_velocity[1];
1761  extrapPos[2] = p_coordinate[2] + diffTime*p_velocity[2];
1762 
1763  return extrapPos;
1764  }
1765 
1766 
1774  std::vector<double> SpicePosition::HermiteCoordinate() {
1777  "Hermite coordinates only available for PolyFunctionOverHermiteConstant",
1778  _FILEINFO_);
1779  }
1780 
1781  // Save the current coordinate so it can be reset
1782  std::vector<double> coordinate = p_coordinate;
1784  std::vector<double> hermiteCoordinate = p_coordinate;
1785  p_coordinate = coordinate;
1786  return hermiteCoordinate;
1787  }
1788 
1789  //=========================================================================
1790  // Observer/Target swap and light time correction methods
1791  //=========================================================================
1792 
1804  return (p_observerCode);
1805  }
1806 
1818  return (p_targetCode);
1819  }
1820 
1821 
1834  return (EphemerisTime() + GetTimeBias());
1835  }
1836 
1837 
1870  void SpicePosition::computeStateVector(double et, int target, int observer,
1871  const QString &refFrame,
1872  const QString &abcorr,
1873  double state[6], bool &hasVelocity,
1874  double &lightTime) const {
1875 
1876  // First try getting the entire state (including the velocity vector)
1877  hasVelocity = true;
1878  lightTime = 0.0;
1879  spkez_c((SpiceInt) target, (SpiceDouble) et, refFrame.toAscii().data(),
1880  abcorr.toAscii().data(), (SpiceInt) observer, state, &lightTime);
1881 
1882  // If Naif fails attempting to get the entire state, assume the velocity vector is
1883  // not available and just get the position. First turn off Naif error reporting and
1884  // return any error without printing them.
1885  SpiceBoolean spfailure = failed_c();
1886  reset_c(); // Reset Naif error system to allow caller to recover
1887  if ( spfailure ) {
1888  hasVelocity = false;
1889  lightTime = 0.0;
1890  spkezp_c((SpiceInt) target, (SpiceDouble) et, refFrame.toAscii().data(),
1891  abcorr.toAscii().data(), (SpiceInt) observer, state, &lightTime);
1892  }
1893 
1894  return;
1895  }
1896 
1921  void SpicePosition::setStateVector(const double state[6],
1922  const bool &hasVelocity) {
1923 
1924  p_coordinate[0] = state[0];
1925  p_coordinate[1] = state[1];
1926  p_coordinate[2] = state[2];
1927 
1928  if ( hasVelocity ) {
1929  p_velocity[0] = state[3];
1930  p_velocity[1] = state[4];
1931  p_velocity[2] = state[5];
1932  }
1933  else {
1934  p_velocity[0] = 0.0;
1935  p_velocity[1] = 0.0;
1936  p_velocity[2] = 0.0;
1937  }
1938  p_hasVelocity = hasVelocity;
1939 
1940  // Negate vectors if swap of observer target is requested so interface
1941  // remains consistent
1942  if (m_swapObserverTarget) {
1943  vminus_c(&p_velocity[0], &p_velocity[0]);
1944  vminus_c(&p_coordinate[0], &p_coordinate[0]);
1945  }
1946 
1947  return;
1948  }
1949 
1951  void SpicePosition::setLightTime(const double &lightTime) {
1952  m_lt = lightTime;
1953  return;
1954  }
1955 
1956  // /** Resets the source interpolation of the position.
1957  // *
1958  // * @param [in] source The interpolation to use for calculating position
1959  // *
1960  // */
1961  // void SetSource(Source source) {
1962  // p_source = source;
1963  // return;
1964  // }
1965 }
1966 
1967 
1968 
1969 
1970 
1971