OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
GEO_hires.c
Go to the documentation of this file.
1 /*
2  *Revision History:
3  * $Log: GEO_hires.c,v $
4  * Revision 6.5 2010/12/14 21:12:18 kuyper
5  * Split samp into osamp and psamp, thereby simplifying the logic, enabling
6  * identification and correction of some off-by-one errors.
7  *
8  * Revision 6.4 2010/06/18 20:38:31 kuyper
9  * Corrected to initialize 'triplets'.
10  *
11  * Revision 6.3 2009/06/12 16:01:46 kuyper
12  * Corrected sign error in formula for the interpolation factor in the scan
13  * direction.
14  * Corrected handling of the case where the first call to GEO_calculate_offsets
15  * fails.
16  *
17  * Revision 6.2 2009/05/22 18:15:20 kuyper
18  * Removed design notes; the ones in the PDL are sufficient.
19  * Added sample_flags to invalid input arguments message.
20  * Corrected several misuses of N_samp.
21  *
22  * Revision 6.1 2009/04/28 18:05:37 kuyper
23  * Initial revision.
24  *
25  * James Kuyper James.R.Kuyper@nasa.gov
26  */
27 
28 #include "PGS_CSC.h"
29 #include "smfio.h"
30 #include "GEO_earth.h"
31 #include "GEO_geo.h"
32 #include "PGS_MODIS_35251.h"
33 
34 #define KILO 1000.0
35 
36 static double GEO_triple_product(
37  double x[3],
38  double y[3],
39  double z[3]
40 )
82 {
83  return x[0] * (y[1] * z[2] - y[2] * z[1]) +
84  x[1] * (y[2] * z[0] - y[0] * z[2]) +
85  x[2] * (y[0] * z[1] - y[1] * z[0]);
86 }
87 
88 static int GEO_calculate_offsets(
89  double hires_scale,
90  double num,
91  double den,
92  double nst,
93  double nds,
94  double ndt,
95  double nqs,
96  double nqt,
97  const double delta[],
98  const double quad[],
99  const double normal[],
100  const double scan[],
101  const double track[],
102  int8 sth[]
103 )
104 
105 
159 {
160  int ind; /* Loop counter */
161  double doff[3]; /* temporary offsets */
162  double den1, den2; /* possible denominators */
163  const double max_offset = (-0.5 - HIRES_FVALUE) * hires_scale;
164 
165  if(fabs(den) <= fabs(num)/DBL_MAX)
166  return 0;
167 
168  doff[0] = num/den;
169  if(fabs(doff[0]) >= max_offset)
170  return 0;
171 
172  den1 = doff[0]*nqs - nst;
173  den2 = doff[0]*nqt;
174  if(fabs(den1) > fabs(den2))
175  {
176  num = nds;
177  den = den1;
178  }
179  else
180  {
181  num = ndt - doff[0]*nst;
182  den = den2;
183  }
184 
185  if(fabs(den) <= fabs(num)/DBL_MAX)
186  return 0;
187 
188  doff[1] = num/den;
189 
190  doff[2] = 0.0;
191  for(ind=0; ind<3; ind++)
192  doff[2] += normal[ind] * (delta[ind] - doff[0]*scan[ind] -
193  doff[1]*track[ind] - doff[0]*doff[1]*quad[ind]) / (KILO * KILO);
194 
195  if(fabs(doff[2]) >= max_offset)
196  return 0;
197 
198  for(ind=0; ind<3; ind++)
199  sth[ind] = (int)floor(doff[ind]/hires_scale + 0.5);
200 
201  return 1;
202 }
203 
204 
205 PGSt_SMF_status GEO_hires(
206  uint16 N_samp,
207  int padded_samples,
208  double hires_scale,
209  double terrain_sample_position[][MAX_PADDED][3],
210  double ecr_sample_position[][MAX_PADDED][3],
211  double ecr_frame_position[][MAX_FRAMES][3],
212  uint8 sample_flags[][MAX_PADDED],
213  uint8 frame_flags[][MAX_FRAMES],
214  int8 hires_offsets[][DETECTORS_QKM][SAMPLES_QKM]
215 )
277 {
278  /* loop counters to identify which high resolution pixel is being
279  * processed.
280  */
281  int hdet;
282  int samples = padded_samples - N_samp + 1;
283  int frames = (samples+1)/N_samp;
284 
285  if(!terrain_sample_position || !ecr_sample_position || !ecr_frame_position
286  || !sample_flags || !frame_flags || !hires_offsets)
287  {
288  char filefunc[] = __FILE__ ", GEO_hires()";
289  char msgbuf[PGS_SMF_MAX_MSGBUF_SIZE];
290 
291  sprintf(msgbuf, "terrain_sample_position=%p, "
292  "ecr_sample_position=%p, ecr_frame_position=%p, sample_flags=%p, "
293  "frame_flags=%p, hires_offsets=%p", (void*)terrain_sample_position,
294  (void*)ecr_sample_position, (void*) ecr_frame_position,
295  (void*)sample_flags, (void*)frame_flags, (void*)hires_offsets);
296  modsmf(MODIS_E_BAD_INPUT_ARG, msgbuf, filefunc);
297 
298  return MODIS_E_BAD_INPUT_ARG;
299  }
300 
301  for(hdet = 0; hdet < DETECTORS_1KM * N_samp; hdet++)
302  {
303  int ind; /* loop counter to index across coordinates. */
304  /* The addition of N_samp cancels final -1, mathematically , but in C
305  * it is needed to ensure the same behavior. */
306  int det = (hdet + N_samp - N_samp/2)/N_samp - 1;
307  double dt; /* Fractional offset in track direction. */
308  int osamp; /* output sample number */
309 
310  if(det < 0)
311  det = 0;
312  else if(det > DETECTORS_1KM - 2)
313  det = DETECTORS_1KM - 2;
314 
315  dt = (hdet + 0.5)/N_samp - (det + 0.5);
316 
317  /* The bracketing detectors are det and det+1 */
318  for(osamp =0; osamp < samples; osamp++)
319  { /* Determine which 1km frames to use for the */
320  /* estimate, and the corresponding values of ds.*/
321  double estimated[3], delta[3], scan[3], track[3];
322  double normal[3], quad[3];
323  double nds, nqs, nst, ndt, nqt; /* triple products */
324  double a, b, c, q; /* quadratic coefficients. */
325  int8 sth[2][3]; /* tentative s/t/h triplets. */
326  int triplets=0; /* number of triplets selected. */
327  double ds; /* Fractional offset in scan direction */
328  /* sample number in padded hi-resolution arrays. */
329  int psamp = osamp + N_samp -1;
330  int frame = osamp/N_samp;
331 
332  if(frame > frames - 2)
333  frame = frames - 2;
334  ds = osamp/(double)N_samp - frame;
335  /* The bracketing frames are frame and frame+1 */
336 
337  if(sample_flags[hdet ][psamp ] >= INVALID_INPUT_DATA ||
338  frame_flags[det ][frame ] >= INVALID_INPUT_DATA ||
339  frame_flags[det ][frame+1] >= INVALID_INPUT_DATA ||
340  frame_flags[det+1][frame ] >= INVALID_INPUT_DATA ||
341  frame_flags[det+1][frame+1] >= INVALID_INPUT_DATA)
342  {
343  for(ind = 0; ind<3; ind++) /* hires_offsets is NOT padded */
344  hires_offsets[ind][hdet][osamp] = HIRES_FVALUE;
345  }
346 
347  /*Perform bilinear interpolation/extrapolation */
348 
349  for(ind =0; ind<3; ind++)
350  {
351  estimated[ind] =
352  (1 - dt)*((1 - ds)*ecr_frame_position[det ][frame ][ind] +
353  ds *ecr_frame_position[det ][frame+1][ind])+
354  dt *((1 - ds)*ecr_frame_position[det+1][frame ][ind] +
355  ds *ecr_frame_position[det+1][frame+1][ind]);
356  delta[ind] =
357  ecr_sample_position[hdet][psamp][ind] - estimated[ind];
358  scan[ind] =
359  (1 - dt)*(ecr_frame_position[det ][frame+1][ind] -
360  ecr_frame_position[det ][frame ][ind]) +
361  dt *(ecr_frame_position[det+1][frame+1][ind] -
362  ecr_frame_position[det+1][frame ][ind]);
363  track[ind] =
364  (1 - ds)*(ecr_frame_position[det+1][frame ][ind] -
365  ecr_frame_position[det ][frame ][ind]) +
366  ds *(ecr_frame_position[det+1][frame+1][ind] -
367  ecr_frame_position[det ][frame+1][ind]);
368  quad[ind] = ecr_frame_position[det ][frame ][ind]-
369  ecr_frame_position[det ][frame+1][ind]-
370  ecr_frame_position[det+1][frame ][ind]+
371  ecr_frame_position[det+1][frame+1][ind];
372  }
373 
374  normal[0] = KILO *
375  cos(terrain_sample_position[hdet][psamp][LAT_IDX]) *
376  cos(terrain_sample_position[hdet][psamp][LON_IDX]);
377  normal[1] = KILO *
378  cos(terrain_sample_position[hdet][psamp][LAT_IDX]) *
379  sin(terrain_sample_position[hdet][psamp][LON_IDX]);
380  normal[2] = KILO *
381  sin(terrain_sample_position[hdet][psamp][LAT_IDX]);
382 
383  nds = GEO_triple_product(normal, delta, scan);
384  ndt = GEO_triple_product(normal, delta, track);
385  nst = GEO_triple_product(normal, scan, track);
386  nqs = GEO_triple_product(normal, quad, scan);
387  nqt = GEO_triple_product(normal, quad, track);
388 
389  /* Calculate quadratic coefficients */
390  a = nst * nqs;
391  b = nds * nqt - nst * nst - ndt * nqs;
392  c = ndt * nst;
393 
394  if(b*b >= 4.0*a*c)
395  {
396  q = -0.5*(b + (b<0 ? -1 : 1)*sqrt(b*b - 4.0*a*c));
397 
398  triplets = GEO_calculate_offsets(hires_scale, q, a, nst, nds,
399  ndt, nqs, nqt, delta, quad, normal, scan, track, sth[0]);
400  triplets += GEO_calculate_offsets(hires_scale, c, q, nst, nds,
401  ndt, nqs, nqt, delta, quad, normal, scan, track,
402  sth[triplets]);
403  }
404 
405  if(triplets > 1 &&
406  sth[1][0]*sth[1][0]+sth[1][1]*sth[1][1]+sth[1][2]*sth[1][2] <
407  sth[0][0]*sth[0][0]+sth[0][1]*sth[0][1]+sth[0][2]*sth[0][2])
408  memcpy(sth[0], sth[1], sizeof sth[0]);
409 
410  if(triplets > 0)
411  {
412  for(ind=0; ind < 3; ind++) /* hires_offsets is NOT padded */
413  hires_offsets[ind][hdet][osamp] = sth[0][ind];
414  }
415  else
416  {
417  for(ind=0; ind < 3; ind++) /* hires_offsets is NOT padded */
418  hires_offsets[ind][hdet][osamp] = HIRES_FVALUE;
419  }
420  }
421  }
422 
423  return PGS_S_SUCCESS;
424 }
425 
data_t q
Definition: decode_rs.h:74
#define MODIS_E_BAD_INPUT_ARG
#define MAX_FRAMES
Definition: GEO_geo.h:79
MOD_PR01 Production producing one five minute granule of output data in each run It can be configured to produce as many as three five minute granules per run Each execution with one construction record and one date file for each dataset In normal these are created by which splits them out of the hour datasets For LANCE they are created by which merges all session MODIS L0 datasets overlapping the requested time and extracts from the merged data those packets which fall within that time period Each scan of data is stored in the L1A granule that covers the start time of that scan
Definition: MOD_PR01_pr.txt:19
#define MAX_PADDED
Definition: GEO_geo.h:84
const double delta
integer, parameter double
#define KILO
Definition: GEO_hires.c:34
#define DETECTORS_1KM
Definition: GEO_geo.h:85
@ LON_IDX
data_t b[NROOTS+1]
Definition: decode_rs.h:77
@ LAT_IDX
#define HIRES_FVALUE
Definition: GEO_geo.h:157
#define fabs(a)
Definition: misc.h:93
data_t den
Definition: decode_rs.h:74
PGSt_SMF_status GEO_hires(uint16 N_samp, int padded_samples, double hires_scale, double terrain_sample_position[][MAX_PADDED][3], double ecr_sample_position[][MAX_PADDED][3], double ecr_frame_position[][MAX_FRAMES][3], uint8 sample_flags[][MAX_PADDED], uint8 frame_flags[][MAX_FRAMES], int8 hires_offsets[][DETECTORS_QKM][SAMPLES_QKM])
Definition: GEO_hires.c:205
#define DBL_MAX
Definition: make_L3_v1.1.c:60
const unsigned char INVALID_INPUT_DATA
#define SAMPLES_QKM
Definition: GEO_geo.h:83
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed as required for compatibility with version of the SDP toolkit Corrected test output file names to end in per delivery and then split off a new MYD_PR03 pcf file for Aqua Added AssociatedPlatformInstrumentSensor to the inventory metadata in MOD01 mcf and MOD03 mcf Created new versions named MYD01 mcf and MYD03 where AssociatedPlatformShortName is rather than Terra The program itself has been changed to read the Satellite Instrument validate it against the input L1A and LUT and to use it determine the correct files to retrieve the ephemeris and attitude data from Changed to produce a LocalGranuleID starting with MYD03 if run on Aqua data Added the Scan Type file attribute to the Geolocation copied from the L1A and attitude_angels to radians rather than degrees The accumulation of Cumulated gflags was moved from GEO_validate_earth_location c to GEO_locate_one_scan c
Definition: HISTORY.txt:464
#define DETECTORS_QKM
Definition: GEO_geo.h:87
PGE01 indicating that PGE02 PGE01 V6 for and PGE01 V2 for MOD03 were used to produce the granule By convention adopted in all MODIS Terra PGE02 code versions are The fourth digit of the PGE02 version denotes the LUT version used to produce the granule The source of the metadata environment variable ProcessingCenter was changed from a QA LUT value to the Process Configuration A sign used in error in the second order term was changed to a
Definition: HISTORY.txt:424