|
Isis 3.0 Application Source Code Reference |
Home |
00001 #include <string> 00002 #include <algorithm> 00003 #include <vector> 00004 #include <cfloat> 00005 #include <cmath> 00006 #include <iostream> 00007 #include <iomanip> 00008 00009 #include "PixelOffset.h" 00010 #include "TextFile.h" 00011 #include "LineEquation.h" 00012 #include "LeastSquares.h" 00013 #include "BasisFunction.h" 00014 #include "PolynomialUnivariate.h" 00015 #include "iString.h" 00016 #include "iException.h" 00017 00018 00019 namespace Isis { 00020 /** 00021 * Construct a Pixel Offset class loading the offsets in the input file into 00022 * the offset caches. 00023 * 00024 * @param tableList Ascii table list of sample, line offsets and their corresponding time. 00025 * @param fl Focal length of instrument in mm. 00026 * @param pixpitch Pixel pitch of instrument in mm/pixel. 00027 */ 00028 PixelOffset::PixelOffset( const std::string &tableList, double fl, double pixPitch, double baseTime, 00029 double timeScale, int degree ) { 00030 p_focalLen = fl; 00031 p_pixPitch = pixPitch; 00032 // p_RJ.resize(9); 00033 p_et = -DBL_MAX; 00034 p_degree = degree; 00035 p_baseTime = baseTime; 00036 p_timeScale = timeScale; 00037 p_angleScale = p_pixPitch/p_focalLen; 00038 std::vector<std::string> lines; 00039 Isis::TextFile inputFile(tableList, "input", lines); 00040 std::vector<std::string> fields; 00041 p_I.push_back(1.0); 00042 p_I.push_back(0.0); 00043 p_I.push_back(0.0); 00044 p_I.push_back(0.0); 00045 p_I.push_back(1.0); 00046 p_I.push_back(0.0); 00047 p_I.push_back(0.0); 00048 p_I.push_back(0.0); 00049 p_I.push_back(1.0); 00050 00051 for (size_t iline=0; iline<lines.size(); iline++) { 00052 int num = iString::Split( ' ', iString::ConvertWhiteSpace(lines[iline]), fields, false); 00053 if (num != 3) { 00054 Isis::iString msg = "Three fields are required: sample, line, and ephemeris time."; 00055 throw Isis::iException::Message(Isis::iException::Io,msg,_FILEINFO_); 00056 } 00057 p_samples.push_back(iString::ToDouble(fields[0])); 00058 p_lines.push_back(iString::ToDouble(fields[1])); 00059 p_times.push_back(iString::ToDouble(fields[2])); 00060 } 00061 00062 // Compute scalers test1 00063 double sMin=p_samples[0]; 00064 double sMax=p_samples[0]; 00065 double lMin=p_lines[0]; 00066 double lMax=p_lines[0]; 00067 00068 for (int i=1; i<(int) p_samples.size(); i++) { 00069 if (p_samples[i]<sMin) sMin = p_samples[i]; 00070 if (p_samples[i]>sMax) sMax = p_samples[i]; 00071 if (p_lines[i]<lMin) lMin = p_lines[i]; 00072 if (p_lines[i]>lMax) lMax = p_lines[i]; 00073 } 00074 p_sampScale = (sMax - sMin)/2.; 00075 p_sampOff = sMax - p_sampScale; 00076 p_lineScale = (lMax - lMin)/2.; 00077 p_lineOff = lMax - p_lineScale; 00078 } 00079 00080 /** Compute the angular equivalents for the offsets at a given time. 00081 * 00082 * This method computes the angular equivalents in radians for the offsets 00083 * at a given et in seconds. The pixel offsets are interpolated from the 00084 * offsets input in the table using a cubic interpolation and converted to 00085 * angles based on the focal length and the pixel pitch. 00086 * 00087 * @param et ephemeris time in seconds 00088 */ 00089 void PixelOffset::SetEphemerisTime(double et) { 00090 // Save the time 00091 if (p_et == et) return; 00092 p_et = et; 00093 00094 // Otherwise determine the interval to interpolate 00095 std::vector<double>::iterator pos; 00096 pos = upper_bound(p_times.begin(),p_times.end(),p_et); 00097 00098 int index=0; 00099 double soc0, soc1, soc2, soc3, loc0, loc1, loc2, loc3; 00100 soc0=soc1=soc2=soc3=loc0=loc1=loc2=loc3=0.; 00101 if (pos != p_times.end() && pos > (p_times.begin()+1) ) { 00102 index = distance(p_times.begin(),pos); 00103 index--; 00104 soc3 = p_samples[index+2] - p_samples[index+1] - p_samples[index-1] + p_samples[index]; 00105 soc2 = p_samples[index-1] - p_samples[index] - soc3; 00106 soc1 = p_samples[index+1] - p_samples[index-1]; 00107 soc0 = p_samples[index]; 00108 loc3 = p_lines[index+2] - p_lines[index+1] - p_lines[index-1] + p_lines[index]; 00109 loc2 = p_lines[index-1] - p_lines[index] - loc3; 00110 loc1 = p_lines[index+1] - p_lines[index-1]; 00111 loc0 = p_lines[index]; 00112 } else if (pos == p_times.begin()+1) { 00113 // Since values for index cacheIndex-1 do not exist, make up one based on slope from segment 00114 // at index to index+1. Replace p_samps[index-1] with 00115 // p_samps[index] - (p_samps[index+1 - p_samps[index] ) 00116 index = distance(p_times.begin(),pos); 00117 index--; 00118 soc3 = p_samples[index+2] - p_samples[index]; 00119 soc2 = p_samples[index] - p_samples[index+1] - soc3; 00120 soc1 = 2.*(p_samples[index+1] - p_samples[index]); 00121 soc0 = p_samples[index]; 00122 loc3 = p_lines[index+2] - p_lines[index]; 00123 loc2 = p_lines[index] - p_lines[index+1] - loc3; 00124 loc1 = 2.*(p_lines[index+1] - p_lines[index]); 00125 loc0 = p_lines[index]; 00126 } 00127 else if (pos == p_times.end()) { 00128 // Since values for index cacheIndex+2 do not exist, make up one based on slope from segment 00129 // at index to index+1. Replace p_samps[index+2] with 00130 // p_samps[index] - (p_samps[index+1 - p_samps[index] ) 00131 index = distance(p_times.begin(),pos); 00132 index--; 00133 soc3 = 3.*p_samples[index] - 2.*p_samples[index+1] - p_samples[index-1]; 00134 soc2 = p_samples[index-1] - p_samples[index] - soc3; 00135 soc1 = p_samples[index+1] - p_samples[index-1]; 00136 soc0 = p_samples[index]; 00137 loc3 = p_lines[index+2] - p_lines[index+1] - p_lines[index-1] + p_lines[index]; 00138 loc3 = 3.*p_lines[index] - 2.*p_lines[index+1] - p_lines[index-1]; 00139 loc2 = p_lines[index-1] - p_lines[index] - loc3; 00140 loc1 = p_lines[index+1] - p_lines[index-1]; 00141 loc0 = p_lines[index]; 00142 } 00143 else if (pos == p_times.begin()) { 00144 index = 0; 00145 soc3 = soc2 = soc1 = 0.; 00146 soc0 = p_samples[index]; 00147 loc3 = loc2 = loc1 = 0.; 00148 loc0 = p_lines[index]; 00149 } 00150 // else if (WHAT DOES upper_bound DO IF VALUE IS GREATER THAN TOP OF INTERVAL?????) { 00151 // } 00152 if (index < 0) index = 0; 00153 00154 // Interpolate the offsets 00155 double mult = (p_et - p_times[index]) / 00156 (p_times[index+1] - p_times[index]); 00157 double mult2 = mult*mult; 00158 double sampleOff = soc3*mult*mult2 + soc2*mult2 + soc1*mult + soc0; 00159 double lineOff = loc3*mult*mult2 + loc2*mult2 + loc1*mult + loc0; 00160 // 00161 // std::cout<<" "<<sampleOff<<" "<<lineOff<<" "<<p_et<<std::endl; 00162 // 00163 // Convert pixel offsets to angles 00164 p_angle1 = -sampleOff; 00165 p_angle2 = -lineOff; 00166 } 00167 00168 00169 /** Cache J2000 rotation quaternion over a time range. 00170 * 00171 * This method will load an internal cache with frames over a time 00172 * range. This prevents the NAIF kernels from being read over-and-over 00173 * again and slowing an application down due to I/O performance. Once the 00174 * cache has been loaded then the kernels can be unloaded from the NAIF 00175 * system. 00176 * 00177 * @param startTime Starting ephemeris time in seconds for the cache 00178 * @param endTime Ending ephemeris time in seconds for the cache 00179 * @param size Number of frames to keep in the cache 00180 * 00181 */ 00182 void PixelOffset::LoadAngles ( std::vector<double> cacheTime ) { 00183 p_cacheTime = cacheTime; 00184 // Make sure angles aren't already loaded TBD 00185 /* if () { 00186 std::string msg = "An angle cache has already been created"; 00187 throw Isis::iException::Message(Isis::iException::Programmer,msg,_FILEINFO_); 00188 } */ 00189 00190 // Loop and load the angle caches 00191 for (size_t i=0; i<p_cacheTime.size(); i++) { 00192 double et = p_cacheTime[i]; 00193 SetEphemerisTime(et); 00194 p_cacheAngle1.push_back( (p_angle1 - p_sampOff)/p_sampScale ); 00195 p_cacheAngle2.push_back( (p_angle2 - p_lineOff)/p_lineScale); 00196 00197 // std::cout<<p_angle1<<" "<<p_angle2<<" "<<et <<std::endl; 00198 } 00199 } 00200 00201 00202 00203 /** Set the coefficients of a polynomial fit to each 00204 * of the three camera angles for the time period covered by the 00205 * cache, angle = a + bt + ct**2, where t = (time - p_baseTime)/ p_timeScale. 00206 * 00207 */ 00208 void PixelOffset::SetPolynomial () { 00209 Isis::PolynomialUnivariate function1(p_degree); //!< Basis function fit to 1st rotation angle 00210 Isis::PolynomialUnivariate function2(p_degree); //!< Basis function fit to 2nd rotation angle 00211 // 00212 LeastSquares *fitAng1 = new LeastSquares ( function1 ); 00213 LeastSquares *fitAng2 = new LeastSquares ( function2 ); 00214 00215 std::vector<double> time; 00216 00217 // Load the known values to compute the fit equation 00218 for (std::vector<double>::size_type pos=0;pos < p_cacheTime.size();pos++) { 00219 double t = p_cacheTime.at(pos); 00220 00221 // Base fit on extent of coverage in the input offset file 00222 if (t >= p_times[0] && t <= p_times[p_times.size()-1]) { 00223 time.push_back( (t - p_baseTime) / p_timeScale ); 00224 SetEphemerisTime( t ); 00225 fitAng1->AddKnown ( time, p_cacheAngle1[pos] ); 00226 fitAng2->AddKnown ( time, p_cacheAngle2[pos] ); 00227 time.clear(); 00228 } 00229 } 00230 00231 if (fitAng1->Knowns() == 0) { 00232 std::string msg; 00233 msg = "Cube time range is not covered by jitter file"; 00234 throw iException::Message(Isis::iException::User,msg,_FILEINFO_); 00235 } 00236 00237 //Solve the equations for the coefficients 00238 fitAng1->Solve(); 00239 fitAng2->Solve(); 00240 00241 // Delete the least squares objects now that we have all the coefficients 00242 delete fitAng1; 00243 delete fitAng2; 00244 00245 // For now assume both angles are fit to a polynomial. Later they may 00246 // each be fit to a unique basis function. 00247 // Fill the coefficient vectors 00248 00249 for ( int i = 0; i < function1.Coefficients(); i++) { 00250 p_ang1Coefficients.push_back( function1.Coefficient( i ) ); 00251 p_ang2Coefficients.push_back( function2.Coefficient( i ) ); 00252 } 00253 00254 // std::cout<<"Angle1 coeff="<<p_ang1Coefficients[0]<<" "<<p_ang1Coefficients[1]<<" "<<p_ang1Coefficients[2]<<std::endl; 00255 // std::cout<<"Angle2 coeff="<<p_ang2Coefficients[0]<<" "<<p_ang2Coefficients[1]<<" "<<p_ang2Coefficients[2]<<std::endl; 00256 00257 } 00258 00259 00260 00261 /** Set ephemeris time for the high pass filtered rotation 00262 * from the instrument frame to the true (corrected) instrument 00263 * frame. [TC] = [angle2 - Pangle2(t)] [angle1 - Pangle1(t)] 00264 * 2 1 00265 * where t = (time - p_baseTime) / p_timeScale, and n = p_degree. 00266 * 00267 * @param [in] et Ephemeris time 00268 * 00269 */ 00270 std::vector<double> PixelOffset::SetEphemerisTimeHPF ( const double et ) { 00271 00272 Isis::PolynomialUnivariate function1( p_degree ); 00273 Isis::PolynomialUnivariate function2( p_degree ); 00274 00275 //If outside the range of the offsets just return the identity matrix 00276 if (et < p_times[0] || et > p_times[p_times.size()-1]) { 00277 return (p_I); 00278 } 00279 00280 // Load the functions with the coefficients 00281 function1.SetCoefficients ( p_ang1Coefficients ); 00282 function2.SetCoefficients ( p_ang2Coefficients ); 00283 00284 // Compute polynomial approximations to angles, pangle1 and pangle2 00285 std::vector<double> rtime; 00286 rtime.push_back((et - p_baseTime) / p_timeScale); 00287 double pangle1 = p_sampOff + p_sampScale*function1.Evaluate (rtime); 00288 double pangle2 = p_lineOff + p_lineScale*function2.Evaluate (rtime); 00289 00290 // Compute p_angles for this time 00291 SetEphemerisTime(et); 00292 double angle1 = p_angleScale*(p_angle1 - pangle1); 00293 double angle2 = p_angleScale*(p_angle2 - pangle2); 00294 00295 00296 // std::cout<<" "<<p_angle1*p_angleScale<<" "<<pangle1*p_angleScale<<" "<<angle1<<" "<<et<<std::endl; 00297 // std::cout<<" "<<p_angle2*p_angleScale<<" "<<pangle2*p_angleScale<<" "<<angle2<<" "<<et<<std::endl; 00298 00299 std::vector<double> TC(9); 00300 00301 eul2m_c ( (SpiceDouble) 0., (SpiceDouble) angle2, (SpiceDouble) angle1, 00302 3, 2, 1, 00303 (SpiceDouble(*) [3]) &TC[0]); 00304 00305 return (TC); 00306 } 00307 00308 00309 00310 /** Wrap the input angle to keep it within 2pi radians of the 00311 * angle to compare. 00312 * 00313 * @param [in] compareAngle 00314 * @param [in] angle Angle to be wrapped if needed 00315 * @return double Wrapped angle 00316 * 00317 */ 00318 double PixelOffset::WrapAngle(double compareAngle, double angle) { 00319 double diff1 = compareAngle - angle; 00320 00321 if ( diff1 < -1*pi_c() ){ 00322 angle -= twopi_c(); 00323 } 00324 else if (diff1 > pi_c() ) { 00325 angle += twopi_c(); 00326 } 00327 return angle; 00328 } 00329 00330 }