OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
GEO_interp_mirr_enc.c
Go to the documentation of this file.
1 #include "imsl_wrap.h"
2 #include "PGS_MODIS_35251.h"
3 #include "GEO_global_arrays.h"
4 #include "GEO_inst.h"
5 #include "GEO_util.h"
6 #include "GEO_output.h"
7 #include "smfio.h"
8 
10  int const scan_number,
11  int const sample_number,
12  double * const sample_enc
13  )
14 /*
15 !C*****************************************************************************
16 !Description:
17  Routine in the instrument model of the Level-1A geolocation software
18  interpolate the mirror encoder number. Once per scan, it uses spline
19  interpolation to estimate the mirror position for all frames in that
20  scan. Each time it's called, it retrieves the interpolated value for
21  the specified frame.
22 
23 !Input Parameters:
24  scan_number The scan number
25  sample_number The sample number for which the encoder values
26 
27 !Output Parameters:
28  sample_enc The pointer to sample encoder number
29 
30 Return value:
31  FAIL If sample_enc is NULL, or the interpolation failed.
32  SUCCESS Otherwise.
33 
34 Externally defined:
35  Global Variables (input):
36  num_impulse The number of impulses in this scan.
37  mirr_impulse_enc The encoder values for each impulse
38  mirr_impulse_time The time values for each impulse
39  sample_time The sample time for each sample
40  scan_start_time The start time for each scan
41  DBL_MAX <float.h>
42  FAIL "GEO_basic.h"
43  GEO_DOUBLE_FILLVALUE "GEO_output.h"
44  MAX_IMPULSE_NUMBER "GEO_geo.h"
45  MAX_SCAN_NUMBER "GEO_geo.h"
46  MAX_PADDED "GEO_geo.h"
47  MODIS_E_GEO "PGS_MODIS_35251.h"
48  MODIS_E_BAD_INPUT_ARG "PGS_MODIS_35251.h"
49  MODIS_E_GEO_MIRR_MOTION "PGS_MODIS_35251.h"
50  MODIS_E_GEO_NO_ZERO_ENCODER "PGS_MODIS_35251.h"
51  SUCCESS "GEO_basic.h"
52 
53 Called by:
54  GEO_get_inst_mirr_normal
55 
56 Routines Called:
57  imsl_d_spline_interp "imslwrap.h"
58  imsl_d_spline_value "imslwrap.h"
59  imsl_d_machine "imslwrap.h"
60  modsmf "smfio.h"
61 
62 !Revision History:
63  $Log: GEO_interp_mirr_enc.c,v $
64  Revision 6.1 2009/05/11 17:49:26 xgeng
65  Changed MAX_SCAN_SAMPLE to MAX_PADDED.
66  Changed to determine padded_samples by examination of sample_time values,
67  and to interpolate only for that many values.
68  The above updates match with PDL revision 6.1 and 6.2.
69 
70  Revision 5.1 2006/10/05 16:10:31 kuyper
71  Changed to call imsl_d_spline_value only once per scan, for all of the
72  frames in the scan, rather than calling it seperately for each frame.
73 
74  Revision 4.6 2004/04/08 19:39:45 kuyper
75  Corrected knot sequence to satisfy constraints.
76 
77  Revision 4.5 2004/03/17 18:35:20 kuyper
78  Changed to fit encoder data to a linear trend plus a quadratic B-spline, with
79  the knots of the B-spline chosen to force a smooth return to 0 at the ends
80  of the knot sequence.
81 
82  Revision 4.4 2003/08/22 17:12:48 kuyper
83  Corrected handling of starting point.
84 
85  Revision 4.3 2003/08/19 19:20:32 vlin
86  Updated to avoid division by zero.
87 
88  Revision 4.2 2003/08/11 19:53:17 kuyper
89  Corrections discovered during testing.
90 
91  Revision 4.1 2003/08/07 15:59:28 vlin
92  Changed to interpolate intermediate values using splines, rather than fitting
93  the data to chebyshev polynomials.
94  Changed to use the interpolated spline to find the time where the encoder
95  count is 0.0, rather than relying on GEO_prepare_mirr_data to extrapolate
96  the first (unknown) encoder time from the later ones.
97 
98  4/5/95
99  Ruiming Chen
100  Finished coding.
101 
102 Requirements:
103  PR03-F-3.1.2-1
104 
105 !Team-unique Header:
106 
107  This software is developed by the MODIS Science Data Support
108  Team for the National Aeronautics and Space Administration,
109  Goddard Space Flight Center, under contract NAS5-32373.
110 
111 Design notes:
112  For a linear least-squares fit to a set of N points (x,y), use the following
113  formulae:
114 
115  sum(x^2)*sum(y) - sum(x)*sum(x*y)
116  intercept = ---------------------------------
117  N*sum(x^2)-(sum(x))^2
118 
119  N*sum(x*y)-sum(x)*sum(y)
120  slope = ------------------------
121  N*sum(x^2)-(sum(x))^2
122 
123 !END
124 *****************************************************************************/
125 {
126 #define QUADRATIC 3
127 #define DERIVS 3
128  static double last_scan_time = -DBL_MAX;
129  static int scan_success=0;
130  static double *enc_array;
131  if(!enc_array) {
132  enc_array = calloc(MAX_PADDED, sizeof(double));
133  }
134  static double intercept, slope;
135  const double aNaN = imsl_d_machine(6);
136  char msgbuf[128]="";
137  char filefunc[] = __FILE__ ", GEO_interp_mirr_enc";
138 
139 
140  if (sample_enc == NULL || scan_number < 0 || scan_number >= MAX_SCAN_NUMBER
141  || sample_number < 0 || sample_number >= MAX_PADDED ||
142  num_impulse[scan_number] < 0 ||
143  num_impulse[scan_number] > MAX_IMPULSE_NUMBER)
144  {
145  sprintf(msgbuf, "sample_enc = %p, scan = %d, frame = %d, impulse = %d",
146  (void *)sample_enc, scan_number, sample_number,
147  num_impulse[scan_number]);
148  modsmf(MODIS_E_BAD_INPUT_ARG, msgbuf, filefunc);
149  return FAIL;
150  }
151 
152  /* The following test uses the scan start time, rather than scan number, to
153  * protect against the possibility that the last valid scan in one granule
154  * might have the same scan number as the first one in the next granule.
155  */
156  if (fabs(scan_start_time[scan_number]-last_scan_time)>=DBL_MIN)
157  {
158  int i, d, ndata, order;
159  double q, tzero, sqrt_arg;
160  double knots[MAX_IMPULSE_NUMBER+QUADRATIC], derivs[DERIVS];
161  double diff[MAX_IMPULSE_NUMBER-1];
162  double sumt=0.0, sumt2=0.0, sume=0.0, sumet=0.0;
163  double denom, delta_t;
164  int padded_samples = 0;
165  Imsl_d_spline *encoder_spline=NULL;
166 
167  last_scan_time = scan_start_time[scan_number];
168  scan_success = 0;
169 
170  /* Don't use mirr_impulse_time[scan_number][0], it's bogus */
171  ndata = num_impulse[scan_number] - 1;
172  if (ndata < QUADRATIC)
173  order = ndata;
174  else
175  order = QUADRATIC;
176 
177  /* Calculate coefficients for a least-squares linear fit. */
178  for(i=1; i<num_impulse[scan_number]; i++)
179  {
180  sumt += mirr_impulse_time[scan_number][i];
181  sumt2 += mirr_impulse_time[scan_number][i] *
182  mirr_impulse_time[scan_number][i];
183  sume += mirr_impulse_enc[scan_number][i];
184  sumet += mirr_impulse_time[scan_number][i] *
185  mirr_impulse_enc[scan_number][i];
186  }
187 
188  denom = ndata*sumt2-sumt*sumt;
189  intercept = sumt2*sume - sumt*sumet;
190  slope = ndata*sumet-sumt*sume;
191  if( fabs(denom) > fabs(intercept)/DBL_MAX &&
192  fabs(denom) > fabs(slope)/DBL_MAX)
193  {
194  intercept /= denom;
195  slope /= denom;
196  }
197  else
198  {
199  /* Shouldn't happen for num_impulse > 1 */
200  sprintf(msgbuf, "%d", scan_number);
201  modsmf(MODIS_E_GEO_MIRR_MOTION, msgbuf, filefunc);
202  return FAIL;
203  }
204 
205  /* Calculate differences from the least-squares linear fit. */
206  for(i=1; i<num_impulse[scan_number]; i++)
207  diff[i-1] = mirr_impulse_enc[scan_number][i] -
208  (intercept+slope*mirr_impulse_time[scan_number][i]);
209 
210  /* Set up knots that never double up. This means that the first order-2
211  * derivatives of the B-spline are forced to go continuously to 0 at the
212  * ends. That ensures that the combination of linear trend+B-spline
213  * returns as smoothly as possible to the linear trend, outside the
214  * range of the available data.
215  */
216 
217  delta_t =
218  mirr_impulse_time[scan_number][1]-mirr_impulse_time[scan_number][0];
219 
220  /* Knots evenly spaced from the second data point. The first data point
221  * is not used, because it's an arbitrary insert, rather than real data.
222  */
223  for (i = 0; i < order; i++)
224  knots[i] = mirr_impulse_time[scan_number][1] + delta_t*(i+1-order);
225 
226  if(order&1) /* Knots are halfway between data points. */
227  for(i=order; i<ndata; i++)
228  knots[i] = 0.5*(mirr_impulse_time[scan_number][i-order/2] +
229  mirr_impulse_time[scan_number][i-order/2+1]);
230  else /* Knots coincide with data points. */
231  for(i=order; i<num_impulse[scan_number]; i++)
232  knots[i] = mirr_impulse_time[scan_number][i-order/2];
233 
234  /* Knots evenly spaced from the last data point. */
235  for(i=ndata; i<ndata+order; i++)
236  knots[i] = mirr_impulse_time[scan_number][ndata] +
237  delta_t*(i-ndata);
238 
239  /* Calculate a spline interpolant of diff. */
240  /* Don't use mirr_impulse_time[0], it's bogus */
241  encoder_spline = imsl_d_spline_interp(ndata,
242  mirr_impulse_time[scan_number]+1, diff,
243  IMSL_ORDER, order, IMSL_KNOTS, knots, 0);
244  if (imsl_error_code()) {
245  sprintf(msgbuf, "imsl_d_spline_interp():%ld",
246  (long)imsl_error_code());
247  modsmf(MODIS_E_GEO, msgbuf, filefunc);
248  free(encoder_spline);
249  return FAIL;
250  }
251 
252  for (d = 0; d < DERIVS; d++)
253  {
254  derivs[d] = imsl_d_spline_value(mirr_impulse_time[scan_number][0],
255  encoder_spline, IMSL_DERIV, d, 0);
256  if ((aNaN != aNaN && derivs[d] != derivs[d]) ||
257  (aNaN == aNaN && derivs[d] == aNaN) )
258  {
259  sprintf(msgbuf, "imsl_d_spline_value() for derivs [%d],\n"
260  "imsl_error_code() = %ld", d, (long)imsl_error_code());
261  modsmf(MODIS_E_GEO, msgbuf, filefunc);
262  free(encoder_spline);
263  return FAIL;
264  }
265  }
266 
267  derivs[0] += intercept;
268  derivs[1] += slope;
269 
270  /* Estimate tzero, the offset from mirr_impulse_time[scan_number][0] for
271  * which the interpolated curve is at the point * where scan start should
272  * occur:
273  * derivs[0]+derivse[1]*tzero+0.5*derivs[2]*tzero*tzero
274  * = mirr_impulse_enc[scan_number][0]
275  */
276  derivs[0] -= mirr_impulse_enc[scan_number][0];
277  sqrt_arg = derivs[1]*derivs[1] - 2*derivs[0]*derivs[2];
278  if (sqrt_arg < 0 || derivs[1] < 0 ) {
279  sprintf(msgbuf, "%d", scan_number);
280  modsmf(MODIS_E_GEO_NO_ZERO_ENCODER, msgbuf, filefunc);
281  free(encoder_spline);
282  return FAIL;
283  }
284 
285  /* See "Numerical Recipes in C" by Preuss et. al., section 5.5 */
286  q = -(derivs[1]+sqrt(sqrt_arg))*0.5;
287 
288  /* Take the smaller of the two roots; the curve will normally be a very
289  * straight line, and 0.0 will be a very good estimate of the proper
290  * value, so the smaller root will normally be very much smaller than
291  * the larger one. Therefore don't calculate both values; the larger one
292  * might overflow!
293  * Don't test q*q vs fabs(derivs[0]*derivs[2]*0.5), because that might
294  * overflow (though this is pretty unlikely).
295  */
296 
297  /* In a typical implementation of C, DBL_MAX*DBL_EPSILON*DBL_EPSILON is
298  * much greater than 1.0. Under those circumstances, if the program
299  * manages to reach this point in the code, it is not possible for the
300  * tests involving DBL_MAX below to actually fail. However, proving
301  * that fact is complicated, so it's safer to leave these tests in.
302  */
303  if (fabs(q) < sqrt(fabs(derivs[0]))*sqrt(fabs(derivs[2]*0.5)))
304  {
305  if(fabs(derivs[2]) <= 2.0*fabs(q)/DBL_MAX)
306  {
307  sprintf(msgbuf, "%d: derivs[2] too small", scan_number);
308  modsmf(MODIS_E_GEO_NO_ZERO_ENCODER, msgbuf, filefunc);
309  free(encoder_spline);
310  return FAIL;
311  }
312  else
313  tzero = 2.0*q/derivs[2];
314  }
315  else
316  {
317  if(fabs(q) <= fabs(derivs[0])/DBL_MAX)
318  {
319  sprintf(msgbuf, "%d, q too small", scan_number);
320  modsmf(MODIS_E_GEO_NO_ZERO_ENCODER, msgbuf, filefunc);
321  free(encoder_spline);
322  return FAIL;
323  }
324  else
325  tzero = derivs[0]/q;
326  }
327 
328  /* Shift encoder times so imsl_d_spline_value(
329  * mirr_impulse_time[scan_number][0], encoder_spline) will be almost
330  * exactly mirr_impulse_enc[scan_number][0]. If DERIVS<=order-1,
331  * it is exact.
332  */
333  for (i = 0; i < ndata+order; i++)
334  encoder_spline->knots[0][i] -= tzero;
335  for (i = 1; i<num_impulse[scan_number]; i++)
336  mirr_impulse_time[scan_number][i] -= tzero;
337 
338  while (padded_samples < MAX_PADDED && sample_time[padded_samples] < GEO_DOUBLE_FILLVALUE) {
339  padded_samples++;
340  }
341 
342  (void)imsl_d_spline_value(0.0, encoder_spline,
343  IMSL_GRID_USER, padded_samples, sample_time, enc_array, 0);
344  /* When using IMSL_GRID_USER, return value does not indicate error. */
345  if(imsl_error_code())
346  modsmf(MODIS_E_GEO, "imsl_d_spline_value()", filefunc);
347  else
348  scan_success = 1;
349 
350  free(encoder_spline);
351  } /* end if scan_start_time != last_scan_time */
352 
353  if(!scan_success)
354  return FAIL;
355  /* First call for this scan failed; no point in further messages. */
356 
357  *sample_enc = enc_array[sample_number] +
358  intercept + slope*sample_time[sample_number];
359  return SUCCESS;
360 }
361 
int GEO_interp_mirr_enc(int const scan_number, int const sample_number, double *const sample_enc)
#define DERIVS
data_t q
Definition: decode_rs.h:74
#define SUCCESS
Definition: ObpgReadGrid.h:15
#define MODIS_E_GEO_MIRR_MOTION
double * mirr_impulse_time[MAX_SCAN_NUMBER]
int16_t * denom[MAXNFILES]
Definition: l2bin.cpp:99
#define FAIL
Definition: ObpgReadGrid.h:18
#define NULL
Definition: decode_rs.h:63
#define MODIS_E_BAD_INPUT_ARG
double IMSL_DECL imsl_d_machine(int n)
#define MAX_PADDED
Definition: GEO_geo.h:84
void derivs(float x, float y[], float dydx[])
#define MODIS_E_GEO
double IMSL_DECL imsl_d_spline_value(double x, Imsl_d_spline *sp,...)
#define GEO_DOUBLE_FILLVALUE
Definition: GEO_output.h:106
float32 slope[]
Definition: l2lists.h:30
long IMSL_DECL imsl_error_code(void)
Definition: imsl_error.c:27
double * sample_time
subroutine diff(x, conec, n, dconecno, dn, dconecmk, units, u, inno, i, outno, o, input, deriv)
Definition: ffnet.f:205
float32 intercept[]
Definition: l2lists.h:44
#define QUADRATIC
Imsl_d_spline *IMSL_DECL imsl_d_spline_interp(int ndata, double xdata[], double fdata[],...)
const int MAX_SCAN_NUMBER
#define MAX_IMPULSE_NUMBER
Definition: GEO_geo.h:91
#define fabs(a)
Definition: misc.h:93
double scan_start_time[MAX_SCAN_NUMBER]
#define DBL_MAX
Definition: make_L3_v1.1.c:60
#define MODIS_E_GEO_NO_ZERO_ENCODER
for(i=0;i< NROOTS;i++) s[i]
Definition: decode_rs.h:85
int i
Definition: decode_rs.h:71
double * mirr_impulse_enc[MAX_SCAN_NUMBER]
int num_impulse[MAX_SCAN_NUMBER]