Isis 3 Programmer Reference
SpicePosition.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "SpicePosition.h"
8
9#include <algorithm>
10#include <cfloat>
11#include <iomanip>
12
13#include <QString>
14#include <QStringList>
15#include <QChar>
16
17#include "BasisFunction.h"
18#include "IException.h"
19#include "LeastSquares.h"
20#include "LineEquation.h"
21#include "NaifStatus.h"
22#include "NumericalApproximation.h"
23#include "PolynomialUnivariate.h"
24#include "TableField.h"
25
26using json = nlohmann::json;
27
28namespace Isis {
37 SpicePosition::SpicePosition(int targetCode, int observerCode) {
38 init(targetCode, observerCode, false);
39 }
40
70 SpicePosition::SpicePosition(int targetCode, int observerCode,
71 bool swapObserverTarget) {
72 init(targetCode, observerCode, swapObserverTarget);
73 }
74
87 void SpicePosition::init(int targetCode, int observerCode,
88 const bool &swapObserverTarget) {
89
91 p_baseTime = 0.;
92 p_coefficients[0].clear();
93 p_coefficients[1].clear();
94 p_coefficients[2].clear();
95 p_coordinate.resize(3);
96 p_degree = 2;
97 p_degreeApplied = false;
98 p_et = -DBL_MAX;
101 p_fullCacheSize = 0;
102 p_hasVelocity = false;
103
104 p_override = NoOverrides;
105 p_source = Spice;
106
107 p_timeBias = 0.0;
108 p_timeScale = 1.;
109 p_velocity.resize(3);
110
111
112 m_swapObserverTarget = swapObserverTarget;
113 m_lt = 0.0;
114 m_state = NULL;
115
116 // Determine observer/target ordering
117 if ( m_swapObserverTarget ) {
118 // New/improved settings.. Results in vector negation in SetEphemerisTimeSpice
119 p_targetCode = observerCode;
120 p_observerCode = targetCode;
121 }
122 else {
123 // Traditional settings
124 p_targetCode = targetCode;
125 p_observerCode = observerCode;
126 }
127 }
128
129
136
137
150 void SpicePosition::SetTimeBias(double timeBias) {
151 p_timeBias = timeBias;
152 }
153
162 return (p_timeBias);
163 }
164
186 void SpicePosition::SetAberrationCorrection(const QString &correction) {
187 QString abcorr(correction);
188 abcorr.remove(QChar(' '));
189 abcorr = abcorr.toUpper();
190 QStringList validAbcorr;
191 validAbcorr << "NONE" << "LT" << "LT+S" << "CN" << "CN+S"
192 << "XLT" << "XLT+S" << "XCN" << "XCN+S";
193
194 if (validAbcorr.indexOf(abcorr) >= 0) {
195 p_aberrationCorrection = abcorr;
196 }
197 else {
198 QString msg = "Invalid abberation correction [" + correction + "]";
199 throw IException(IException::Programmer, msg, _FILEINFO_);
200 }
201 }
202
216
231 return (m_lt);
232 }
233
249 const std::vector<double> &SpicePosition::SetEphemerisTime(double et) {
251
252 // Save the time
253 if(et == p_et) {
254 return p_coordinate;
255 }
256
257 p_et = et;
258
259 // Read from the cache
260 if(p_source == Memcache) {
262 }
263 else if(p_source == HermiteCache) {
265 }
266 else if(p_source == PolyFunction) {
268 }
271 }
272 else { // Read from the kernel
274 }
275
277
278 // Return the coordinate
279 return p_coordinate;
280 }
281
282
297 void SpicePosition::LoadCache(double startTime, double endTime, int size) {
298 // Make sure cache isn't already loaded
300 QString msg = "A SpicePosition cache has already been created";
301 throw IException(IException::Programmer, msg, _FILEINFO_);
302 }
303
304 if(startTime > endTime) {
305 QString msg = "Argument startTime must be less than or equal to endTime";
306 throw IException(IException::Programmer, msg, _FILEINFO_);
307 }
308
309 if((startTime != endTime) && (size == 1)) {
310 QString msg = "Cache size must be more than 1 if startTime endTime differ";
311 throw IException(IException::Programmer, msg, _FILEINFO_);
312 }
313
314 // Save full cache parameters
315 p_fullCacheStartTime = startTime;
316 p_fullCacheEndTime = endTime;
317 p_fullCacheSize = size;
319
320 // Loop and load the cache
321 std::vector<ale::State> stateCache;
322 for(int i = 0; i < size; i++) {
323 double et = p_cacheTime[i];
325 ale::State currentState = ale::State(ale::Vec3d(p_coordinate));
326 if (p_hasVelocity) {
327 currentState.velocity = ale::Vec3d(p_velocity);
328 }
329 stateCache.push_back(currentState);
330 }
331
332 if (m_state != NULL) {
333 delete m_state;
334 }
335
336 m_state = new ale::States(p_cacheTime, stateCache);
338 }
339
340
354 void SpicePosition::LoadCache(double time) {
355 LoadCache(time, time, 1);
356 }
357
358
368 void SpicePosition::LoadCache(json &isdPos) {
369 if (p_source != Spice) {
370 throw IException(IException::Programmer, "SpicePosition::LoadCache(json) only supports Spice source", _FILEINFO_);
371 }
372
373 // Load the full cache time information from the label if available
374 p_fullCacheStartTime = isdPos["spk_table_start_time"].get<double>();
375 p_fullCacheEndTime = isdPos["spk_table_end_time"].get<double>();
376 p_fullCacheSize = isdPos["spk_table_original_size"].get<double>();
377 p_cacheTime = isdPos["ephemeris_times"].get<std::vector<double>>();
378
379 p_hasVelocity = isdPos.find("velocities") != isdPos.end();
380
381 std::vector<ale::State> stateCache;
382 if (p_hasVelocity) {
383 for (auto it = isdPos["positions"].begin(); it != isdPos["positions"].end(); it++) {
384 int index = it - isdPos["positions"].begin();
385 std::vector<double> pos = it->get<std::vector<double>>();
386 std::vector<double> vel = isdPos["velocities"][index];
387 stateCache.push_back(ale::State(ale::Vec3d(pos), ale::Vec3d(vel)));
388 }
389 }
390 else {
391 for (auto it = isdPos["positions"].begin(); it != isdPos["positions"].end(); it++) {
392 std::vector<double> pos = {it->at(0).get<double>(), it->at(1).get<double>(), it->at(2).get<double>()};
393 stateCache.push_back(ale::State(ale::Vec3d(pos)));
394 }
395 }
396
397 if (m_state != NULL) {
398 delete m_state;
399 }
400
401 m_state = new ale::States(p_cacheTime, stateCache);
402
405 }
406
430
431 // Make sure cache isn't alread loaded
433 QString msg = "A SpicePosition cache has already been created";
434 throw IException(IException::Programmer, msg, _FILEINFO_);
435 }
436
437 // Load the full cache time information from the label if available
438 if(table.Label().hasKeyword("SpkTableStartTime")) {
439 p_fullCacheStartTime = toDouble(table.Label().findKeyword("SpkTableStartTime")[0]);
440 }
441 if(table.Label().hasKeyword("SpkTableEndTime")) {
442 p_fullCacheEndTime = toDouble(table.Label().findKeyword("SpkTableEndTime")[0]);
443 }
444 if(table.Label().hasKeyword("SpkTableOriginalSize")) {
445 p_fullCacheSize = toDouble(table.Label().findKeyword("SpkTableOriginalSize")[0]);
446 }
447
448
449 // set source type by table's label keyword
450 if(!table.Label().hasKeyword("CacheType")) {
452 }
453 else if(table.Label().findKeyword("CacheType")[0] == "Linear") {
455 }
456 else if(table.Label().findKeyword("CacheType")[0] == "HermiteSpline") {
459 p_override = ScaleOnly;
460 }
461 else if(table.Label().findKeyword("CacheType")[0] == "PolyFunction") {
463 }
464 else {
466 "Invalid value for CacheType keyword in the table "
467 + table.Name(),
468 _FILEINFO_);
469 }
470
471 std::vector<ale::State> stateCache;
472 // Loop through and move the table to the cache
473 if (p_source != PolyFunction) {
474 for (int r = 0; r < table.Records(); r++) {
475 TableRecord &rec = table[r];
476 if (rec.Fields() == 7) {
477 p_hasVelocity = true;
478 }
479 else if (rec.Fields() == 4) {
480 p_hasVelocity = false;
481 }
482 else {
483 QString msg = "Expecting four or seven fields in the SpicePosition table";
484 throw IException(IException::Programmer, msg, _FILEINFO_);
485 }
486
487 ale::State currentState(ale::Vec3d((double)rec[0],
488 (double)rec[1],
489 (double)rec[2]));
490
491 int inext = 3;
492
493 if (p_hasVelocity) {
494 currentState.velocity = ale::Vec3d((double)rec[3],
495 (double)rec[4],
496 (double)rec[5]);
497 inext = 6;
498 }
499 stateCache.push_back(currentState);
500 p_cacheTime.push_back((double)rec[inext]);
501 }
502
503 if (m_state != NULL) {
504 delete m_state;
505 }
506 m_state = new ale::States(p_cacheTime, stateCache);
507 }
508 else {
509 // Coefficient table for postion coordinates x, y, and z
510 std::vector<double> coeffX, coeffY, coeffZ;
511
512 for (int r = 0; r < table.Records() - 1; r++) {
513 TableRecord &rec = table[r];
514
515 if(rec.Fields() != 3) {
516 // throw an error
517 }
518 coeffX.push_back((double)rec[0]);
519 coeffY.push_back((double)rec[1]);
520 coeffZ.push_back((double)rec[2]);
521 }
522 // Take care of function time parameters
523 TableRecord &rec = table[table.Records()-1];
524 double baseTime = (double)rec[0];
525 double timeScale = (double)rec[1];
526 double degree = (double)rec[2];
527 SetPolynomialDegree((int) degree);
528 SetOverrideBaseTime(baseTime, timeScale);
529 SetPolynomial(coeffX, coeffY, coeffZ);
530 if (degree > 0) p_hasVelocity = true;
531 if(degree == 0 && m_state->hasVelocity()) p_hasVelocity = true;
532 }
533 }
534
535
549 Table SpicePosition::Cache(const QString &tableName) {
551 LineCache(tableName);
552 // TODO Figure out how to get the tolerance -- for now hard code .01
554 }
555
556 // record to be added to table
557 TableRecord record;
558
559 if (p_source != PolyFunction) {
560 // add x,y,z position labels to record
561 TableField x("J2000X", TableField::Double);
562 TableField y("J2000Y", TableField::Double);
563 TableField z("J2000Z", TableField::Double);
564 record += x;
565 record += y;
566 record += z;
567
568 if (p_hasVelocity) {
569 // add x,y,z velocity labels to record
570 TableField vx("J2000XV", TableField::Double);
571 TableField vy("J2000YV", TableField::Double);
572 TableField vz("J2000ZV", TableField::Double);
573 record += vx;
574 record += vy;
575 record += vz;
576 }
577 // add time label to record
579 record += t;
580
581 // create output table
582 Table table(tableName, record);
583
584 int inext = 0;
585
586 std::vector<ale::State> stateCache = m_state->getStates();
587 for (int i = 0; i < (int)stateCache.size(); i++) {
588 record[inext++] = stateCache[i].position.x; // record[0]
589 record[inext++] = stateCache[i].position.y; // record[1]
590 record[inext++] = stateCache[i].position.z; // record[2]
591 if (p_hasVelocity) {
592 record[inext++] = stateCache[i].velocity.x; // record[3]
593 record[inext++] = stateCache[i].velocity.y; // record[4]
594 record[inext++] = stateCache[i].velocity.z; // record[5]
595 }
596 record[inext] = p_cacheTime[i]; // record[6]
597 table += record;
598
599 inext = 0;
600 }
601
602 CacheLabel(table);
603 return table;
604 }
605
606 else if(p_source == PolyFunction && p_degree == 0 && p_fullCacheSize == 1){
607 // Just load the position for the single epoch
608 return LineCache(tableName);
609 }
610 // Load the coefficients for the curves fit to the 3 camera angles
611 else if (p_source == PolyFunction) {
612 // PolyFunction case
613 TableField spacecraftX("J2000SVX", TableField::Double);
614 TableField spacecraftY("J2000SVY", TableField::Double);
615 TableField spacecraftZ("J2000SVZ", TableField::Double);
616
617 TableRecord record;
618 record += spacecraftX;
619 record += spacecraftY;
620 record += spacecraftZ;
621
622 Table table(tableName, record);
623
624 for(int cindex = 0; cindex < p_degree + 1; cindex++) {
625 record[0] = p_coefficients[0][cindex];
626 record[1] = p_coefficients[1][cindex];
627 record[2] = p_coefficients[2][cindex];
628 table += record;
629 }
630
631 // Load one more table entry with the time adjustments for the fit equation
632 // t = (et - baseTime)/ timeScale
633 record[0] = p_baseTime;
634 record[1] = p_timeScale;
635 record[2] = (double) p_degree;
636
637 CacheLabel(table);
638 table += record;
639 return table;
640 }
641
642 else {
644 "Cannot create Table, no Cache is loaded.",
645 _FILEINFO_);
646 }
647
648 }
649
650
651
659 // determine type of table to return
660 QString tabletype = "";
661
662 if(p_source == Memcache) {
663 tabletype = "Linear";
664 }
665 else if(p_source == HermiteCache) {
666 tabletype = "HermiteSpline";
667 }
668 else {
669 tabletype = "PolyFunction";
670 }
671
672 table.Label() += PvlKeyword("CacheType", tabletype);
673
674 // Write original time coverage
675 if(p_fullCacheStartTime != 0) {
676 table.Label() += PvlKeyword("SpkTableStartTime");
677 table.Label()["SpkTableStartTime"].addValue(toString(p_fullCacheStartTime));
678 }
679 if(p_fullCacheEndTime != 0) {
680 table.Label() += PvlKeyword("SpkTableEndTime");
681 table.Label()["SpkTableEndTime"].addValue(toString(p_fullCacheEndTime));
682 }
683 if(p_fullCacheSize != 0) {
684 table.Label() += PvlKeyword("SpkTableOriginalSize");
685 table.Label()["SpkTableOriginalSize"].addValue(toString(p_fullCacheSize));
686 }
687 }
688
689
690
704 Table SpicePosition::LineCache(const QString &tableName) {
705
706 // Apply the function and fill the caches
708
709 if(p_source != Memcache) {
710 QString msg = "Only cached positions can be returned as a line cache of positions and time";
711 throw IException(IException::Programmer, msg, _FILEINFO_);
712 }
713 // Load the table and return it to caller
714 return Cache(tableName);
715 }
716
717
718
731
732 // Save current et
733 double et = p_et;
734
735 // Make sure source is a function
736 if(p_source < HermiteCache) {
737 QString msg = "The SpicePosition has not yet been fit to a function";
738 throw IException(IException::Programmer, msg, _FILEINFO_);
739 }
740
741 // Clear existing positions from thecache
742 p_cacheTime.clear();
743
744 // Load the time cache first
746
747 if (p_hasVelocity) {
748 // Load the positions and velocity caches
749 p_et = -DBL_MAX; // Forces recalculation in SetEphemerisTime
750 std::vector<ale::State> stateCache;
751 for (std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
753 stateCache.push_back(ale::State(ale::Vec3d(p_coordinate), ale::Vec3d(p_velocity)));
754 }
755 if (m_state != NULL) {
756 delete m_state;
757 }
758 m_state = new ale::States(p_cacheTime, stateCache);
759 }
760 else {
761 // Load the position for the single updated time instance
762 p_et = p_cacheTime[0];
764 std::vector<ale::State> stateCache;
765 stateCache.push_back(ale::Vec3d(p_coordinate));
766 std::vector<double> timeCache;
767 timeCache.push_back(p_cacheTime[0]);
768 if (m_state != NULL) {
769 delete m_state;
770 }
771 m_state = new ale::States(timeCache, stateCache);
772 }
773
774 // Set source to cache and reset current et
776 p_et = -DBL_MAX;
778
780 }
781
782
793 Table SpicePosition::LoadHermiteCache(const QString &tableName) {
794 // find the first and last time values
795 double firstTime = p_fullCacheStartTime;
796 double lastTime = p_fullCacheEndTime;
797 int cacheTimeSize = (int) p_fullCacheSize;
798
799 // Framing cameras are already cached and don't need to be reduced.
800 if(cacheTimeSize == 1) return Cache(tableName);
801
802 //If it's already a hermite cache, just return it.
803 if (p_source == HermiteCache) {
804 return Cache(tableName);
805 }
806
807 // Make sure a polynomial function is already loaded
808 if(p_source != PolyFunction) {
809 QString msg = "A SpicePosition polynomial function has not been created yet";
810 throw IException(IException::Programmer, msg, _FILEINFO_);
811 }
812
813 // Load the polynomial functions
817 function1.SetCoefficients(p_coefficients[0]);
818 function2.SetCoefficients(p_coefficients[1]);
819 function3.SetCoefficients(p_coefficients[2]);
820
821 // Clear existing coordinates from cache
822 ClearCache();
823
824 // Set velocity vector to true since it is calculated
825 p_hasVelocity = true;
826
827 // Calculate new postition coordinates from polynomials fit to coordinates &
828 // fill cache
829// std::cout << "Before" << std::endl;
830// double savetime = p_cacheTime.at(0);
831// SetEphemerisTime(savetime);
832// std::cout << " at time " << savetime << std::endl;
833// for (int i=0; i<3; i++) {
834// std::cout << p_coordinate[i] << std::endl;
835// }
836
837
838 // find time for the extremum of each polynomial
839 // since this is only a 2nd degree polynomial,
840 // finding these extrema is simple
841 double b1 = function1.Coefficient(1);
842 double c1 = function1.Coefficient(2);
843 double extremumXtime = -b1 / (2.*c1) + p_baseTime; // extremum is time value for root of 1st derivative
844 // make sure we are in the domain
845 if(extremumXtime < firstTime) {
846 extremumXtime = firstTime;
847 }
848 if(extremumXtime > lastTime) {
849 extremumXtime = lastTime;
850 }
851
852 double b2 = function2.Coefficient(1);
853 double c2 = function2.Coefficient(2);
854 double extremumYtime = -b2 / (2.*c2) + p_baseTime;
855 // make sure we are in the domain
856 if(extremumYtime < firstTime) {
857 extremumYtime = firstTime;
858 }
859 if(extremumYtime > lastTime) {
860 extremumYtime = lastTime;
861 }
862
863 double b3 = function3.Coefficient(1);
864 double c3 = function3.Coefficient(2);
865 double extremumZtime = -b3 / (2.*c3) + p_baseTime;
866 // make sure we are in the domain
867 if(extremumZtime < firstTime) {
868 extremumZtime = firstTime;
869 }
870 if(extremumZtime > lastTime) {
871 extremumZtime = lastTime;
872 }
873
874 // refill the time vector
875 p_cacheTime.clear();
876 p_cacheTime.push_back(firstTime);
877 p_cacheTime.push_back(extremumXtime);
878 p_cacheTime.push_back(extremumYtime);
879 p_cacheTime.push_back(extremumZtime);
880 p_cacheTime.push_back(lastTime);
881 // we don't know order of extrema, so sort
882 sort(p_cacheTime.begin(), p_cacheTime.end());
883 // in case an extremum is an endpoint
884 std::vector <double>::iterator it = unique(p_cacheTime.begin(), p_cacheTime.end());
885 p_cacheTime.resize(it - p_cacheTime.begin());
886
887 if(p_cacheTime.size() == 2) {
888 p_cacheTime.clear();
889 p_cacheTime.push_back(firstTime);
890 p_cacheTime.push_back((firstTime + lastTime) / 2);
891 p_cacheTime.push_back(lastTime);
892 }
893
894 // add positions and velocities for these times
895 std::vector<ale::State> stateCache;
896 for(int i = 0; i < (int) p_cacheTime.size(); i++) {
897 // x,y,z positions
898 double time;
899 time = p_cacheTime[i] - p_baseTime;
900 p_coordinate[0] = function1.Evaluate(time);
901 p_coordinate[1] = function2.Evaluate(time);
902 p_coordinate[2] = function3.Evaluate(time);
903
904 // x,y,z velocities
905 p_velocity[0] = b1 + 2 * c1 * (p_cacheTime[i] - p_baseTime);
906 p_velocity[1] = b2 + 2 * c2 * (p_cacheTime[i] - p_baseTime);
907 p_velocity[2] = b3 + 2 * c3 * (p_cacheTime[i] - p_baseTime);
908 stateCache.push_back(ale::State(p_coordinate, p_velocity));
909 }
910 if (m_state != NULL) {
911 delete m_state;
912 }
913 m_state = new ale::States(p_cacheTime, stateCache);
914
916 double et = p_et;
917 p_et = -DBL_MAX;
919
920 return Cache(tableName);
921 }
922
923
931 std::vector<double> XC, YC, ZC;
932
933 // Check to see if the position is already a Polynomial Function
934 if (p_source == PolyFunction)
935 return;
936
937 // Adjust the degree of the polynomial to the available data
938 int size = m_state->getStates().size();
939 if (size == 1) {
940 p_degree = 0;
941 }
942 else if (size == 2) {
943 p_degree = 1;
944 }
945
946 // Check for polynomial over Hermite constant and initialize coefficients
948 XC.assign(p_degree + 1, 0.);
949 YC.assign(p_degree + 1, 0.);
950 ZC.assign(p_degree + 1, 0.);
951 SetPolynomial(XC, YC, ZC, type);
952 return;
953 }
954
958
959 // Compute the base time
961 std::vector<double> time;
962
963 if(size == 1) {
964 double t = p_cacheTime.at(0);
966 XC.push_back(p_coordinate[0]);
967 YC.push_back(p_coordinate[1]);
968 ZC.push_back(p_coordinate[2]);
969 }
970 else if(size == 2) {
971 // Load the times and get the corresponding coordinates
972 double t1 = p_cacheTime.at(0);
974 std::vector<double> coord1 = p_coordinate;
975 t1 = (t1 - p_baseTime) / p_timeScale;
976 double t2 = p_cacheTime.at(1);
978 std::vector<double> coord2 = p_coordinate;
979 t2 = (t2 - p_baseTime) / p_timeScale;
980 double slope[3];
981 double intercept[3];
982
983 // Compute the linear equation for each coordinate and save them
984 for(int cIndex = 0; cIndex < 3; cIndex++) {
985 Isis::LineEquation posline(t1, coord1[cIndex], t2, coord2[cIndex]);
986 slope[cIndex] = posline.Slope();
987 intercept[cIndex] = posline.Intercept();
988 }
989 XC.push_back(intercept[0]);
990 XC.push_back(slope[0]);
991 YC.push_back(intercept[1]);
992 YC.push_back(slope[1]);
993 ZC.push_back(intercept[2]);
994 ZC.push_back(slope[2]);
995 }
996 else {
997 LeastSquares *fitX = new LeastSquares(function1);
998 LeastSquares *fitY = new LeastSquares(function2);
999 LeastSquares *fitZ = new LeastSquares(function3);
1000
1001 // Load the known values to compute the fit equation
1002 for(std::vector<double>::size_type pos = 0; pos < p_cacheTime.size(); pos++) {
1003 double t = p_cacheTime.at(pos);
1004 time.push_back( (t - p_baseTime) / p_timeScale);
1006 std::vector<double> coord = p_coordinate;
1007
1008 fitX->AddKnown(time, coord[0]);
1009 fitY->AddKnown(time, coord[1]);
1010 fitZ->AddKnown(time, coord[2]);
1011 time.clear();
1012 }
1013 // Solve the equations for the coefficients
1014 fitX->Solve();
1015 fitY->Solve();
1016 fitZ->Solve();
1017
1018 // Delete the least squares objects now that we have all the coefficients
1019 delete fitX;
1020 delete fitY;
1021 delete fitZ;
1022
1023 // For now assume all three coordinates are fit to a polynomial function.
1024 // Later they may each be fit to a unique basis function.
1025 // Fill the coefficient vectors
1026
1027 for(int i = 0; i < function1.Coefficients(); i++) {
1028 XC.push_back(function1.Coefficient(i));
1029 YC.push_back(function2.Coefficient(i));
1030 ZC.push_back(function3.Coefficient(i));
1031 }
1032
1033 }
1034
1035 // Now that the coefficients have been calculated set the polynomial with them
1036 SetPolynomial(XC, YC, ZC);
1037 return;
1038 }
1039
1040
1041
1054 void SpicePosition::SetPolynomial(const std::vector<double>& XC,
1055 const std::vector<double>& YC,
1056 const std::vector<double>& ZC,
1057 const Source type) {
1058
1062
1063 // Load the functions with the coefficients
1064 function1.SetCoefficients(XC);
1065 function2.SetCoefficients(YC);
1066 function3.SetCoefficients(ZC);
1067
1068 // Compute the base time
1070
1071 // Save the current coefficients
1072 p_coefficients[0] = XC;
1073 p_coefficients[1] = YC;
1074 p_coefficients[2] = ZC;
1075
1076 // Set the flag indicating p_degree has been applied to the spacecraft
1077 // positions and the coefficients of the polynomials have been saved.
1078 p_degreeApplied = true;
1079
1080 // Reset the interpolation source
1081 p_source = type;
1082
1083 // Update the current position
1084 double et = p_et;
1085 p_et = -DBL_MAX;
1086 SetEphemerisTime(et);
1087
1088 return;
1089 }
1090
1091
1092
1103 void SpicePosition::GetPolynomial(std::vector<double>& XC,
1104 std::vector<double>& YC,
1105 std::vector<double>& ZC) {
1106 XC = p_coefficients[0];
1107 YC = p_coefficients[1];
1108 ZC = p_coefficients[2];
1109
1110 return;
1111 }
1112
1113
1114
1117 if(p_override == NoOverrides) {
1118 p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1120 }
1121 else if (p_override == ScaleOnly) {
1122 p_baseTime = (p_cacheTime.at(0) + p_cacheTime.at(p_cacheTime.size() - 1)) / 2.;
1124 }
1125 else {
1128 }
1129
1130 // Take care of case where 1st and last times are the same
1131 if(p_timeScale == 0) p_timeScale = 1.0;
1132 return;
1133 }
1134
1135
1147 void SpicePosition::SetOverrideBaseTime(double baseTime, double timeScale) {
1148 p_overrideBaseTime = baseTime;
1149 p_overrideTimeScale = timeScale;
1150 p_override = BaseAndScale;
1151 return;
1152 }
1153
1154
1155
1168 SpicePosition::PartialType partialVar, int coeffIndex) {
1169 // Start with a zero vector since the derivative of the other coordinates
1170 // with respect to the partial var will be 0.
1171 std::vector<double> coordinate(3, 0);
1172
1173 // Get the index of the coordinate to update with the partial derivative
1174 int coordIndex = partialVar;
1175
1176// if(coeffIndex > 2) {
1177// QString msg = "SpicePosition only supports up to a 2nd order fit for the spacecraft position";
1178// throw IException(IException::Programmer, msg, _FILEINFO_);
1179// }
1180//
1181 // Reset the coordinate to its derivative
1182 coordinate[coordIndex] = DPolynomial(coeffIndex);
1183 return coordinate;
1184 }
1185
1186
1187
1209 SpicePosition::PartialType partialVar, int coeffIndex) {
1210 // Start with a zero vector since the derivative of the other coordinates with
1211 // respect to the partial var will be 0.
1212 std::vector<double> dvelocity(3, 0);
1213
1214 // Get the index of the coordinate to update with the partial derivative
1215 int coordIndex = partialVar;
1216
1217 double time = (p_et - p_baseTime) / p_timeScale;
1218 double derivative = 0.;
1219
1220 // Handle arithmetic failures
1221 double Epsilon = 1.0e-15;
1222 if (fabs(time) <= Epsilon) time = 0.0;
1223
1224 // Only compute for coefficient index > 0 to avoid problems like 0 ** -1
1225 if (coeffIndex > 0)
1226 derivative = coeffIndex * pow(time, coeffIndex-1) / p_timeScale;
1227
1228 // Reset the velocity coordinate to its derivative
1229 dvelocity[coordIndex] = derivative;
1230 return dvelocity;
1231 }
1232
1233
1244 double SpicePosition::DPolynomial(const int coeffIndex) {
1245 double derivative = 1.;
1246 double time = (p_et - p_baseTime) / p_timeScale;
1247
1248 if(coeffIndex > 0 && coeffIndex <= p_degree) {
1249 derivative = pow(time, coeffIndex);
1250 }
1251 else if(coeffIndex == 0) {
1252 derivative = 1;
1253 }
1254 else {
1255 QString msg = "Unable to evaluate the derivative of the SPICE position fit polynomial for "
1256 "the given coefficient index [" + toString(coeffIndex) + "]. "
1257 "Index is negative or exceeds degree of polynomial ["
1258 + toString(p_degree) + "]";
1259 throw IException(IException::Programmer, msg, _FILEINFO_);
1260 }
1261
1262 return derivative;
1263 }
1264
1269 const std::vector<double> &SpicePosition::Velocity() {
1270 if(p_hasVelocity) {
1271 return p_velocity;
1272 }
1273 else {
1274 QString msg = "No velocity vector available";
1275 throw IException(IException::Programmer, msg, _FILEINFO_);
1276 }
1277 }
1278
1279
1280
1295 ale::State state;
1296 if (p_cacheTime.size() == 1) {
1297 state = m_state->getStates().front();
1298 }
1299 else {
1300 state = m_state->getState(p_et, ale::LINEAR);
1301 }
1302 p_coordinate[0] = state.position.x;
1303 p_coordinate[1] = state.position.y;
1304 p_coordinate[2] = state.position.z;
1305 if (p_hasVelocity){
1306 p_velocity[0] = state.velocity.x;
1307 p_velocity[1] = state.velocity.y;
1308 p_velocity[2] = state.velocity.z;
1309 }
1310 }
1311
1312
1325 if (p_hasVelocity) {
1326 ale::State state = m_state->getState(p_et, ale::SPLINE);
1327
1328 p_coordinate[0] = state.position.x;
1329 p_coordinate[1] = state.position.y;
1330 p_coordinate[2] = state.position.z;
1331
1332 p_velocity[0] = state.velocity.x;
1333 p_velocity[1] = state.velocity.y;
1334 p_velocity[2] = state.velocity.z;
1335 }
1336 else {
1337 throw IException(IException::Io, "No velocities available. Cannot calculate Hermite Cache.",
1338 _FILEINFO_);
1339 }
1340 }
1341
1342
1343
1356 // Create the empty functions
1360
1361 // Load the coefficients to define the functions
1362 functionX.SetCoefficients(p_coefficients[0]);
1363 functionY.SetCoefficients(p_coefficients[1]);
1364 functionZ.SetCoefficients(p_coefficients[2]);
1365
1366 // Normalize the time
1367 double rtime;
1368 rtime = (p_et - p_baseTime) / p_timeScale;
1369
1370 // Evaluate the polynomials at current et to get position;
1371 p_coordinate[0] = functionX.Evaluate(rtime);
1372 p_coordinate[1] = functionY.Evaluate(rtime);
1373 p_coordinate[2] = functionZ.Evaluate(rtime);
1374
1375 if(p_hasVelocity) {
1376
1377 if( p_degree == 0) {
1378 ale::Vec3d velocity = m_state->getVelocities()[0];
1379 p_velocity[0] = velocity.x;
1380 p_velocity[1] = velocity.y;
1381 p_velocity[2] = velocity.z;
1382 }
1383 else {
1387 }
1388 }
1389 }
1390
1391
1405 std::vector<double> hermiteCoordinate = p_coordinate;
1406
1407 std::vector<double> hermiteVelocity = p_velocity;
1409
1410 for (int index = 0; index < 3; index++) {
1411 p_coordinate[index] += hermiteCoordinate[index];
1412 p_velocity[index] += hermiteVelocity[index];
1413 }
1414 }
1415
1416
1433
1434 double state[6];
1435 bool hasVelocity;
1436 double lt;
1437
1438 // NOTE: Prefer method getter access as some are virtualized and portray
1439 // the appropriate internal representation!!!
1441 "J2000", GetAberrationCorrection(), state, hasVelocity,
1442 lt);
1443
1444 // Set the internal state
1445 setStateVector(state, hasVelocity);
1446 setLightTime(lt);
1447 return;
1448 }
1449
1450
1464 if(p_source == HermiteCache) {
1465 return;
1466 }
1467 else if(p_source != Memcache) {
1469 "Source type is not Memcache, cannot convert.",
1470 _FILEINFO_);
1471 }
1472
1473 ale::States *tempStates = new ale::States(m_state->minimizeCache(tolerance));
1474 delete m_state;
1475 m_state = tempStates;
1476 p_cacheTime = m_state->getTimes();
1478 }
1479
1480
1485 if (m_state) {
1486 delete m_state;
1487 m_state = NULL;
1488 }
1489
1490 p_cacheTime.clear();
1491 }
1492
1493
1503 // Adjust the degree for the data
1504 if(p_fullCacheSize == 1) {
1505 degree = 0;
1506 }
1507 else if(p_fullCacheSize == 2) {
1508 degree = 1;
1509 }
1510 // If polynomials have not been applied yet simply set the degree and return
1511 if(!p_degreeApplied) {
1512 p_degree = degree;
1513 }
1514
1515 // Otherwise the existing polynomials need to be either expanded ...
1516 else if(p_degree < degree) { // (increase the number of terms)
1517 std::vector<double> coefX(p_coefficients[0]),
1518 coefY(p_coefficients[1]),
1519 coefZ(p_coefficients[2]);
1520
1521 for(int icoef = p_degree + 1; icoef <= degree; icoef++) {
1522 coefX.push_back(0.);
1523 coefY.push_back(0.);
1524 coefZ.push_back(0.);
1525 }
1526 p_degree = degree;
1527 SetPolynomial(coefX, coefY, coefZ);
1528 }
1529 // ... or reduced (decrease the number of terms)
1530 else if(p_degree > degree) {
1531 std::vector<double> coefX(degree + 1),
1532 coefY(degree + 1),
1533 coefZ(degree + 1);
1534
1535 for(int icoef = 0; icoef <= degree; icoef++) {
1536 coefX.push_back(p_coefficients[0][icoef]);
1537 coefY.push_back(p_coefficients[1][icoef]);
1538 coefZ.push_back(p_coefficients[2][icoef]);
1539 }
1540 p_degree = degree;
1541 SetPolynomial(coefX, coefY, coefZ);
1542 }
1543 }
1544
1545
1552 return (p_et - p_baseTime) / p_timeScale;
1553 }
1554
1555
1568 p_source = Spice;
1569 ClearCache();
1570 LoadCache(table);
1571 }
1572
1578 // Loop and load the time cache
1579 double cacheSlope = 0.0;
1580
1581 if(p_fullCacheSize > 1)
1582 cacheSlope = (p_fullCacheEndTime - p_fullCacheStartTime) /
1583 (double)(p_fullCacheSize - 1);
1584
1585 for(int i = 0; i < p_fullCacheSize; i++) {
1586 double et = p_fullCacheStartTime + (double) i * cacheSlope;
1587 p_cacheTime.push_back(et);
1588 }
1589
1590 }
1591
1595 const std::vector<double> &SpicePosition::GetCenterCoordinate() {
1596 // Compute the center time
1597 double etCenter = (p_fullCacheEndTime + p_fullCacheStartTime) / 2.;
1598 SetEphemerisTime(etCenter);
1599
1600 return Coordinate();
1601 }
1602
1603
1613 double velocity = 0.;
1614
1615 for (int icoef=1; icoef <= p_degree; icoef++) {
1616 velocity += icoef*p_coefficients[var][icoef]*pow((p_et - p_baseTime), (icoef - 1))
1617 / pow(p_timeScale, icoef);
1618 }
1619
1620 return velocity;
1621 }
1622
1623
1635 std::vector<double> SpicePosition::Extrapolate(double timeEt) {
1636 if (!p_hasVelocity) return p_coordinate;
1637
1638 double diffTime = timeEt - p_et;
1639 std::vector<double> extrapPos(3, 0.);
1640 extrapPos[0] = p_coordinate[0] + diffTime*p_velocity[0];
1641 extrapPos[1] = p_coordinate[1] + diffTime*p_velocity[1];
1642 extrapPos[2] = p_coordinate[2] + diffTime*p_velocity[2];
1643
1644 return extrapPos;
1645 }
1646
1647
1655 std::vector<double> SpicePosition::HermiteCoordinate() {
1658 "Hermite coordinates only available for PolyFunctionOverHermiteConstant",
1659 _FILEINFO_);
1660 }
1661
1662 // Save the current coordinate so it can be reset
1663 std::vector<double> coordinate = p_coordinate;
1665 std::vector<double> hermiteCoordinate = p_coordinate;
1666 p_coordinate = coordinate;
1667 return hermiteCoordinate;
1668 }
1669
1670 //=========================================================================
1671 // Observer/Target swap and light time correction methods
1672 //=========================================================================
1673
1685 return (p_observerCode);
1686 }
1687
1699 return (p_targetCode);
1700 }
1701
1702
1715 return (EphemerisTime() + GetTimeBias());
1716 }
1717
1718
1751 void SpicePosition::computeStateVector(double et, int target, int observer,
1752 const QString &refFrame,
1753 const QString &abcorr,
1754 double state[6], bool &hasVelocity,
1755 double &lightTime) const {
1756
1757 // First try getting the entire state (including the velocity vector)
1759 hasVelocity = true;
1760 lightTime = 0.0;
1761 spkez_c((SpiceInt) target, (SpiceDouble) et, refFrame.toLatin1().data(),
1762 abcorr.toLatin1().data(), (SpiceInt) observer, state, &lightTime);
1763
1764 // If Naif fails attempting to get the entire state, assume the velocity vector is
1765 // not available and just get the position. First turn off Naif error reporting and
1766 // return any error without printing them.
1767 SpiceBoolean spfailure = failed_c();
1768 reset_c(); // Reset Naif error system to allow caller to recover
1769 if ( spfailure ) {
1770 hasVelocity = false;
1771 lightTime = 0.0;
1772 spkezp_c((SpiceInt) target, (SpiceDouble) et, refFrame.toLatin1().data(),
1773 abcorr.toLatin1().data(), (SpiceInt) observer, state, &lightTime);
1774 }
1776 return;
1777 }
1778
1803 void SpicePosition::setStateVector(const double state[6],
1804 const bool &hasVelocity) {
1805
1806 p_coordinate[0] = state[0];
1807 p_coordinate[1] = state[1];
1808 p_coordinate[2] = state[2];
1809
1810 if ( hasVelocity ) {
1811 p_velocity[0] = state[3];
1812 p_velocity[1] = state[4];
1813 p_velocity[2] = state[5];
1814 }
1815 else {
1816 p_velocity[0] = 0.0;
1817 p_velocity[1] = 0.0;
1818 p_velocity[2] = 0.0;
1819 }
1820 p_hasVelocity = hasVelocity;
1821
1822 // Negate vectors if swap of observer target is requested so interface
1823 // remains consistent
1824 if (m_swapObserverTarget) {
1825 vminus_c(&p_velocity[0], &p_velocity[0]);
1826 vminus_c(&p_coordinate[0], &p_coordinate[0]);
1827 }
1828
1829 return;
1830 }
1831
1833 void SpicePosition::setLightTime(const double &lightTime) {
1834 m_lt = lightTime;
1835 return;
1836 }
1837
1838 // /** Resets the source interpolation of the position.
1839 // *
1840 // * @param [in] source The interpolation to use for calculating position
1841 // *
1842 // */
1843 // void SetSource(Source source) {
1844 // p_source = source;
1845 // return;
1846 // }
1847}
Isis exception class.
Definition IException.h:91
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
Generic least square fitting class.
Utility class for creating and using cartesean line equations.
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
Nth degree Polynomial with one variable.
A single keyword-value pair.
Definition PvlKeyword.h:87
bool hasKeyword(const QString &kname, FindOptions opts) const
See if a keyword is in the current PvlObject, or deeper inside other PvlObjects and Pvlgroups within ...
PvlKeyword & findKeyword(const QString &kname, FindOptions opts)
Finds a keyword in the current PvlObject, or deeper inside other PvlObjects and Pvlgroups within this...
Obtain SPICE information for a spacecraft.
Definition Spice.h:283
double DPolynomial(const int coeffIndex)
Evaluate the derivative of the fit polynomial (parabola) defined by the given coefficients with respe...
void Memcache2HermiteCache(double tolerance)
This method reduces the cache for position, time and velocity to the minimum number of values needed ...
void ComputeBaseTime()
Compute the base time using cached times.
Table LineCache(const QString &tableName)
Return a table with J2000 to reference positions.
double p_timeScale
Time scale used in fit equations.
std::vector< double > p_coordinate
J2000 position at time et.
double GetTimeBias() const
Returns the value of the time bias added to ET.
double ComputeVelocityInTime(PartialType var)
Compute the velocity with respect to time instead of scaled time.
Source p_source
Enumerated value for the location of the SPK information used.
std::vector< double > p_coefficients[3]
Coefficients of polynomials fit to 3 coordinates.
void SetOverrideBaseTime(double baseTime, double timeScale)
Set an override base time to be used with observations on scanners to allow all images in an observat...
virtual void SetEphemerisTimeSpice()
This is a protected method that is called by SetEphemerisTime() when Source type is Spice.
void GetPolynomial(std::vector< double > &XC, std::vector< double > &YC, std::vector< double > &ZC)
Return the coefficients of a polynomial fit to each of the three coordinates of the position for the ...
void setLightTime(const double &lightTime)
Inheritors can set the light time if indicated.
const std::vector< double > & Velocity()
Return the current J2000 velocity.
void ReloadCache()
Cache J2000 positions over existing cached time range using polynomials.
Source
This enum indicates the status of the object.
@ HermiteCache
Object is reading from splined table.
@ PolyFunction
Object is calculated from nth degree polynomial.
@ Memcache
Object is reading from cached table.
@ Spice
Object is reading directly from the kernels.
@ PolyFunctionOverHermiteConstant
Object is reading from splined.
ale::States * m_state
!< Light time correction
void LoadCache(double startTime, double endTime, int size)
Cache J2000 position over a time range.
double p_baseTime
Base time used in fit equations.
void SetEphemerisTimeMemcache()
This is a protected method that is called by SetEphemerisTime() when Source type is Memcache.
void LoadTimeCache()
Load the time cache.
int p_observerCode
observer body code
void SetPolynomial(const Source type=PolyFunction)
Set the coefficients of a polynomial fit to each of the components (X, Y, Z) of the position vector f...
Table LoadHermiteCache(const QString &tableName)
Cache J2000 position over existing cached time range using polynomials stored as Hermite cubic spline...
std::vector< double > Extrapolate(double timeEt)
Extrapolate position for a given time assuming a constant velocity.
void setStateVector(const double state[6], const bool &hasVelocity)
Sets the state of target relative to observer.
double m_lt
!< Swap traditional order
std::vector< double > p_cacheTime
iTime for corresponding position
std::vector< double > p_velocity
J2000 velocity at time et.
double p_overrideTimeScale
Value set by caller to override computed time scale.
bool p_degreeApplied
Flag indicating whether or not a polynomial.
bool p_hasVelocity
Flag to indicate velocity is available.
void ClearCache()
Removes the entire cache from memory.
double p_fullCacheEndTime
Original end time of the complete cache after spiceinit.
int getTargetCode() const
Returns target code.
double p_overrideBaseTime
Value set by caller to override computed base time.
const std::vector< double > & GetCenterCoordinate()
Compute and return the coordinate at the center time.
void CacheLabel(Table &table)
Add labels to a SpicePosition table.
double scaledTime() const
Return the scaled time.
int p_targetCode
target body code
double getAdjustedEphemerisTime() const
Returns adjusted ephemeris time.
std::vector< double > CoordinatePartial(SpicePosition::PartialType partialVar, int coeffIndex)
Set the coefficients of a polynomial fit to each of the three coordinates of the position vector for ...
void SetEphemerisTimeHermiteCache()
This is a protected method that is called by SetEphemerisTime() when Source type is HermiteCache.
virtual double EphemerisTime() const
Return the current ephemeris time.
void init(int targetCode, int observerCode, const bool &swapObserverTarget=false)
Internal initialization of the object support observer/target swap.
double p_et
Current ephemeris time.
void SetTimeBias(double timeBias)
Apply a time bias when invoking SetEphemerisTime method.
virtual ~SpicePosition()
Destructor.
double p_timeBias
iTime bias when reading kernels
void SetEphemerisTimePolyFunction()
This is a protected method that is called by SetEphemerisTime() when Source type is PolyFunction.
int getObserverCode() const
Returns observer code.
virtual const std::vector< double > & SetEphemerisTime(double et)
Return J2000 coordinate at given time.
SpicePosition(int targetCode, int observerCode)
Construct an empty SpicePosition class using valid body codes.
void SetEphemerisTimePolyFunctionOverHermiteConstant()
This is a protected method that is called by SetEphemerisTime() when Source type is PolyFunctionOverH...
double GetLightTime() const
Return the light time coorection value.
double p_fullCacheSize
Orignial size of the complete cache after spiceinit.
std::vector< double > HermiteCoordinate()
This method returns the Hermite coordinate for the current time for PolyFunctionOverHermiteConstant f...
OverrideType p_override
Time base and scale override options;.
int p_degree
Degree of polynomial function fit to the coordinates of the position.
double p_fullCacheStartTime
Original start time of the complete cache after spiceinit.
virtual const std::vector< double > & Coordinate()
Return the current J2000 position.
void computeStateVector(double et, int target, int observer, const QString &refFrame, const QString &abcorr, double state[6], bool &hasVelocity, double &lightTime) const
Computes the state vector of the target w.r.t observer.
QString p_aberrationCorrection
Light time correction to apply.
virtual void SetAberrationCorrection(const QString &correction)
Set the aberration correction (light time)
void SetPolynomialDegree(int degree)
Set the polynomial degree.
std::vector< double > VelocityPartial(SpicePosition::PartialType partialVar, int coeffIndex)
Compute the derivative of the velocity with respect to the specified variable.
Table Cache(const QString &tableName)
Return a table with J2000 positions.
virtual QString GetAberrationCorrection() const
Returns current state of stellar aberration correction.
Class for storing an Isis::Table's field information.
Definition TableField.h:47
@ Double
The values in the field are 8 byte doubles.
Definition TableField.h:54
Class for storing Table blobs information.
Definition Table.h:61
int Records() const
Returns the number of records.
Definition Table.cpp:313
PvlObject & Label()
The Table's label.
Definition Table.cpp:260
QString Name() const
The Table's name.
Definition Table.cpp:247
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition IString.cpp:149