Isis 3 Programmer Reference
LineScanCameraGroundMap.cpp
Go to the documentation of this file.
1 
25 
26 #include <iostream>
27 #include <iomanip>
28 
29 #include <QTime>
30 #include <QList>
31 #include <QFile>
32 #include <QTextStream>
33 
34 #include "IException.h"
35 #include "IString.h"
36 #include "Camera.h"
37 #include "CameraDistortionMap.h"
38 #include "CameraFocalPlaneMap.h"
39 #include "Distance.h"
41 #include "iTime.h"
42 #include "Latitude.h"
43 #include "Longitude.h"
44 #include "Statistics.h"
45 #include "SurfacePoint.h"
46 #include "FunctionTools.h"
47 
48 
49 using namespace std;
50 using namespace Isis;
51 
52 bool ptXLessThan(const QList<double> l1, const QList<double> l2);
53 
60  public std::unary_function<double, double > {
61  public:
62 
63  LineOffsetFunctor(Isis::Camera *camera, const Isis::SurfacePoint &surPt) {
64  m_camera = camera;
65  m_surfacePoint = surPt;
66  }
67 
68 
69  ~LineOffsetFunctor() {}
70 
71 
80  double operator()(double et) {
81  double lookC[3] = {0.0, 0.0, 0.0};
82  double ux = 0.0;
83  double uy = 0.0;
84  double dx = 0.0;
85  double dy = 0.0;
86 
87  // Verify the time is with the cache bounds
88  double startTime = m_camera->cacheStartTime().Et();
89  double endTime = m_camera->cacheEndTime().Et();
90  if (et < startTime || et > endTime) {
91  IString msg = "Ephemeris time passed to LineOffsetFunctor is not within the image "
92  "cache bounds";
93  throw IException(IException::Programmer, msg, _FILEINFO_);
94  }
95 
96  m_camera->Sensor::setTime(et);
97 
98  // Set ground
99  if (!m_camera->Sensor::SetGround(m_surfacePoint, false)) {
100  IString msg = "Sensor::SetGround failed for surface point in LineScanCameraGroundMap.cpp"
101  " LineOffsetFunctor";
102  throw IException(IException::Programmer, msg, _FILEINFO_);
103  }
104 
105  // Calculate the undistorted focal plane coordinates
106  m_camera->Sensor::LookDirection(lookC);
107  ux = m_camera->FocalLength() * lookC[0] / lookC[2];
108  uy = m_camera->FocalLength() * lookC[1] / lookC[2];
109 
110 
111  // This was replaced with the code below to get Chandrayaan M3 to work.
112  // SetUndistortedFocalPlane was failing a majority of the time, causing most SetGround calls
113  // to fail. Even when it did succeed, it was producing non-continous return values.
114  // Set the undistorted focal plane coordinates
115  // if (!m_camera->DistortionMap()->SetUndistortedFocalPlane(ux, uy)) {
116  // IString msg = "DistortionMap::SetUndistoredFocalPlane failed for surface point in "
117  // "LineScanCameraGroundMap.cpp LineOffsetFunctor";
118  // throw IException(IException::Programmer, msg, _FILEINFO_);
119  // }
120 
121  // Get the natural (distorted focal plane coordinates)
122  // dx = m_camera->DistortionMap()->FocalPlaneX();
123  // dy = m_camera->DistortionMap()->FocalPlaneY();
124  // std::cout << "use dist" << std::endl;
125  //}
126 
127 
128  // Try to use SetUndistortedFocalPlane, if that does not work use the distorted x,y
129  // under the assumption (bad|good) that extrapolating the distortion
130  // is causing the distorted x,y to be way off the sensor, and thus not very good anyway.
131  if (m_camera->DistortionMap()->SetUndistortedFocalPlane(ux, uy)) {
132  // Get the natural (distorted focal plane coordinates)
133  dx = m_camera->DistortionMap()->FocalPlaneX();
134  dy = m_camera->DistortionMap()->FocalPlaneY();
135  }
136  else {
137  dx = ux;
138  dy = uy;
139  }
140 
141  if (!m_camera->FocalPlaneMap()->SetFocalPlane(dx, dy)) {
142  IString msg = "FocalPlaneMap::SetFocalPlane failed for surface point in "
143  "LineScanCameraGroundMap.cpp LineOffsetFunctor";
144  throw IException(IException::Programmer, msg, _FILEINFO_);
145  }
146 
147  // Return the offset
148  return (m_camera->FocalPlaneMap()->DetectorLineOffset() -
149  m_camera->FocalPlaneMap()->DetectorLine());
150  }
151 
152 
153  private:
154  SurfacePoint m_surfacePoint;
155  Camera* m_camera;
156 };
157 
158 
165  public std::unary_function<double, double > {
166 
167  public:
169  m_camera = camera;
170  surfacePoint = surPt;
171  }
172 
173 
175 
176 
177  double operator()(double et) {
178  double s[3], p[3];
179 
180  //verify the time is with the cache bounds
181  double startTime = m_camera->cacheStartTime().Et();
182  double endTime = m_camera->cacheEndTime().Et();
183 
184  if (et < startTime || et > endTime) {
185  IString msg = "Ephemeris time passed to SensorSurfacePointDistanceFunctor is not within the image "
186  "cache bounds";
187  throw IException(IException::Programmer, msg, _FILEINFO_);
188  }
189  m_camera->Sensor::setTime(et);
190  if (!m_camera->Sensor::SetGround(surfacePoint, false)) {
191  IString msg = "Sensor::SetGround failed for surface point in LineScanCameraGroundMap.cpp"
192  "SensorSurfacePointDistanceFunctor";
193  }
194  m_camera->instrumentPosition(s);
195  m_camera->Coordinate(p);
196  return sqrt((s[0] - p[0]) * (s[0] - p[0]) +
197  (s[1] - p[1]) * (s[1] - p[1]) +
198  (s[2] - p[2]) * (s[2] - p[2]) ); //distance
199  }
200 
201  private:
202  SurfacePoint surfacePoint;
203  Camera* m_camera;
204 };
205 
206 
207 namespace Isis {
208 
213  LineScanCameraGroundMap::LineScanCameraGroundMap(Camera *cam) : CameraGroundMap(cam) {}
214 
215 
220 
229  const Longitude &lon) {
230  Distance radius(p_camera->LocalRadius(lat, lon));
231 
232  if (radius.isValid()) {
233  return SetGround(SurfacePoint(lat, lon, radius));
234  }
235  else {
236  return false;
237  }
238  }
239 
240 
249  bool LineScanCameraGroundMap::SetGround(const SurfacePoint &surfacePoint, const int &approxLine) {
250  FindFocalPlaneStatus status = FindFocalPlane(approxLine, surfacePoint);
251  if (status == Success) return true;
252  //if(status == Failure) return false;
253  return false;
254  }
255 
256 
264  FindFocalPlaneStatus status = FindFocalPlane(-1, surfacePoint);
265 
266  if (status == Success) return true;
267 
268  return false;
269  }
270 
271 
272  double LineScanCameraGroundMap::FindSpacecraftDistance(int line,
273  const SurfacePoint &surfacePoint) {
274 
275  CameraDetectorMap *detectorMap = p_camera->DetectorMap();
276  detectorMap->SetParent(p_camera->ParentSamples() / 2, line);
277  if (!p_camera->Sensor::SetGround(surfacePoint, false)) {
278  return DBL_MAX;
279  }
280 
281  return p_camera->SlantDistance();
282  }
283 
284 
285  LineScanCameraGroundMap::FindFocalPlaneStatus
286  LineScanCameraGroundMap::FindFocalPlane(const int &approxLine,
287  const SurfacePoint &surfacePoint) {
288 
289  //CameraDistortionMap *distortionMap = p_camera->DistortionMap();
290  //CameraFocalPlaneMap *focalMap = p_camera->FocalPlaneMap();
291 
292  double approxTime = 0;
293  double approxOffset = 0;
294  double lookC[3] = {0.0, 0.0, 0.0};
295  double ux = 0.0;
296  double uy = 0.0;
297  //double dx = 0.0, dy = 0.0;
298  //double s[3], p[3];
299  const double cacheStart = p_camera->Spice::cacheStartTime().Et();
300  const double cacheEnd = p_camera->Spice::cacheEndTime().Et();
301 
302  double lineRate = ((LineScanCameraDetectorMap *)p_camera->DetectorMap())->LineRate(); //line rate
303 
304  if (lineRate == 0.0) return Failure;
305 
306  LineOffsetFunctor offsetFunc(p_camera,surfacePoint);
307  SensorSurfacePointDistanceFunctor distanceFunc(p_camera,surfacePoint);
308 
309  // METHOD #1
310  // Use the line given as a start point for the secant method root search.
311  if (approxLine >= 0.5) {
312 
313  // convert the approxLine to an approximate time and offset
314  p_camera->DetectorMap()->SetParent(p_camera->ParentSamples() / 2.0, approxLine);
315  approxTime = p_camera->time().Et();
316 
317  approxOffset = offsetFunc(approxTime);
318 
319  // Check to see if there is no need to improve this root, it's good enough
320  if (fabs(approxOffset) < 1e-2) {
321  p_camera->Sensor::setTime(approxTime);
322  // check to make sure the point isn't behind the planet
323  if (!p_camera->Sensor::SetGround(surfacePoint, true)) {
324  return Failure;
325  }
326  p_camera->Sensor::LookDirection(lookC);
327  ux = p_camera->FocalLength() * lookC[0] / lookC[2];
328  uy = p_camera->FocalLength() * lookC[1] / lookC[2];
329 
330  p_focalPlaneX = ux;
331  p_focalPlaneY = uy;
332 
333  return Success;
334  }
335 
336  double fl, fh, xl, xh;
337 
338  // starting times for the secant method, kept within the domain of the cache
339  xh = approxTime;
340  if (xh + lineRate < cacheEnd) {
341  xl = xh + lineRate;
342  }
343  else {
344  xl = xh - lineRate;
345  }
346 
347  // starting offsets
348  fh = approxOffset; //the first is already calculated
349  fl = offsetFunc(xl);
350 
351  // Iterate to refine the given approximate time that the instrument imaged the ground point
352  for (int j=0; j < 10; j++) {
353  if (fl-fh == 0.0) {
354  return Failure;
355  }
356  double etGuess = xl + (xh - xl) * fl / (fl - fh);
357 
358  if (etGuess < cacheStart) etGuess = cacheStart;
359  if (etGuess > cacheEnd) etGuess = cacheEnd;
360 
361  double f = offsetFunc(etGuess);
362 
363 
364  // elliminate the node farthest away from the current best guess
365  if (fabs( xl- etGuess) > fabs( xh - etGuess)) {
366  xl = etGuess;
367  fl = f;
368  }
369  else {
370  xh = etGuess;
371  fh = f;
372  }
373 
374  // See if we converged on the point so set up the undistorted focal plane values and return
375  if (fabs(f) < 1e-2) {
376  p_camera->Sensor::setTime(approxTime);
377  // check to make sure the point isn't behind the planet
378  if (!p_camera->Sensor::SetGround(surfacePoint, true)) {
379  return Failure;
380  }
381  p_camera->Sensor::LookDirection(lookC);
382  ux = p_camera->FocalLength() * lookC[0] / lookC[2];
383  uy = p_camera->FocalLength() * lookC[1] / lookC[2];
384 
385  p_focalPlaneX = ux;
386  p_focalPlaneY = uy;
387 
388  return Success;
389  }
390  } // End itteration using a guess
391  // return Failure; // Removed to let the lagrange method try to find the line if secant fails
392  } // End use a guess
393 
394 
395  // METHOD #2
396  // The guess or middle line did not work so try estimating with a quadratic
397  // The offsets are typically quadratic, so three points will be used to approximate a quadratic
398  // as a first order attempt to find the root location(s)
399 
400  // The three nodes to be used to approximate the quadratic
401  double offsetNodes[3];
402  double timeNodes[3];
403  double timeAverage;
404  double scale;
405  QList<double> root;
406  QList<double> offset;
407  QList<double> dist;
408 
409  timeNodes[0] = cacheStart;
410  timeNodes[2] = cacheEnd;
411  timeNodes[1] = (cacheStart+cacheEnd) / 2.0; // middle time
412 
413  double quadPoly[3];
414  double temp;
415 
416  for (int i=0; i<3; i++) {
417  offsetNodes[i] = offsetFunc(timeNodes[i]);
418  }
419 
420  // centralize and normalize the data for stability in root finding
421  timeAverage = (timeNodes[0] + timeNodes[1] + timeNodes[2]) / 3.0;
422  timeNodes[0] -= timeAverage;
423  timeNodes[1] -= timeAverage;
424  timeNodes[2] -= timeAverage;
425 
426  scale = 1.0 / sqrt((timeNodes[0] - timeNodes[2]) *
427  (timeNodes[0] - timeNodes[2]) +
428  (offsetNodes[0] - offsetNodes[2]) *
429  (offsetNodes[0] - offsetNodes[2]));
430 
431  timeNodes[0] *= scale;
432  timeNodes[1] *= scale;
433  timeNodes[2] *= scale;
434 
435  offsetNodes[0] *= scale;
436  offsetNodes[1] *= scale;
437  offsetNodes[2] *= scale;
438 
439  // Use lagrange interpolating polynomials to find the coefficients of the quadratic,
440  // there are many ways to do this; I chose to do it this way because it is pretty straight
441  // forward and cheap
442  quadPoly[0] = quadPoly[1] = quadPoly[2] = 0.0;
443 
444  temp = offsetNodes[0] / ((timeNodes[0] - timeNodes[1]) * (timeNodes[0] - timeNodes[2]));
445  quadPoly[0] += temp;
446  quadPoly[1] += temp * (-timeNodes[1] - timeNodes[2]);
447  quadPoly[2] += temp * timeNodes[1] * timeNodes[2];
448 
449  temp = offsetNodes[1] / ((timeNodes[1] - timeNodes[0]) * (timeNodes[1] - timeNodes[2]));
450  quadPoly[0] += temp;
451  quadPoly[1] += temp * (-timeNodes[0] - timeNodes[2]);
452  quadPoly[2] += temp * timeNodes[0] * timeNodes[2];
453 
454  temp = offsetNodes[2] / ((timeNodes[2] - timeNodes[0]) * (timeNodes[2] - timeNodes[1]));
455  quadPoly[0] += temp;
456  quadPoly[1] += temp * (-timeNodes[0] - timeNodes[1]);
457  quadPoly[2] += temp * timeNodes[0] * timeNodes[1];
458 
459  // Now that we have the coefficients of the quadratic look for roots
460  // (see Numerical Recipes Third Edition page 227)
461  temp = quadPoly[1] * quadPoly[1] - 4.0 * quadPoly[0] * quadPoly[2]; //discriminant
462 
463  // THIS IS A PREMATURE FAILURE RETURN. IT SHOULD TRY THE NEXT METHON BEFORE FAILING
464  if (temp < 0.0) {
465  return Failure; // there are apparently not any real roots on this image
466  }
467 
468  if (quadPoly[1] >= 0.0) {
469  temp = -0.5 * (quadPoly[1] + sqrt(temp));
470  }
471  else {
472  temp = -0.5 * (quadPoly[1] - sqrt(temp));
473  }
474 
475  if (quadPoly[0] != 0.0) {
476  root.push_back(temp/quadPoly[0]);
477  }
478 
479  if (quadPoly[2] != 0.0) {
480  root.push_back(quadPoly[2]/temp);
481  }
482 
483  // check to see if the roots are in the time interval of the cache
484  for (int i=root.size()-1; i>=0; i--) {
485  if ( root[i] < timeNodes[0] || root[i] > timeNodes[2] ) {
486  root.removeAt(i);
487  }
488  }
489 
490  // return the calculated roots to the original system
491  for (int i=0; i<root.size(); i++) {
492  root[i] = root[i]/scale + timeAverage;
493  }
494 
495  // THIS IS A PREMATURE FAILURE RETURN. IT SHOULD TRY THE NEXT METHON BEFORE FAILING
496  if (root.size() == 0) {
497  return Failure; // there are apparently not any roots on this image
498  }
499 
500  // At the time of this writing ISIS made no attempt to support any sensors that were not "1 to 1".
501  // Meaning they imaged the same point on the ground in multiple lines of the image
502  // therefore we must somehow reduce multiple possible roots to a single one, the legacy
503  // code (replaced with this code) did this based on distance from the sensor to the target
504  // the shortest distance being the winner. For legacy consistency I have used the same logic below.
505 
506  for (int i=0; i<root.size(); i++) { // Offset/dist calculation loop
507  dist << distanceFunc(root[i]);
508  offset << offsetFunc(root[i]);
509  }
510 
511  // Save the root with the smallest dist
512  {
513  int j=0;
514  for (int i=1; i<root.size(); i++) {
515  if (dist[i] < dist[j]) j=i;
516  }
517 
518  approxTime = root[j]; // Now we have our time
519  approxOffset = offset[j]; // The offsets are saved to avoid recalculating it later
520  }
521 
522  if (fabs(approxOffset) < 1.0e-2) { // No need to iteratively improve this root, it's good enough
523  p_camera->Sensor::setTime(approxTime);
524 
525  // Check to make sure the point isn't behind the planet
526  if (!p_camera->Sensor::SetGround(surfacePoint, true)) {
527  return Failure;
528  }
529 
530  p_camera->Sensor::LookDirection(lookC);
531  ux = p_camera->FocalLength() * lookC[0] / lookC[2];
532  uy = p_camera->FocalLength() * lookC[1] / lookC[2];
533 
534  p_focalPlaneX = ux;
535  p_focalPlaneY = uy;
536 
537  return Success;
538  }
539 
540 
541  // METHOD #3
542  // Estimated line and quadratic approximation insufficient, try Brent's method
543  // The offsets are typically quadratic, so three points will be used to approximate a quadratic
544  // as a first order attempt to find the root location(s)
545 
546  // The above sections are sufficient for finding the correct times for the vast majority of
547  // back projection solutions. The following section exists for those few particularly
548  // stuborn problems. They are typically characterised by being significantly non-quadratic.
549  // Examples mostly include images with very long exposure times.
550  //
551  // Further, while the preceeding sections is intended to be fast, this section is intended to be
552  // thurough. Brents method (Numerical Recipies 454 - 456) will be used to find all the roots,
553  // that are bracketed by the five points defined in the quadratic solution method above.
554  // The root with the shortest distance to the camera will be returned
555 
556  // Get everything ordered for iteration combine and sort the five already defined points
557  QList <QList <double> > pts;
558 
559  for (int i=0; i<3; i++) {
560  QList <double> pt;
561  pt << timeNodes[i] / scale + timeAverage;
562  pt << offsetNodes[i] / scale;
563  pts << pt;
564  }
565 
566  for (int i=0; i<root.size(); i++) {
567  QList <double> pt;
568  pt << root[i];
569  pt << offset[i];
570  pts << pt;
571  }
572 
573  qSort(pts.begin(), pts.end(), ptXLessThan);
574 
575  root.clear();
576  for (int i=1; i<pts.size(); i++) {
577  // If the signs of the two offsets are not the same they bracket at least one root
578  if ( (pts[i-1][1] > 0) - (pts[i-1][1] < 0) != (pts[i][1] > 0) - (pts[i][1] < 0) ) {
579  double temp;
580  if (FunctionTools::brentsRootFinder <LineOffsetFunctor> (offsetFunc, pts[i-1], pts[i],
581  1.0e-3, 200, temp)) {
582  root << temp;
583  }
584  }
585  }
586 
587  // Discard any roots that are looking through the planet
588  for (int i = root.size()-1; i>=0; i--) {
589  p_camera->Sensor::setTime(root[i]);
590  //check to make sure the point isn't behind the planet
591  if (!p_camera->Sensor::SetGround(surfacePoint, true)) {
592  root.removeAt(i);
593  }
594  }
595 
596  // If none of the roots remain...
597  if (root.size() == 0) {
598  return Failure;
599  }
600 
601  // Choose from the remaining roots, the solution with the smallest distance to target
602  dist.clear();
603  offset.clear();
604  for (int i=0; i<root.size(); i++) { // Offset/dist calculation loop
605  dist << distanceFunc(root[i]);
606  offset << offsetFunc(root[i]);
607  }
608 
609  // Save the root with the smallest dist
610  {
611  int j=0;
612  for (int i=1; i<root.size(); i++) {
613  if (dist[i] < dist[j]) j=i;
614  }
615 
616  p_camera->Sensor::setTime(root[j]);
617  }
618 
619  // No need to make sure the point isn't behind the planet, it was done above
620  p_camera->Sensor::LookDirection(lookC);
621  ux = p_camera->FocalLength() * lookC[0] / lookC[2];
622  uy = p_camera->FocalLength() * lookC[1] / lookC[2];
623 
624  p_focalPlaneX = ux;
625  p_focalPlaneY = uy;
626 
627  return Success;
628  }
629 }
630 
631 
632 bool ptXLessThan(const QList<double> l1, const QList<double> l2) {
633  return l1[0] < l2[0];
634 }
635 
This class defines a body-fixed surface point.
Definition: SurfacePoint.h:148
CameraDetectorMap * DetectorMap()
Returns a pointer to the CameraDetectorMap object.
Definition: Camera.cpp:2858
virtual bool SetParent(const double sample, const double line)
Compute detector position from a parent image coordinate.
Camera * p_camera
Camera.
double p_focalPlaneY
Camera&#39;s y focal plane coordinate.
double operator()(double et)
Compute the number of lines between the current line (i.e., the line imaged at the et as set in the c...
double p_focalPlaneX
Camera&#39;s x focal plane coordinate.
Namespace for the standard library.
This class is designed to encapsulate the concept of a Latitude.
Definition: Latitude.h:63
double SlantDistance() const
Return the distance between the spacecraft and surface point in kmv.
Definition: Sensor.cpp:648
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Distance measurement, usually in meters.
Definition: Distance.h:47
Distance LocalRadius() const
Returns the local radius at the intersection point.
Definition: Sensor.cpp:282
virtual bool SetGround(const Latitude &lat, const Longitude &lon)
Compute undistorted focal plane coordinate from ground position.
Convert between parent image coordinates and detector coordinates.
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
Convert between undistorted focal plane and ground coordinates.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
double FocalLength() const
Returns the focal length.
Definition: Camera.cpp:2744
int ParentSamples() const
Returns the number of samples in the parent alphacube.
Definition: Camera.cpp:2828
virtual ~LineScanCameraGroundMap()
Destructor.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Isis exception class.
Definition: IException.h:107
Adds specific functionality to C++ strings.
Definition: IString.h:181
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
Convert between parent image coordinates and detector coordinates.
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition: iTime.h:139
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
iTime time() const
Returns the ephemeris time in seconds which was used to obtain the spacecraft and sun positions...
Definition: Spice.cpp:809