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