USGS

Isis 3.0 Application Source Code Reference

Home

PixelOffset.cpp

Go to the documentation of this file.
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 }