Isis 3.0 Programmer Reference
Back | Home
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:86
CameraDetectorMap * DetectorMap()
Returns a pointer to the CameraDetectorMap object.
Definition: Camera.cpp:2906
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.
double SlantDistance() const
Return the distance between the spacecraft and surface point in kmv.
Definition: Sensor.cpp:625
This class is designed to encapsulate the concept of a Latitude.
Definition: Latitude.h:59
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
double FocalLength() const
Returns the focal length.
Definition: Camera.cpp:2794
Distance measurement, usually in meters.
Definition: Distance.h:47
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition: iTime.h:135
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
iTime time() const
Returns the ephemeris time in seconds which was used to obtain the spacecraft and sun positions...
Definition: Spice.cpp:804
Convert between undistorted focal plane and ground coordinates.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
virtual ~LineScanCameraGroundMap()
Destructor.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Distance LocalRadius() const
Returns the local radius at the intersection point.
Definition: Sensor.cpp:282
Isis exception class.
Definition: IException.h:99
Adds specific functionality to C++ strings.
Definition: IString.h:179
int ParentSamples() const
Returns the number of samples in the parent alphacube.
Definition: Camera.cpp:2876
Convert between parent image coordinates and detector coordinates.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:22:14