USGS

Isis 3.0 Object Programmers' Reference

Home

BundleAdjust.cpp

00001 #include "BundleAdjust.h"
00002 
00003 #include <iomanip>
00004 
00005 #include "SpecialPixel.h"
00006 #include "BasisFunction.h"
00007 #include "LeastSquares.h"
00008 #include "CameraDetectorMap.h"
00009 #include "CameraDistortionMap.h"
00010 #include "ControlPoint.h"
00011 #include "SpicePosition.h"
00012 #include "SpicePosition.h"
00013 #include "Application.h"
00014 
00015 namespace Isis {
00016   BundleAdjust::BundleAdjust(const std::string &cnetFile,
00017                              const std::string &cubeList,
00018                              bool printSummary) {
00019     // Get control net and serial number list  
00020     p_cleanUp = true;
00021     Progress progress;
00022     p_cnet = new Isis::ControlNet(cnetFile, &progress);
00023     p_snlist = new Isis::SerialNumberList(cubeList);
00024     p_printSummary = printSummary;
00025     p_heldsnlist = NULL;
00026     p_observationMode = false;
00027     p_solutionMethod = "SVD";
00028     p_onlist = NULL;
00029 
00030     Init( &progress );
00031   }
00032 
00033   BundleAdjust::BundleAdjust(const std::string &cnetFile,
00034                              const std::string &cubeList,
00035                              const std::string &heldList,
00036                              bool printSummary) {
00037      // Get control net, serial number list, and held serial number list
00038      p_cleanUp = true;
00039      Progress progress;
00040      p_cnet = new Isis::ControlNet(cnetFile, &progress);
00041      p_snlist = new Isis::SerialNumberList(cubeList);
00042      p_heldsnlist = new Isis::SerialNumberList(heldList);
00043      p_printSummary = printSummary;
00044      p_observationMode = false;
00045      p_solutionMethod = "SVD";
00046      p_onlist = NULL;
00047 
00048      Init( &progress );
00049    }
00050 
00051   BundleAdjust::BundleAdjust(Isis::ControlNet &cnet,
00052                              Isis::SerialNumberList &snlist,
00053                              bool printSummary) {
00054     // Get control net and serial number list
00055     p_cleanUp = false;
00056     p_cnet = &cnet;
00057     p_snlist = &snlist;
00058     p_printSummary = printSummary;
00059     p_heldsnlist = NULL;
00060     p_observationMode = false;
00061     p_solutionMethod = "SVD";
00062     p_onlist = NULL;
00063 
00064     Init();
00065   }
00066 
00067   BundleAdjust::BundleAdjust(Isis::ControlNet &cnet,
00068                              Isis::SerialNumberList &snlist,
00069                              Isis::SerialNumberList &heldsnlist,
00070                              bool printSummary) {
00071     // Get control net, image serial number list and hold image serial number list
00072     p_cleanUp = false;
00073     p_cnet = &cnet;
00074     p_snlist = &snlist;
00075     p_heldsnlist = &heldsnlist;
00076     p_printSummary = printSummary;
00077     p_observationMode = false;
00078     p_solutionMethod = "SVD";
00079     p_onlist = NULL;
00080 
00081     Init();
00082   }
00083 
00084   BundleAdjust::~BundleAdjust() {
00085     if (p_cleanUp) {
00086       delete p_cnet;
00087       delete p_snlist;
00088       if (p_heldImages > 0) delete p_heldsnlist;
00089       if (p_observationMode) delete p_onlist;
00090     }
00091   }
00092 
00093 
00094   void BundleAdjust::Init(Progress *progress) {
00095     // Get the cameras set up for all images
00096     p_cnet->SetImages(*p_snlist, progress);
00097 
00098     p_heldImages = 0;
00099     int count;
00100 
00101     if (p_heldsnlist != NULL) {
00102       //Check to make sure held images are in the control net
00103       CheckHeldList();
00104       // Set all points on held images to held, using measurement on held image
00105       // to get lat/lon/radius of point 
00106       ApplyHeldList();
00107 
00108       // Create a lookup table of held images
00109       count = 0;
00110       for (int i=0; i<p_snlist->Size(); i++) {
00111         if (p_heldsnlist->HasSerialNumber(p_snlist->SerialNumber(i))) {
00112           p_imageIndexMap.push_back(-1);
00113           p_heldImages++;
00114         } else {
00115           p_imageIndexMap.push_back(count);
00116           count++;
00117         }
00118       }
00119     }
00120     else {
00121       for (int i=0; i<p_snlist->Size(); i++) p_imageIndexMap.push_back(i);
00122     }
00123 
00124 
00125     // Create a lookup table of ignored, held, and ground points
00126     p_heldPoints = p_groundPoints = p_ignoredPoints = 0;
00127     count = 0;
00128     for (int i=0; i<p_cnet->Size(); i++) {
00129       if ((*p_cnet)[i].Held()) {
00130         p_pointIndexMap.push_back(-1);
00131         p_heldPoints++;
00132       }
00133       else if ((*p_cnet)[i].Ignore()) {
00134         p_pointIndexMap.push_back(-1);
00135         p_ignoredPoints++;
00136       }
00137       else if ((*p_cnet)[i].Type() == ControlPoint::Ground) {
00138         p_pointIndexMap.push_back(-1);
00139         p_groundPoints++;
00140       }
00141       else {
00142         p_pointIndexMap.push_back(count);
00143         count++;
00144       }
00145     }
00146 
00147     // Set default variables to solve for
00148     p_solveTwist = true;
00149     p_solveRadii = false;
00150     p_cmatrixSolveType = AnglesOnly;
00151     p_spacecraftPositionSolveType = Nothing;
00152     p_ckDegree = 2;
00153     p_solveCamDegree = p_ckDegree;
00154     p_numberCameraCoefSolved = 1;
00155 
00156     ComputeNumberPartials();
00157 
00158     // TODO:  Need to have some validation code to make sure everything is
00159     // on the up-and-up with the control network.  Add checks for multiple
00160     // networks, images without any points, and points on images removed from
00161     // the control net (when we start adding software to remove points with high
00162     // residuals) and ?.
00163   }
00164 
00170   void BundleAdjust::CheckHeldList () {
00171     for (int ih=0; ih < p_heldsnlist->Size(); ih++) {
00172       if (!(p_snlist->HasSerialNumber(p_heldsnlist->SerialNumber(ih)))) {
00173         std::string msg = "Held image [" + p_heldsnlist->SerialNumber(ih) 
00174                           + "not in FROMLIST";
00175         throw iException::Message(iException::User,msg,_FILEINFO_);
00176       }
00177     }
00178   }
00179 
00180 
00186   void BundleAdjust::ApplyHeldList () {
00187     double lat,lon,rad;
00188     // TODO  Check for points already held ie) case where user has a point on 2
00189     // or more held images
00190 
00191     for (int i=0; i<p_cnet->Size(); i++) {
00192       ControlPoint &pt = (*p_cnet)[i];
00193       if (pt.Ignore()) continue;
00194 
00195       for (int j=0; j<pt.Size(); j++) {
00196         ControlMeasure &m = pt[j];
00197         if (m.Ignore()) continue;
00198 
00199         if (p_heldsnlist->HasSerialNumber(m.CubeSerialNumber())) {
00200           Camera *cam=m.Camera();
00201           if (cam->SetImage(m.Sample(), m.Line())) {
00202             lat = cam->UniversalLatitude();
00203             lon = cam->UniversalLongitude();
00204             rad = cam->LocalRadius(); //meters
00205           }
00206           else {
00207             std::string msg = "Cannot compute lat/lon for control point [" +
00208                pt.Id() + "], measure [" + m.CubeSerialNumber() + "]";
00209              throw iException::Message(iException::User,msg,_FILEINFO_);
00210           }
00211           pt.SetUniversalGround (lat, lon, rad);
00212           pt.SetHeld (true);
00213         }
00214       }
00215     }
00216   }
00217 
00224   void BundleAdjust::ComputeNumberPartials() {
00225     p_numImagePartials = 0;
00226 
00227     if (p_cmatrixSolveType != None) {
00228       // Solve for ra/dec always
00229       p_numImagePartials = 2;
00230      
00231       // Do we solve for twist
00232       if (p_solveTwist) {
00233         p_numImagePartials++;
00234       }
00235 
00236       // Do we solve for angles only, +velocity, or +velocity and acceleration, or all coefficients
00237       p_numImagePartials *= p_numberCameraCoefSolved;
00238 /*      if (p_cmatrixSolveType == AnglesVelocity) {
00239         p_numImagePartials *= 2;
00240       }
00241       else if (p_cmatrixSolveType == AnglesVelocityAcceleration) {
00242         p_numImagePartials *= 3;
00243       }*/
00244     }
00245 
00246     if (p_spacecraftPositionSolveType != Nothing) {
00247        // Solve for position always.
00248        p_numImagePartials += 3;
00249 
00250        // Do we solve for position and velocity, position, velocity and acceleration, or position only
00251        if (p_spacecraftPositionSolveType == PositionVelocity) {
00252          p_numImagePartials += 3;
00253        }
00254        else if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00255          p_numImagePartials += 6;
00256        }
00257      }
00258 
00259     // Solve for lat/lon always
00260     p_numPointPartials = 2;
00261 
00262     // Do we solve for radii
00263     if (p_solveRadii) {
00264       p_numPointPartials++;
00265     }
00266   }
00267 
00273   void BundleAdjust::SetObservationMode ( bool observationMode ) {
00274     p_observationMode = observationMode;
00275 
00276     if (p_observationMode) {
00277     // Create the observation number list
00278       p_onlist = new Isis::ObservationNumberList(p_snlist);
00279       if (p_heldImages != 0) {
00280         p_onlist->Remove(p_heldsnlist);
00281       }
00282 
00283       if (p_heldsnlist != NULL) {
00284       //Make sure ALL images in an observation are held if any are
00285         for (int ih=0; ih < p_heldsnlist->Size(); ih++) {
00286           for (int isn=0; isn < p_snlist->Size(); isn++) {
00287             if (p_heldsnlist->ObservationNumber(ih) == p_snlist->ObservationNumber(isn)) {
00288               if (!(p_heldsnlist->HasSerialNumber(p_snlist->SerialNumber(isn)))) {
00289                 std::string msg = "Cube file " + p_snlist->Filename(isn)
00290                   + " must be held since it is on the same observation as held cube " 
00291                   + p_heldsnlist->Filename(ih);
00292                   throw iException::Message(iException::User,msg,_FILEINFO_);
00293               }
00294             }
00295           }
00296         }
00297       }
00298     }
00299   }
00300 
00301 
00302 
00303 
00307   void BundleAdjust::SetSolveTwist(bool solve) {
00308     p_solveTwist = solve;
00309     ComputeNumberPartials();
00310   }
00311 
00315   void BundleAdjust::SetSolveRadii(bool solve) {
00316     p_solveRadii = solve;
00317     ComputeNumberPartials();
00318   }
00319 
00323   void BundleAdjust::SetSolveCmatrix(CmatrixSolveType type) {
00324     p_cmatrixSolveType = type;
00325 
00326     switch (type) {
00327     case BundleAdjust::AnglesOnly:
00328       p_numberCameraCoefSolved = 1;
00329       break;
00330     case BundleAdjust::AnglesVelocity:
00331       p_numberCameraCoefSolved = 2;
00332       break;
00333     case BundleAdjust::AnglesVelocityAcceleration:
00334       p_numberCameraCoefSolved = 3;
00335       break;
00336     case BundleAdjust::All:
00337       p_numberCameraCoefSolved = p_solveCamDegree+1;
00338       break;
00339     default:
00340       p_numberCameraCoefSolved = 0;
00341       break;
00342     }
00343 
00344     // Make sure the degree of the polynomial the user selected for
00345     // the camera angles fit is sufficient for the selected CAMSOLVE
00346     if (p_numberCameraCoefSolved > p_solveCamDegree+1 ) {
00347       std::string msg = "Selected SolveCameraDegree " + iString(p_solveCamDegree)
00348         + " is not sufficient for the CAMSOLVE";
00349         throw iException::Message(iException::User,msg,_FILEINFO_);
00350     }
00351     ComputeNumberPartials();
00352   }
00353 
00357   void BundleAdjust::SetSolveSpacecraftPosition(SpacecraftPositionSolveType type) {
00358     p_spacecraftPositionSolveType = type;
00359     ComputeNumberPartials();
00360   }
00361 
00367   int BundleAdjust::BasisColumns() const {
00368     int imageColumns = Observations() * p_numImagePartials;
00369 
00370 //    imageColumns -= p_heldImages * p_numImagePartials;
00371 
00372     int pointColumns = p_cnet->Size() * p_numPointPartials;
00373 
00374     pointColumns -= p_groundPoints * p_numPointPartials;
00375     pointColumns -= p_heldPoints * p_numPointPartials;
00376     pointColumns -= p_ignoredPoints * p_numPointPartials;
00377 
00378     return imageColumns + pointColumns;
00379   }
00380 
00395   double BundleAdjust::Solve(double tol, int maxIterations) {
00396     double mmPerPixel = DBL_MAX;
00397     double averageError;
00398     std::vector<int> observationInitialValueIndex;  // Index of image to use for observation inital values
00399     int iIndex = -1; //Index of the image to use for initial spice for an observation
00400     int oIndex = -1;      // Index of current observation
00401 
00402     if (p_observationMode) {
00403       observationInitialValueIndex.assign( p_onlist->ObservationSize(), -1);
00404     }
00405 
00406     for (int i=0; i<Images(); i++) {
00407       if (p_heldImages > 0) {
00408          if ((p_heldsnlist->HasSerialNumber(p_snlist->SerialNumber(i)))) continue;
00409        }
00410       Camera *cam = p_cnet->Camera(i);
00411 
00412       if (p_observationMode) {
00413           oIndex = i;
00414           oIndex = p_onlist->ObservationNumberMapIndex(oIndex);  // Get the observation index for this image
00415           iIndex = observationInitialValueIndex[oIndex]; // Get the index of the image to use for initial values                                                 // being used for the observation
00416       }
00417       if (p_cmatrixSolveType != None) {
00418         // For observations, find the index of the first image and use its polynomial for the observation
00419         // initial coefficient values.  Initialize indeces to -1
00420 
00421         // Fit the camera pointing to an equation
00422         SpiceRotation *rot = cam->InstrumentRotation();
00423 
00424         if (!p_observationMode ) {
00425           rot->SetPolynomialDegree( p_ckDegree ); // Set the ck polynomial fit degree
00426           rot->SetPolynomial();
00427           rot->SetPolynomialDegree( p_solveCamDegree ); // Update to the solve polynomial fit degree
00428         }
00429         else {
00430           // Index of image to use for initial values is set already so set polynomial to initial values
00431           if (iIndex >= 0) {
00432             SpiceRotation *orot = p_cnet->Camera(iIndex)->InstrumentRotation(); //Observation rotation
00433             std::vector<double> anglePoly1, anglePoly2, anglePoly3;
00434             orot->GetPolynomial( anglePoly1, anglePoly2, anglePoly3);
00435             double baseTime = orot->GetBaseTime();
00436             double timeScale = orot->GetTimeScale();
00437             rot->SetPolynomialDegree( p_solveCamDegree ); // Update to the solve polynomial fit degree
00438             rot->SetOverrideBaseTime( baseTime, timeScale );
00439             rot->SetPolynomial( anglePoly1, anglePoly2, anglePoly3);
00440           }
00441           else {
00442             // Index of image to use for inital observation values has not been assigned yet so use this image
00443             rot->SetPolynomialDegree( p_ckDegree ); 
00444             rot->SetPolynomial();
00445             rot->SetPolynomialDegree( p_solveCamDegree ); // Update to the solve polynomial fit degree
00446             observationInitialValueIndex[oIndex] = i;
00447           }
00448         }
00449       }
00450       if (p_spacecraftPositionSolveType != Nothing) {
00451         // Set the spacecraft position to an equation
00452         SpicePosition *pos = cam->InstrumentPosition();
00453 
00454         if (!p_observationMode ) {
00455           pos->SetPolynomial();
00456         }
00457         else {
00458           // Index of image to use for initial values is set already so set polynomial to initial values
00459           if (iIndex >= 0) {
00460             SpicePosition *opos = p_cnet->Camera(iIndex)->InstrumentPosition(); //Observation position
00461             std::vector<double> posPoly1, posPoly2, posPoly3;
00462             opos->GetPolynomial( posPoly1, posPoly2, posPoly3);
00463             double baseTime = opos->GetBaseTime();
00464             pos->SetOverrideBaseTime( baseTime );
00465             pos->SetPolynomial( posPoly1, posPoly2, posPoly3);
00466           }
00467           else {
00468             // Index of image to use for inital observation values has not been assigned yet so use this image
00469             pos->SetPolynomial();
00470             observationInitialValueIndex[oIndex] = i;
00471           }
00472         }
00473       }
00474       if (cam->PixelPitch() < mmPerPixel) {
00475         mmPerPixel = cam->PixelPitch();
00476       }
00477     }
00478 
00479     // Compute the apriori lat/lons for each nonheld point
00480     p_error = DBL_MAX;
00481     p_cnet->ComputeApriori();
00482 
00483     // Initialize solution parameters
00484     double sigmaXY, sigmaHat, sigmaX, sigmaY;
00485     sigmaXY = sigmaHat = sigmaX = sigmaY = 0.;
00486     p_iteration = 0;
00487 
00488     while (p_iteration < maxIterations) {
00489       p_iteration++;
00490       p_cnet->ComputeErrors();
00491       p_error = p_cnet->MaximumError();
00492       averageError = p_cnet->AverageError();
00493       if (p_printSummary) {
00494         IterationSummary(averageError,sigmaXY,sigmaHat,sigmaX,sigmaY);
00495       }
00496       p_statx.Reset();
00497       p_staty.Reset();
00498 
00499       if (p_error <= tol) return p_error;
00500 
00501       // Create the basis function and prep for a least squares solution
00502       BasisFunction basis("Bundle",BasisColumns(),BasisColumns());
00503       LeastSquares *lsq;
00504       if (p_solutionMethod == "SPARSE") {
00505         lsq = new LeastSquares(basis,Isis::LeastSquares::SPARSE,
00506                                p_cnet->NumValidMeasures()*2,BasisColumns());
00507       }
00508       else {
00509         lsq = new LeastSquares(basis);
00510       }
00511 
00512       // Loop through the control net and add the partials for each point
00513       for (int i=0; i<p_cnet->Size(); i++) {
00514         AddPartials(*lsq,i);
00515       }
00516 
00517       // Try to solve the iteration
00518       try {
00519         if (p_solutionMethod == "SVD") {
00520           lsq->Solve(Isis::LeastSquares::SVD);
00521 
00522         } else if (p_solutionMethod == "QRD") {
00523           lsq->Solve(Isis::LeastSquares::QRD);
00524         }
00525         else {
00526           int zeroColumn = lsq->Solve(Isis::LeastSquares::SPARSE);
00527           if (zeroColumn != 0) {
00528             std::string msg;
00529             int imageColumns = Observations() * p_numImagePartials;
00530             if (zeroColumn <= imageColumns) {
00531               msg = "Solution matrix has a column of zeros which probably ";
00532               msg += "indicates an image with no points.  Running the program, ";
00533               msg += "cnetcheck, before jigsaw should catch these problems.";
00534             }
00535             else {
00536               msg = "Solution matrix has a column of zeros which probably ";
00537               msg += "indicates a point with no measures.  Running the program, ";
00538               msg += "cnetcheck, before jigsaw should catch these problems.";
00539             }
00540             throw Isis::iException::Message(iException::Math,msg,_FILEINFO_);
00541           }
00542         }
00543       }
00544       catch (iException &e) {
00545         std::string msg = "Unable to solve in BundleAdjust, ";
00546         msg += "Iteration " + Isis::iString(p_iteration) + " of ";
00547         msg += Isis::iString(maxIterations) + ", Tolerance = ";
00548         msg += Isis::iString(tol);
00549         throw Isis::iException::Message(iException::Math,msg,_FILEINFO_);
00550       }
00551 
00552       // Ok take the results and put them back into the camera blobs
00553       Update(basis);
00554 //      return p_error;
00555 
00556       //Compute sigmas
00557       sigmaXY = sqrt((p_statx.SumSquare() + p_staty.SumSquare())/lsq->Knowns());
00558       sigmaHat = (lsq->Knowns() - BasisColumns()) ? 
00559         (sqrt((p_statx.SumSquare() + p_staty.SumSquare())/ (lsq->Knowns() - BasisColumns())))
00560         : 0.;
00561       sigmaX = p_statx.TotalPixels() ? 
00562         sqrt(p_statx.SumSquare()/p_statx.TotalPixels()) : 0.;
00563       sigmaY = p_staty.TotalPixels() ? 
00564         sqrt(p_staty.SumSquare()/p_staty.TotalPixels()) : 0.;
00565     }
00566 
00567     std::string msg = "Did not converge to tolerance [";
00568     msg += iString(tol) + "] in less than [";
00569     msg += iString(maxIterations) + "] iterations";
00570     throw iException::Message(iException::User,msg,_FILEINFO_);
00571   }
00572 
00576   void BundleAdjust::AddPartials (LeastSquares &lsq,
00577                                   int pointIndex) {
00578     ControlPoint &point = (*p_cnet)[pointIndex];
00579     if (point.Ignore()) return;  // Ignore entire point
00580 
00581     for (int i=0; i<point.Size(); i++) {
00582       if (point[i].Ignore()) continue;  // Ignore this measure
00583 
00584       if (p_heldImages) {
00585         if (p_heldsnlist->HasSerialNumber(point[i].CubeSerialNumber())) continue;
00586       }
00587 
00588       Camera *cam = point[i].Camera();
00589       // Get focal length with direction
00590       double fl = cam->DistortionMap()->UndistortedFocalPlaneZ();
00591       // Map the control point lat/lon/radius into the camera through the Spice
00592       // at the measured point to correctly compute the partials for line scan
00593       // cameras.  The camera SetUniversalGround method computes a time based
00594       // on the lat/lon/radius and uses the Spice for that time instead of the
00595       // measured point's time.
00596       cam->SetImage(point[i].Sample(),point[i].Line());  // Set the Spice to the measured point
00597 
00598       // Compute the look vector in body-fixed coordinates
00599       double pB[3]; // Point on surface
00600       latrec_c( point.Radius() / 1000.0,
00601                (point.UniversalLongitude() * Isis::PI / 180.0),
00602                (point.UniversalLatitude() * Isis::PI / 180.0),
00603                pB);
00604 
00605       // May need to do back of planet test here TODO 
00606       std::vector<double> lookB(3);
00607       SpiceRotation *bodyRot = cam->BodyRotation();
00608       std::vector<double> sB(3); // Spacecraft vector in bodyj-fixed coordinates
00609       sB = bodyRot->ReferenceVector(cam->InstrumentPosition()->Coordinate());
00610       for (int ic=0; ic<3; ic++)   lookB[ic] = pB[ic] - sB[ic];
00611       
00612 //      if (!cam->SetUniversalGround(point.UniversalLatitude(),
00613 //                                   point.UniversalLongitude(),
00614 //                                   point.Radius())) {
00615 //         std::string msg = "Point off image (lat/lon = ";
00616 //         msg += "Isis::iString(point.UniversalLatitude()/";
00617 //         msg += "Isis::iString(point.UniversalLongitude() - point may be too ";
00618 //         msg += "close to the edge on point Isis::iString(pointIndex) and ";
00619 //         msg += "Measure Isis::iString(i)";
00620 //         throw iException::Message(iException::User,msg,_FILEINFO_);
00621 //         exit(0);
00622 //      }
00623 
00624       // Create the known array to put in the least squares
00625       std::vector<double> xKnowns(BasisColumns(),0.0);
00626       std::vector<double> yKnowns(BasisColumns(),0.0);
00627 
00628       // Get the look vector in the camera frame and the instrument rotation
00629       std::vector<double> lookC(3);
00630       std::vector<double> lookJ(3);
00631       lookJ = cam->BodyRotation()->J2000Vector( lookB );
00632       SpiceRotation *instRot = cam->InstrumentRotation();
00633       lookC = instRot->ReferenceVector( lookJ);
00634 
00635 //      cam->LookDirection(((double *)&lookC[0]));  // From computed position
00636 
00637       // Determine the image index for nonheld images
00638       bool useImage=false;
00639       if ( p_heldImages == 0) { 
00640         useImage = true;
00641       }
00642       else if (p_heldImages > 0) {
00643         if ((!(p_heldsnlist->HasSerialNumber(point[i].CubeSerialNumber())))) 
00644           useImage = true;
00645       }
00646       if (useImage) {
00647         int index = p_snlist->SerialNumberIndex(point[i].CubeSerialNumber());
00648         index = ImageIndex(index);
00649 
00650         if (p_spacecraftPositionSolveType != Nothing) {
00651           SpicePosition *instPos = cam->InstrumentPosition();
00652 
00653           // Add the partial for the x coordinate of the position (differentiating
00654           // point(x,y,z) - spacecraftPosition(x,y,z) in J2000
00655           std::vector<double> d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_X0);
00656           for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00657           std::vector<double> d_lookC_WRT_X0 =  instRot->ReferenceVector(d_lookJ);
00658           xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X0,0);
00659           yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X0,1);
00660           index++;
00661 
00662           if (p_spacecraftPositionSolveType > PositionOnly) {
00663             d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_X1);
00664             for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00665             std::vector<double> d_lookC_WRT_X1 =  instRot->ReferenceVector(d_lookJ);
00666             xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X1,0);
00667             yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X1,1); index++;
00668 
00669             if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00670               d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_X2);
00671               for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00672               std::vector<double> d_lookC_WRT_X2 =  instRot->ReferenceVector(d_lookJ);
00673               xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X2,0);
00674               yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_X2,1); index++;
00675             }
00676           }
00677 
00678           // Add the partial for the y coordinate of the position
00679           d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Y0);
00680           for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00681           std::vector<double> d_lookC_WRT_Y0 =  instRot->ReferenceVector(d_lookJ);
00682           xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y0,0);
00683           yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y0,1);
00684           index++;
00685 
00686           if (p_spacecraftPositionSolveType > PositionOnly) {
00687              d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Y1);
00688              for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00689              std::vector<double> d_lookC_WRT_Y1 =  instRot->ReferenceVector(d_lookJ);
00690              xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y1,0);
00691              yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y1,1); index++;
00692 
00693             if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00694               d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Y2);
00695               for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00696               std::vector<double> d_lookC_WRT_Y2 =  instRot->ReferenceVector(d_lookJ);
00697               xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y2,0);
00698               yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Y2,1); index++;
00699             }
00700           }
00701 
00702           // Add the partial for the z coordinate of the position
00703           d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Z0);
00704           for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00705           std::vector<double> d_lookC_WRT_Z0 =  instRot->ReferenceVector(d_lookJ);
00706           xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z0,0);
00707           yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z0,1);
00708           index++;
00709 
00710           if (p_spacecraftPositionSolveType > PositionOnly) {
00711             d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Z1);
00712             for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00713             std::vector<double> d_lookC_WRT_Z1 =  instRot->ReferenceVector(d_lookJ);
00714             xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z1,0);
00715             yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z1,1); index++;
00716 
00717             if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00718               d_lookJ = instPos->CoordinatePartial (SpicePosition::WRT_Z2);
00719               for (int j=0; j<3; j++) d_lookJ[j] *= -1.0;
00720               std::vector<double> d_lookC_WRT_Z2 =  instRot->ReferenceVector(d_lookJ);
00721               xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z2,0);
00722               yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_Z1,1); index++;
00723             }
00724           }
00725         }
00726         if (p_cmatrixSolveType != None) {
00727           std::vector<double> d_lookC;
00728 
00729           // Add the partials for ra
00730           for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00731             d_lookC = instRot->ToReferencePartial(lookJ,SpiceRotation::WRT_RightAscension, icoef);
00732             xKnowns[index] = fl * LowDHigh(lookC,d_lookC,0);
00733             yKnowns[index] = fl * LowDHigh(lookC,d_lookC,1);
00734             index++;
00735           }
00736 
00737           // Add the partials for dec
00738           for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00739             d_lookC = instRot->ToReferencePartial(lookJ,SpiceRotation::WRT_Declination, icoef);
00740             xKnowns[index] = fl * LowDHigh(lookC,d_lookC,0);
00741             yKnowns[index] = fl * LowDHigh(lookC,d_lookC,1);
00742             index++;
00743           }
00744 
00745           // Add the partial for twist if necessary
00746           if (p_solveTwist) {
00747             for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00748               d_lookC = instRot->ToReferencePartial(lookJ,SpiceRotation::WRT_Twist, icoef);
00749               xKnowns[index] = fl * LowDHigh(lookC,d_lookC,0);
00750               yKnowns[index] = fl * LowDHigh(lookC,d_lookC,1); index++;
00751             }
00752           }
00753         }
00754       }
00755       if ((!point.Held()) && (point.Type() != ControlPoint::Ground)) {
00756         std::vector<double> d_lookB_WRT_LAT = PointPartial(point,WRT_Latitude);
00757         std::vector<double> d_lookB_WRT_LON = PointPartial(point,WRT_Longitude);
00758 
00759         SpiceRotation *bodyRot = cam->BodyRotation();
00760         std::vector<double> d_lookJ_WRT_LAT = bodyRot->J2000Vector(d_lookB_WRT_LAT);
00761         std::vector<double> d_lookJ_WRT_LON = bodyRot->J2000Vector(d_lookB_WRT_LON);
00762         std::vector<double> d_lookC_WRT_LAT = instRot->ReferenceVector(d_lookJ_WRT_LAT);
00763         std::vector<double> d_lookC_WRT_LON = instRot->ReferenceVector(d_lookJ_WRT_LON);
00764 
00765         int index = PointIndex(pointIndex);
00766         xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_LAT,0);
00767         yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_LAT,1);
00768         index++;
00769 
00770         xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_LON,0);
00771         yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_LON,1);
00772         index++;
00773 
00774         if (p_solveRadii) {
00775           std::vector<double> d_lookB_WRT_RAD = PointPartial(point,WRT_Radius);
00776           std::vector<double> d_lookJ_WRT_RAD = bodyRot->J2000Vector(d_lookB_WRT_RAD);
00777           std::vector<double> d_lookC_WRT_RAD = instRot->ReferenceVector(d_lookJ_WRT_RAD);
00778           xKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_RAD,0);
00779           yKnowns[index] = fl * LowDHigh(lookC,d_lookC_WRT_RAD,1);
00780           index++;
00781         }
00782       }
00783 
00784       double mudx = point[i].FocalPlaneMeasuredX();
00785 //      double cudx = point[i].FocalPlaneComputedX();
00786       double cudx = lookC[0] * fl / lookC[2];
00787       double mudy = point[i].FocalPlaneMeasuredY();
00788 //      double cudy = point[i].FocalPlaneComputedY();
00789       double cudy = lookC[1] * fl / lookC[2];
00790 
00791       double deltax = mudx - cudx;
00792       double deltay = mudy - cudy;
00793 
00794 /*      if (cam->DetectorMap()->LineRate() != 0.0) {
00795         if ( cam->DetectorMap()->IsYAxisTimeDependent() ) {
00796           deltay = point[i].MeasuredEphemerisTime() - point[i].ComputedEphemerisTime();
00797           deltay = deltay / cam->DetectorMap()->LineRate();    // Convert to pixels
00798           deltay = deltay * cam->PixelPitch();  // convert to mm
00799         }
00800         if (cam->DetectorMap()->IsXAxisTimeDependent() ) {
00801           deltax = point[i].MeasuredEphemerisTime() - point[i].ComputedEphemerisTime();
00802           deltax = deltax / cam->DetectorMap()->LineRate();    // Convert to pixels
00803           deltax = deltax * cam->PixelPitch();  // convert to mm
00804         }
00805       }
00806 
00807 //      deltay *= cam->DetectorMap()->YAxisDirection();
00808 //      deltax *= cam->DetectorMap()->XAxisDirection();*/
00809 
00810       lsq.AddKnown(xKnowns,deltax);
00811       lsq.AddKnown(yKnowns,deltay);
00812       p_statx.AddData(deltax);
00813       p_staty.AddData(deltay);
00814     }
00815   }
00816 
00820   double BundleAdjust::LowDHigh(std::vector<double> &lookC,
00821                               std::vector<double> &dlookC,
00822                               int index) {
00823     return (lookC[2] * dlookC[index] - lookC[index] * dlookC[2]) /
00824            (lookC[2] * lookC[2]);
00825   }
00826 
00827 
00837   void BundleAdjust::Update (BasisFunction &basis) {
00838     // Update selected spice for each image
00839     for (int i=0; i<Images(); i++) {
00840       if (p_heldImages > 0) {
00841         if ((p_heldsnlist->HasSerialNumber(p_snlist->SerialNumber(i)))) continue;
00842       }
00843 
00844       Camera *cam = p_cnet->Camera(i);
00845       int index = i;
00846       index = ImageIndex(index);
00847       if (p_spacecraftPositionSolveType != Nothing) {
00848         SpicePosition *pos = cam->InstrumentPosition();
00849         std::vector<double> abcX(3),abcY(3),abcZ(3);
00850         pos->GetPolynomial(abcX,abcY,abcZ);
00851 
00852         // Update the X coordinate coefficient(s)
00853         abcX[0] += basis.Coefficient(index); index++;
00854         if (p_spacecraftPositionSolveType > PositionOnly) {
00855           abcX[1] += basis.Coefficient(index); index++;
00856           if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00857             abcX[2] += basis.Coefficient(index); index++;
00858           }
00859         }
00860 
00861         // Update the Y coordinate coefficient(s)
00862         abcY[0] += basis.Coefficient(index); index++;
00863         if (p_spacecraftPositionSolveType > PositionOnly) {
00864           abcY[1] += basis.Coefficient(index); index++;
00865           if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00866             abcY[2] += basis.Coefficient(index); index++;
00867           }
00868         }
00869 
00870         // Update the Z coordinate cgoefficient(s)
00871         abcZ[0] += basis.Coefficient(index); index++;
00872         if (p_spacecraftPositionSolveType > PositionOnly) {
00873           abcZ[1] += basis.Coefficient(index); index++;
00874           if (p_spacecraftPositionSolveType == PositionVelocityAcceleration) {
00875             abcZ[2] += basis.Coefficient(index); index++;
00876           }
00877         }
00878         pos->SetPolynomial(abcX,abcY,abcZ);
00879       }
00880 
00881       if (p_cmatrixSolveType != None) {
00882         SpiceRotation *rot = cam->InstrumentRotation();
00883          std::vector<double> coefRA(p_numberCameraCoefSolved),
00884                              coefDEC(p_numberCameraCoefSolved),
00885                              coefTWI(p_numberCameraCoefSolved);
00886          rot->GetPolynomial(coefRA,coefDEC,coefTWI);
00887 
00888         // Update right ascension coefficient(s)
00889         for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00890           coefRA[icoef] += basis.Coefficient(index); index++;
00891         }
00892 
00893         // Update declination coefficient(s)
00894         for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00895           coefDEC[icoef] += basis.Coefficient(index); index++;
00896         }
00897 
00898         if (p_solveTwist) {
00899           // Update twist coefficient(s)
00900           for (int icoef=0; icoef<p_numberCameraCoefSolved; icoef++) {
00901             coefTWI[icoef] += basis.Coefficient(index); index++;
00902           }
00903         }
00904 
00905         rot->SetPolynomial(coefRA,coefDEC,coefTWI);
00906       }
00907     }
00908 
00909     // Update lat/lon for each control point
00910     for (int i=0; i<p_cnet->Size(); i++) {
00911       if ((*p_cnet)[i].Held()) continue;
00912       if ((*p_cnet)[i].Ignore()) continue;
00913       if ((*p_cnet)[i].Type() == ControlPoint::Ground) continue;
00914 
00915       double lat = (*p_cnet)[i].UniversalLatitude();
00916       double lon = (*p_cnet)[i].UniversalLongitude();
00917       double rad = (*p_cnet)[i].Radius();
00918       int index = PointIndex(i);
00919       lat += (180.0 / Isis::PI) * (basis.Coefficient(index)); index++;
00920       lon += (180.0 / Isis::PI) * (basis.Coefficient(index)); index++;
00921       // Make sure updated values are still in valid range.  
00922       // TODO What is the valid lon range?
00923       if (lat < -90.) {
00924         lat = -180. - lat;
00925         lon = lon + 180.;
00926       }
00927       if (lat > 90.) {
00928         lat = 180. - lat;
00929         lon = lon + 180.;
00930       }
00931       while (lon > 360.) lon = lon - 360.;
00932       while (lon < 0) lon = lon + 360.;
00933 
00934       if (p_solveRadii) {
00935         rad += 1000.*basis.Coefficient(index); index++;
00936       }
00937 /*      else {  // Recompute radius to match updated lat/lon... Should this be removed?
00938         ControlMeasure &m = ((*p_cnet)[i])[0];
00939         Camera *cam = m.Camera();
00940         cam->SetUniversalGround(lat, lon);
00941         rad = cam->LocalRadius(); //meters
00942      }*/
00943       (*p_cnet)[i].SetUniversalGround(lat,lon,rad);
00944     }
00945   }
00946 
00948   int BundleAdjust::PointIndex (int i) const {
00949     int index;
00950 
00951     if (!p_observationMode) {
00952       index = (Images() - p_heldImages) * p_numImagePartials; 
00953     }
00954     else {
00955       index = Observations() * p_numImagePartials;
00956     }
00957 
00958     index += p_pointIndexMap[i] * p_numPointPartials;
00959     return index;
00960   }
00961 
00963   int BundleAdjust::ImageIndex (int i) const {
00964     if (!p_observationMode) {
00965       return p_imageIndexMap[i] * p_numImagePartials;
00966     }
00967     else {
00968       return p_onlist->ObservationNumberMapIndex(i) * p_numImagePartials;
00969     }
00970   }
00971 
00972 
00974   std::string BundleAdjust::Filename(int i) {
00975 //    std::string serialNumber = (*p_snlist)[i];
00976 //    return p_snlist->Filename(serialNumber);
00977     return p_snlist->Filename(i);
00978   }
00979 
00982   Table BundleAdjust::Cmatrix(int i) {
00983     return p_cnet->Camera(i)->InstrumentRotation()->Cache("InstrumentPointing");
00984   }
00985 
00988   Table BundleAdjust::SpVector(int i) {
00989     return p_cnet->Camera(i)->InstrumentPosition()->Cache("InstrumentPosition");
00990   }
00991 
00993   int BundleAdjust::Images () const {
00994     return p_snlist->Size();
00995   }
00996 
00998   int BundleAdjust::Observations () const {
00999     if ( !p_observationMode ) {
01000       return p_snlist->Size() - p_heldImages;
01001     }
01002     else {
01003       return p_onlist->ObservationSize();
01004     }
01005   }
01006 
01007   std::vector<double> BundleAdjust::PointPartial(Isis::ControlPoint &point, PartialDerivative wrt) {
01008     double lat = point.UniversalLatitude() * Isis::PI / 180.0;
01009     double lon = point.UniversalLongitude() * Isis::PI / 180.0;
01010     double sinLon = sin(lon);
01011     double cosLon = cos(lon);
01012     double sinLat = sin(lat);
01013     double cosLat = cos(lat);
01014     double radius = point.Radius() / 1000.0;
01015 
01016     std::vector<double> v(3);
01017     if (wrt == WRT_Latitude) {
01018       v[0] = -radius * sinLat * cosLon;
01019       v[1] = -radius * sinLon * sinLat;
01020       v[2] =  radius * cosLat;
01021     }
01022     else if (wrt == WRT_Longitude) {
01023       v[0] = -radius * cosLat * sinLon;
01024       v[1] =  radius * cosLat * cosLon;
01025       v[2] =  0.0;
01026     }
01027     else {
01028       v[0] = cosLon * cosLat;
01029       v[1] = sinLon * cosLat;
01030       v[2] = sinLat;
01031     }
01032 
01033     return v;
01034   }
01035 
01036 
01054    void BundleAdjust::IterationSummary(double avErr, double sigmaXY, double sigmaHat, 
01055                                          double sigmaX, double sigmaY) {
01056      //Add this iteration to the summary pvl
01057      std::string itlog = "Iteration" + iString( p_iteration );
01058      PvlGroup gp( itlog );
01059      gp += PvlKeyword("MaximumError", p_error, "pixels");
01060      gp += PvlKeyword("AverageError", avErr, "pixels");
01061      gp += PvlKeyword("SigmaXY", sigmaXY, "mm");
01062      gp += PvlKeyword("SigmaHat", sigmaHat, "mm");
01063      gp += PvlKeyword("SigmaX", sigmaX, "mm");
01064      gp += PvlKeyword("SigmaY", sigmaY, "mm");
01065 
01066      Application::Log( gp );
01067    }
01068 
01069 }