Isis 3.0 Programmer Reference
Back | Home
RadarSlantRangeMap.cpp
Go to the documentation of this file.
1 
23 #include "RadarSlantRangeMap.h"
24 
25 #include "IString.h"
26 #include "iTime.h"
27 #include "PvlSequence.h"
28 
29 using namespace std;
30 
31 namespace Isis {
40  RadarSlantRangeMap::RadarSlantRangeMap(Camera *parent, double groundRangeResolution) :
41  CameraDistortionMap(parent, 1.0) {
42 
43  p_camera = parent;
44  p_et = DBL_MAX;
45  p_a[0] = p_a[1] = p_a[2] = p_a[3] = 0.0;
46 
47  // Need to come up with an initial guess when solving for ground
48  // range given slantrange. We will compute the ground range at the
49  // near and far edges of the image by evaluating the sample-to-
50  // ground-range equation: r_gnd=(S-1)*groundRangeResolution
51  // at the edges of the image. We also need to add some padding to
52  // allow for solving for coordinates that are slightly outside of
53  // the actual image area. Use S=-0.25*p_camera->Samples() and
54  // S=1.25*p_camera->Samples().
55  // The above approach works nicely for a level 1 image but the
56  // sensor model at level2 doesn't have access to the level 1
57  // number of samples. Instead, we will calculate initial guesses
58  // on the fly in SetUndistortedFocalPlane.
59  p_tolerance = 0.1; // Default tolerance is a tenth of a meter
60  p_maxIterations = 30;
61 
62  }
63 
67  bool RadarSlantRangeMap::SetFocalPlane(const double dx, const double dy) {
68  p_focalPlaneX = dx; // dx is a ground range distance in meters
69  p_focalPlaneY = dy; // dy is Doppler shift in htz and should always be 0
70 
71  if(p_et != p_camera->time().Et()) ComputeA();
72  double slantRange = p_a[0] + p_a[1] * dx + p_a[2] * dx * dx + p_a[3] * dx * dx * dx; // meters
73 
74  p_camera->SetFocalLength(slantRange);
75  p_undistortedFocalPlaneX = slantRange / p_rangeSigma;
76  p_undistortedFocalPlaneY = 0;
77 
78  return true;
79  }
80 
85  const double uy) {
86  p_undistortedFocalPlaneX = ux * p_rangeSigma; // ux converts to slant range in meters
87  p_undistortedFocalPlaneY = uy * p_dopplerSigma; // uy converts to Doppler shift in htz and should always be 0
88 
89  if(p_et != p_camera->time().Et()) ComputeA();
90 
91  double slant = p_undistortedFocalPlaneX;
92  // First trap the case where no iteration is needed. Since this occurs at
93  // the first pixel of the image, it's a real possibility to encounter.
94  if (fabs(slant-p_a[0]) < p_tolerance) {
95  p_focalPlaneX = 0.0;
96  p_focalPlaneY = 0.0;
97  return true;
98  }
99  // Now calculate two guesses by the first and second iterations of
100  // Newton's method, where the zeroth iteration is at ground range = 0.
101  // The nature of the slant range function is such that, in the region
102  // of validity (including all image data) the Newton approximations are
103  // always too high, but the second one is within a few meters. We therefore
104  // add 10 m of “windage” to it to bracket the root.
105  // Performing a third Newton iteration would give a satisfactory solution in
106  // the data area but raises the problem of trapping errors outside this region
107  // where the polynomial is not so well behaved.
108  // Use the “min” variables temporarily to hold the first approximation
109  p_initialMinGroundRangeGuess = (slant - p_a[0]) / p_a[1];
110  double minGroundRangeGuess = slant - (p_a[0] + p_initialMinGroundRangeGuess *
111  (p_a[1] + p_initialMinGroundRangeGuess * (p_a[2] + p_initialMinGroundRangeGuess * p_a[3])));
112  // Now the max is the second approximation
113  p_initialMaxGroundRangeGuess = p_initialMinGroundRangeGuess + minGroundRangeGuess /
114  (p_a[1] + p_initialMinGroundRangeGuess * (2.0 * p_a[2] + p_initialMinGroundRangeGuess *
115  3.0 * p_a[3]));
116  double maxGroundRangeGuess = slant - (p_a[0] + p_initialMaxGroundRangeGuess *
117  (p_a[1] + p_initialMaxGroundRangeGuess * (p_a[2] +
118  p_initialMaxGroundRangeGuess * p_a[3])));
119  // Finally, apply the “windage” to bracket the root.
120  p_initialMinGroundRangeGuess = p_initialMaxGroundRangeGuess - 10.0;
121  minGroundRangeGuess = slant - (p_a[0] + p_initialMinGroundRangeGuess *
122  (p_a[1] + p_initialMinGroundRangeGuess * (p_a[2] + p_initialMinGroundRangeGuess * p_a[3])));
123 
124  // If both guesses are on the same side of zero, we need to expand the bracket range
125  // to include a zero-crossing.
126  if ((minGroundRangeGuess < 0.0 && maxGroundRangeGuess < 0.0) ||
127  (minGroundRangeGuess > 0.0 && maxGroundRangeGuess > 0.0)) {
128 
129  int maxBracketIters = 10;
130 
131  float xMin = p_initialMinGroundRangeGuess;
132  float xMax = p_initialMaxGroundRangeGuess;
133 
134  float funcMin = minGroundRangeGuess;
135  float funcMax = maxGroundRangeGuess;
136 
137  for (int j=0; j<maxBracketIters; j++) {
138 
139  //distance between guesses
140  float dist = abs(abs(xMin) - abs(xMax));
141 
142  // move move the x-value of the closest root twice as far away from the other root as it was
143  // before to extend the bracket range.
144  if (abs(funcMin) <= abs(funcMax)) {
145  //min is closer
146  xMin = xMax - 2*dist;
147  xMax = xMax; //doesn't change
148  }
149  else {
150  //max is closer
151  xMax = xMin + 2*dist;
152  xMin = xMin; //doesn't change
153  }
154 
155  funcMin = slant -(p_a[0]+ xMin *(p_a[1] + xMin * (p_a[2] + xMin * p_a[3])));
156  funcMax = slant - (p_a[0] + xMax *(p_a[1] + xMax * (p_a[2] + xMax * p_a[3])));
157 
158  // if we've successfully bracketed the root, we can break.
159  if((funcMin <= 0.0 && funcMax >= 0.0) || (funcMin >= 0.0 && funcMax <= 0.0)){
160  p_initialMinGroundRangeGuess = xMin;
161  p_initialMaxGroundRangeGuess = xMax;
162  minGroundRangeGuess = funcMin;
163  maxGroundRangeGuess = funcMax;
164  break;
165  }
166  }
167  }
168 
169  // If the ground range guesses at the 2 extremes of the image are equal
170  // or they have the same sign, then the ground range cannot be solved for.
171  // The only case where they are equal should be at zero, which we already trapped.
172  if((minGroundRangeGuess == maxGroundRangeGuess) ||
173  (minGroundRangeGuess < 0.0 && maxGroundRangeGuess < 0.0) ||
174  (minGroundRangeGuess > 0.0 && maxGroundRangeGuess > 0.0)) {
175  return false;
176  }
177 
178  // Use Wijngaarden/Dekker/Brent algorithm to find a root of the function:
179  // g(groundRange) = slantRange - (p_a[0] + groundRange * (p_a[1] +
180  // groundRange * (p_a[2] + groundRange * p_a[3])))
181  // The algorithm used is a combination of the bisection method with the
182  // secant method.
183  int iter = 0;
184  double eps = 3.E-8;
185  double ax = p_initialMinGroundRangeGuess;
186  double bx = p_initialMaxGroundRangeGuess;
187  double fax = minGroundRangeGuess;
188  double fbx = maxGroundRangeGuess;
189  double fcx = fbx;
190  double cx = 0.0;
191  double d = 0.0;
192  double e = 0.0;
193  double tol1;
194  double xm;
195  double p, q, r, s, t;
196 
197  do {
198  iter++;
199  if(fbx * fcx > 0.0) {
200  cx = ax;
201  fcx = fax;
202  d = bx - ax;
203  e = d;
204  }
205  if(fabs(fcx) < fabs(fbx)) {
206  ax = bx;
207  bx = cx;
208  cx = ax;
209  fax = fbx;
210  fbx = fcx;
211  fcx = fax;
212  }
213  tol1 = 2.0 * eps * fabs(bx) + 0.5 * p_tolerance;
214  xm = 0.5 * (cx - bx);
215  if(fabs(xm) <= tol1 || fbx == 0.0) {
216  p_focalPlaneX = bx;
217  p_focalPlaneY = 0.0;
218  return true;
219  }
220  if(fabs(e) >= tol1 && fabs(fax) > fabs(fbx)) {
221  s = fbx / fax;
222  if(ax == cx) {
223  p = 2.0 * xm * s;
224  q = 1.0 - s;
225  }
226  else {
227  q = fax / fcx;
228  r = fbx / fcx;
229  p = s * (2.0 * xm * q * (q - r) - (bx - ax) * (r - 1.0));
230  q = (q - 1.0) * (r - 1.0) * (s - 1.0);
231  }
232  if(p > 0.0) q = -q;
233  p = fabs(p);
234  t = 3.0 * xm * q - fabs(tol1 * q);
235  if(t > fabs(e * q)) t = fabs(e * q);
236  if(2.0 * p < t) {
237  e = d;
238  d = p / q;
239  }
240  else {
241  d = xm;
242  e = d;
243  }
244  }
245  else {
246  d = xm;
247  e = d;
248  }
249  ax = bx;
250  fax = fbx;
251  if(fabs(d) > tol1) {
252  bx = bx + d;
253  }
254  else {
255  if(xm >= 0.0) {
256  t = fabs(tol1);
257  }
258  else {
259  t = -fabs(tol1);
260  }
261  bx = bx + t;
262  }
263  fbx = slant - (p_a[0] + bx * (p_a[1] + bx * (p_a[2] + bx * p_a[3])));
264  }
265  while(iter <= p_maxIterations);
266 
267  return false;
268  }
269 
275  PvlSequence seq;
276  seq = keyword;
277  for(int i = 0; i < seq.Size(); i++) {
278  // TODO: Test array size to be 4 if not throw error
279  std::vector<QString> array = seq[i];
280  double et;
281  utc2et_c(array[0].toLatin1().data(), &et);
282  p_time.push_back(et);
283  p_a0.push_back(toDouble(array[1]));
284  p_a1.push_back(toDouble(array[2]));
285  p_a2.push_back(toDouble(array[3]));
286  p_a3.push_back(toDouble(array[4]));
287  // TODO: Test that times are ordered if not throw error
288  // Make the mrf2isis program sort them if necessary
289  }
290  }
291 
297  double currentEt = p_camera->time().Et();
298 
299  std::vector<double>::iterator pos = lower_bound(p_time.begin(), p_time.end(), currentEt);
300 
301  int index = p_time.size() - 1;
302  if (currentEt <= p_time[0]) {
303  p_a[0] = p_a0[0];
304  p_a[1] = p_a1[0];
305  p_a[2] = p_a2[0];
306  p_a[3] = p_a3[0];
307  }
308  else if (currentEt >= p_time.at(index)) {
309  p_a[0] = p_a0[index];
310  p_a[1] = p_a1[index];
311  p_a[2] = p_a2[index];
312  p_a[3] = p_a3[index];
313  } else {
314  index = pos - p_time.begin();
315  double weight = (currentEt - p_time.at(index-1)) /
316  (p_time.at(index) - p_time.at(index-1));
317  p_a[0] = p_a0[index-1] * (1.0 - weight) + p_a0[index] * weight;
318  p_a[1] = p_a1[index-1] * (1.0 - weight) + p_a1[index] * weight;
319  p_a[2] = p_a2[index-1] * (1.0 - weight) + p_a2[index] * weight;
320  p_a[3] = p_a3[index-1] * (1.0 - weight) + p_a3[index] * weight;
321  }
322  }
323 
327  void RadarSlantRangeMap::SetWeightFactors(double range_sigma, double doppler_sigma) {
328  p_rangeSigma = range_sigma; // meters scaling factor
329  p_dopplerSigma = doppler_sigma; // htz scaling factor
330  }
331 }
void SetWeightFactors(double range_sigma, double doppler_sigma)
Set the weight factors for slant range and Doppler shift.
Parse and return elements of a Pvl sequence.
Definition: PvlSequence.h:64
int Size() const
Number of arrays in the sequence.
Definition: PvlSequence.h:88
void ComputeA()
Set new A-coefficients based on the current ephemeris time.
virtual bool SetFocalPlane(const double dx, const double dy)
Set the ground range and compute a slant range.
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
double Et() const
Returns the ephemeris time (TDB) representation of the time as a double.
Definition: iTime.h:135
virtual bool SetUndistortedFocalPlane(const double ux, const double uy)
Set the slant range and compute a ground range.
iTime time() const
Returns the ephemeris time in seconds which was used to obtain the spacecraft and sun positions...
Definition: Spice.cpp:804
void SetFocalLength(double v)
Sets the focal length.
Definition: Camera.cpp:3064
A single keyword-value pair.
Definition: PvlKeyword.h:98
Distort/undistort focal plane coordinates.
void SetCoefficients(PvlKeyword &keyword)
Load the ground range/slant range coefficients from the RangeCoefficientSet keyword.

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:28:08