OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
GEO_prepare_mirr_data.c
Go to the documentation of this file.
1 #include <stdio.h>
2 #include "PGS_MODIS_35251.h"
3 #include "GEO_global_arrays.h"
4 #include "GEO_input.h"
5 #include "GEO_validation.h"
6 #include "GEO_earth.h"
7 #include "smfio.h"
8 
9 static int compare_double(
10  const void *first,
11  const void *second
12 ){ /* Designed for use with qsort() or bsearch() */
13  const double *left = (double*)first;
14  const double *right = (double*)second;
15  if(*left<*right)
16  return -1;
17  if(*left>*right)
18  return 1;
19  return 0;
20 }
21 
22 PGSt_SMF_status GEO_prepare_mirr_data(
23  uint16 earth_encoder_times[MAX_SCAN_NUMBER][ENCODER_LENGTH],
24  int16 view_sector_start[MAX_SCAN_NUMBER][SECTOR_LENGTH],
25  const mirror_preparation_struct *mirr_params,
26  double const t_frame,
28 )
29 /*****************************************************************************
30 !C
31 
32 !Description:
33  Routine in Input group of the Level-1A geolocation
34  software to unpack the mirror encoder data from the
35  encoder and sector start segments. For each scan, it
36  unpacks mirror encoder times from the encoder data,
37  computes encoder values from the sector start and mirror
38  side, and validates the data
39 
40 !Input Parameters:
41  earth_encoder_times Array of Earth View Encoder Time Data (time
42  samples at every 100th mirror encoder pulse
43  over the earth view)
44  view_sector_start array of View Sector Start Encoder Count and
45  Vernier Count words
46  mirr_params parameters describing the mirror operation.
47  t_frame The time interval between frames.
48 
49 !Input/Output Parameters:
50  l1a_data Data from the L1A file.
51 
52 !Output Parameters:
53  None
54 
55 Return Values:
56  MODIS_E_BAD_INPUT_ARG if input parameter check fails
57  PGS_S_SUCCESS otherwise
58 
59 Externally Defined:
60  BAD_DATA "GEO_validation.h"
61  CHAN_A "GEO_parameters.h"
62  ENCODER_LENGTH "GEO_geo.h"
63  GOOD_DATA "GEO_validation.h"
64  MAX_IMPULSE_NUMBER "GEO_geo.h"
65  MAX_SCAN_NUMBER "GEO_geo.h"
66  MODIS_E_BAD_INPUT_ARG "PGS_MODIS_35251.h"
67  MODIS_E_DATA_SCAN "PGS_MODIS_35251.h"
68  SECTOR_LENGTH "GEO_geo.h"
69  SUCCESS "GEO_basic.h"
70  PGS_S_SUCCESS "PGS_SMF.h"
71 
72 Called by: GEO_prepare_l1a_data
73 
74 Routines called:
75  GEO_del_limit_check "GEO_validation.h"
76  GEO_abs_limit_check "GEO_validation.h"
77  GEO_get_ancil_packet_time "GEO_earth.h"
78  modsmf "smfio.h"
79 
80 !Revision History:
81  $Log: GEO_prepare_mirr_data.c,v $
82  Revision 6.2 2010/06/18 20:41:04 kuyper
83  Corrected parameters that were pointers to array by removing their leading
84  'const' qualifiers.
85 
86  Revision 6.1 2010/04/08 18:49:59 kuyper
87  Corrected to have encoder_adjustment default to 0.
88  Replaced arguments derived from an l1a_data_struct with a pointer to the
89  l1a_data_struct that they came from.
90  Partially addressed Bug 17 by validating earth_encoder_times and EV_start
91  times using l1a_data->fill_values.
92  Corrected buffer underrun error.
93 
94  Revision 4.15 2004/06/14 22:21:41 kuyper
95  Corrected to not adjust bogus first time interval.
96 
97  Revision 4.14 2004/06/08 20:09:24 vlin
98  added if condition around line 263
99 
100  Revision 4.13 2004/06/04 23:35:46 kuyper
101  Corrected to not include bogus sample in statistics.
102  Corrected to re-initialize time_count for each scan during second pass.
103 
104  Revision 4.12 2004/06/01 22:17:28 kuyper
105  Corrected way that gap correction was applied.
106 
107  Revision 4.11 2004/05/21 22:59:26 kuyper
108  Corrected handling of missing mirror encoder times.
109 
110  Revision 4.9 2004/04/09 21:59:28 kuyper
111  Corrected handling of low-count cases.
112 
113  Revision 4.8 2004/04/09 17:50:43 vlin
114  replaced PACKET_PERIOD with mirr_params-> packet_interval.
115 
116  Revision 4.7 2004/03/30 22:56:33 kuyper
117  Corrected gap in flow of encoder values.
118 
119  Revision 4.6 2004/03/19 22:48:59 vlin
120  Added fill values check, changed to calibrate jump detection parameters
121  using current granule. Validate twice instead of once: once before calibration,
122  and second time after applying jump correction.
123 
124  Revision 4.5 2003/08/11 19:10:16 kuyper
125  Changed handling of first impulse time and encoder; needed by
126  GEO_interp_mirr_enc(), even if following time count is bad.
127 
128  Revision 4.4 2003/08/07 19:08:19 kuyper
129  Added GEO_earth.h to the #include list.
130 
131  Revision 4.3 2003/06/03 18:49:28 vlin
132  Corrected limits on final impulse loop according to PDL change
133 
134  Revision 4.2 2003/05/28 15:59:41 vlin
135  Changed to partially use and otherwise skip over bad data, rather than
136  replacing it with interpolated or extrapolated values.
137  Adjust encoder times for the difference in clock rates of the
138  encoder timer and the spacecraft clock.
139  Changed to return SMF status codes.
140  vlin@saicmodis.com
141 
142  Revision 4.1 2003/02/21 22:23:47 kuyper
143  Corrected pointer types used with %p.
144 
145 
146 Requirements:
147  PR03-F-2.5-1
148  PR03-F-2.5-2
149  PR03-F-2.5-3
150  PR03-F-2.5-4
151 
152 !Team-unique Header:
153 
154  This software is developed by the MODIS Science Data Support
155  Team for the National Aeronautics and Space Administration,
156  Goddard Space Flight Center, under contract NAS5-32373.
157 
158 Design Notes:
159  Assumes that sector_word[i] and vernier_word[i] < SECTOR_LENGTH
160  for i = 0 and i = 1.
161 
162 !END
163 **************************************************************************/
164 
165 {
166  const double TIME_ROLL_CORRECTION = 64000.0;
167  /* encoder time rollover correction */
168  const int SIDEB_OFF = 2; /* the offset in words of the encoder data */
169  int vernier = 0; /* vernier offset count */
170  int offset, scan, sample;
171  int num_impulse; /* count of good encoder values */
172  size_t best_low = 0, best_high = 0;
173  size_t num_times = 0, nnorm = 0, njump = 0;
174  size_t low, high;
175  double best = DBL_MAX;
176  double cum_sum[MAX_SCAN_NUMBER*MAX_IMPULSE_NUMBER];
177  double cum_sum2[MAX_SCAN_NUMBER*MAX_IMPULSE_NUMBER];
178  double sorted_times[MAX_SCAN_NUMBER*MAX_IMPULSE_NUMBER][2];
179  double norm_sum, jump_sum, norm_sum2;
180  double jump_sum2, criterion, phase;
181  int encoder_flags[MAX_IMPULSE_NUMBER] = {0};
182  /* validation flag for each encoder time delta */
183  double encoder_adjustment=0; /* The adjustment appropriate for mirr_chan. */
184  double factor=1.0; /* correction needed for the difference in speed */
185  /* of the encoder timer and the spacecraft clock */
186  double gap_start = 0.0, gap_end = 0.0;
187  /* range of impulse times that need adjustment */
188  double impulse_enc[MAX_IMPULSE_NUMBER]={0.0};
189  /* the Earth sector start encoder count state */
190  double impulse_time[MAX_IMPULSE_NUMBER]={0.0};
191  /* the current impulse time */
192  double time_count[MAX_IMPULSE_NUMBER] = {0.0}; /* encoder time deltas */
193  /* between every 100 encoder counts */
194  double mirr_del_limit, mirr_abs_limit[2];
195  char filefunc[] = __FILE__ ", GEO_prepare_mirr_data";
196  char msgbuf[256] = "";
197 
198 
199  if ((earth_encoder_times == NULL) || (view_sector_start == NULL) ||
200  (mirr_params == NULL) || (l1a_data == NULL))
201  {
202  sprintf(msgbuf, "earth_encoder_times:%p, view_sector_start:%p,\n"
203  "mirr_params:%p, l1a_data:%p", (void*)earth_encoder_times,
204  (void*)view_sector_start, (void*)mirr_params, (void*)l1a_data);
205  modsmf(MODIS_E_BAD_INPUT_ARG, msgbuf, filefunc);
206  return MODIS_E_BAD_INPUT_ARG;
207  }
208  if (l1a_data->num_scans > MAX_SCAN_NUMBER || l1a_data->num_scans < 0)
209  {
210  sprintf(msgbuf, "num_scans:%d", l1a_data->num_scans);
211  modsmf(MODIS_E_BAD_INPUT_ARG, msgbuf, filefunc);
212  return MODIS_E_BAD_INPUT_ARG;
213  }
214 
215  /* The first encoder time reported after the creation of a new engineering
216  * data packet usually shows a jump by a very consistent amount of time.
217  * the interval of time between packets is extremely reliable, so the
218  * affected encoder times can be reliably identified by taking the
219  * remainder of the absolute time at which the encoder time was recorded
220  * when divided by the packet interval. The encoder jumps are small enough
221  * that, over the time period covered by one scan, they can be ignored for
222  * purposes of calculating those absolute times:
223  */
224  for (scan = 0; scan < l1a_data->num_scans; scan++)
225  {
226  time_count[0] = (mirr_params->mirr_abs_limit[0] +
227  mirr_params->mirr_abs_limit[1]) / 2.0;
228  for (sample = 1; sample < MAX_IMPULSE_NUMBER-1; sample++)
229  {
230  if (earth_encoder_times[scan][sample]==MAX_UINT16_VAL ||
231  earth_encoder_times[scan][sample-1]==MAX_UINT16_VAL)
232  {
233  encoder_flags[sample] = BAD_DATA;
234  time_count[sample] = time_count[0]; /* Simplistic. */
235  }
236  else
237  {
238  encoder_flags[sample] = GOOD_DATA;
239  /* cast unsigned integers into larger, signed integers */
240  /* to ensure computation performed correctly. */
241  time_count[sample] =
242  (double)((int32)earth_encoder_times[scan][sample] -
243  (int32)earth_encoder_times[scan][sample-1]);
244  if(time_count[sample] < 0.0)
245  /* earth_encoder_time has rolled over */
246  time_count[sample] += TIME_ROLL_CORRECTION;
247  }
248  }
249 
250  /* Validate mirror side and encoder times */
251  if ((l1a_data->mirr_data[scan].mirr_side != 0 &&
252  l1a_data->mirr_data[scan].mirr_side != 1) ||
253  l1a_data->frame_data[scan].EV_start ==
254  l1a_data->fill_values.EV_start_time)
255  {
256  sprintf(msgbuf, "in scan %d", scan);
257  modsmf(MODIS_E_DATA_SCAN, msgbuf, filefunc);
258  }
259  else
260  { /* Unpack Earth sector start and vernier count */
261  offset = (l1a_data->mirr_data[scan].mirr_chan == CHAN_A
262  ? 0 : SIDEB_OFF);
263  impulse_enc[0] = (double)view_sector_start[scan]
264  [mirr_params->sector_word[l1a_data->mirr_data[scan].mirr_side]
265  + offset];
266  vernier = (int)view_sector_start[scan]
267  [mirr_params->vernier_word[l1a_data->mirr_data[scan].mirr_side]
268  + offset];
269  impulse_time[0] = - (double)(mirr_params->N_reset*t_frame
270  + vernier*mirr_params->t_vernier);
271 
272  /* For our initial estimates, we want to include those encoder
273  * times that contain jumps. Therefore, we relax the validation
274  * criteria:
275  */
276  mirr_abs_limit[0] = mirr_params->mirr_abs_limit[0]-32.0;
277  mirr_abs_limit[1] = mirr_params->mirr_abs_limit[1]+32.0;
278  mirr_del_limit = mirr_params->mirr_del_limit+32.0;
279 
280  /* No need to validate magnitude of first time. However, the delta
281  * between the first and the second might still need checking.
282  */
283  if(GEO_abs_limit_check(time_count+1, MAX_IMPULSE_NUMBER-2,
284  mirr_abs_limit, encoder_flags+1) != SUCCESS
285  || GEO_del_limit_check(time_count, MAX_IMPULSE_NUMBER-1,
286  mirr_del_limit, encoder_flags) != SUCCESS)
287  {
288  sprintf(msgbuf, "in scan %d", scan);
289  modsmf(MODIS_E_DATA_SCAN, msgbuf, filefunc);
290  }
291  else for (sample = 0; sample < MAX_IMPULSE_NUMBER; sample++)
292  {
293  if(sample)
294  {
295  impulse_enc[sample] = impulse_enc[sample-1] +
296  (double)mirr_params->sample_impulse;
297  impulse_time[sample] = impulse_time[sample-1] +
298  time_count[sample-1]*mirr_params->t_encoder;
299  /* Phase of encoder sample */
300  if (sample < (MAX_IMPULSE_NUMBER - 1))
301  {
302  sorted_times[num_times][0] =
303  fmod(l1a_data->frame_data[scan].EV_start
304  + impulse_time[sample], mirr_params->packet_interval);
305  sorted_times[num_times++][1] =
306  time_count[sample]*mirr_params->t_encoder;
307  }
308  }
309  l1a_data->mirr_data[scan].mirr_impulse_enc[sample] =
310  impulse_enc[sample];
311  l1a_data->mirr_data[scan].mirr_impulse_time[sample] =
312  impulse_time[sample];
313  }
314  }
315  } /* End of scan loop */
316 
317  if (num_times>3)
318  { /* Next, we determine empirically which range of values for the
319  * remainders identifies the affected encoder times, and also
320  * empirically determine the size of the adjustment.
321  */
322  qsort(sorted_times, num_times, sizeof(sorted_times[0]), compare_double);
323  cum_sum[0] = sorted_times[0][1];
324  cum_sum2[0] = cum_sum[0]*cum_sum[0];
325  for (low=1; low<num_times; low++)
326  {
327  cum_sum[low] = cum_sum[low-1] + sorted_times[low][1];
328  cum_sum2[low] = cum_sum2[low-1] +
329  sorted_times[low][1]*sorted_times[low][1];
330  }
331  /* Examine all possible intervals for the affected encoder times. The
332  * number of encoder times reported in a single granule is small enough
333  * to make this brute-force approach feasible.
334  */
335  for (low=0; low+2<num_times; low++)
336  {
337  for (high=low+1; high+2<num_times; high++)
338  {
339  if(sorted_times[low][0]+0.0175 < sorted_times[high][0] &&
340  sorted_times[high][0] < sorted_times[low][0]+0.0185)
341  {
342  njump = high-low;
343  nnorm = num_times - njump;
344  norm_sum = cum_sum[low] + cum_sum[num_times-1] - cum_sum[high];
345  jump_sum = cum_sum[high]-cum_sum[low];
346  norm_sum2 =
347  cum_sum2[low] + cum_sum2[num_times-1] - cum_sum2[high];
348  jump_sum2 = cum_sum2[high]-cum_sum2[low];
349  /* This is the standard deviation of the time counts in the
350  * jump region, plus the standard deviation of the time counts
351  * not in that region.
352  */
353  criterion = (norm_sum2-norm_sum*norm_sum/(double)nnorm) +
354  (jump_sum2-jump_sum*jump_sum/(double)njump);
355  if (criterion < best)
356  { /* The right jump region is the one that minimizes that sum */
357  best = criterion;
358  best_low = low;
359  best_high = high;
360  encoder_adjustment =
361  norm_sum/(double)nnorm - jump_sum/(double)njump;
362  }
363  }
364  }
365  }
366 
367  /* Determine the range of value for the remainders that correspond to
368  * tht_low and best_high values.
369  */
370  gap_start =
371  0.5*(sorted_times[best_low][0] + sorted_times[best_low+1][0]);
372  gap_end =
373  0.5*(sorted_times[best_high][0] + sorted_times[best_high+1][0]);
374  /* Because some time was dropped, the clock rate needs to be adjusted
375  * for that fact.
376  */
377  factor = 1.0 - encoder_adjustment/mirr_params->packet_interval;
378  }
379 
380  /* Now that we know how to identify the affected encoder times, correct
381  * those times and recalculate the absolute times accordingly.
382  */
383  for(scan = 0; scan < l1a_data->num_scans; scan++)
384  {
385  impulse_time[0] = l1a_data->mirr_data[scan].mirr_impulse_time[0];
386  impulse_enc[0] = l1a_data->mirr_data[scan].mirr_impulse_enc[0];
387  num_impulse = 0;
388  for(sample = 1; sample < MAX_IMPULSE_NUMBER; sample++)
389  {
390  if (earth_encoder_times[scan][sample] ==
391  l1a_data->fill_values.raw_mir_enc ||
392  earth_encoder_times[scan][sample-1] ==
393  l1a_data->fill_values.raw_mir_enc)
394  encoder_flags[sample] = BAD_DATA;
395  else
396  encoder_flags[sample] = GOOD_DATA;
397  impulse_enc[sample] =
398  l1a_data->mirr_data[scan].mirr_impulse_enc[sample];
399  impulse_time[sample] = impulse_time[sample-1] +
400  factor*(l1a_data->mirr_data[scan].mirr_impulse_time[sample]-
401  l1a_data->mirr_data[scan].mirr_impulse_time[sample-1]);
402  if(sample>1)
403  {
404  phase = fmod(l1a_data->frame_data[scan].EV_start +
405  l1a_data->mirr_data[scan].mirr_impulse_time[sample-1],
406  mirr_params->packet_interval);
407  if (phase > gap_start && phase < gap_end)
408  impulse_time[sample] += encoder_adjustment;
409  time_count[sample-1] = (impulse_time[sample] -
410  impulse_time[sample-1]) / mirr_params->t_encoder;
411  }
412  }
413 
414  /* Now that the encoder time jumps have been corrected, we can
415  * re-validate using stricter limits.
416  */
417  if(GEO_abs_limit_check(time_count+1, MAX_IMPULSE_NUMBER-2,
418  (double*)mirr_params->mirr_abs_limit, encoder_flags+1) != SUCCESS
419  || GEO_del_limit_check(time_count, MAX_IMPULSE_NUMBER-1,
420  mirr_params->mirr_del_limit, encoder_flags) != SUCCESS)
421  {
422  sprintf(msgbuf, "in scan %d", scan);
423  modsmf(MODIS_E_DATA_SCAN, msgbuf, filefunc);
424  l1a_data->mirr_data[scan].impulse_flag = BAD_DATA;
425  }
426  for(sample = 0; sample < MAX_IMPULSE_NUMBER; sample++)
427  {
428  if( (encoder_flags[sample] == GOOD_DATA) ||
429  (sample && encoder_flags[sample-1] == GOOD_DATA) )
430  {
431  l1a_data->mirr_data[scan].mirr_impulse_enc[num_impulse] =
432  impulse_enc[sample];
433  l1a_data->mirr_data[scan].mirr_impulse_time[num_impulse] =
434  impulse_time[sample];
435  num_impulse++;
436  }
437  }
438  l1a_data->mirr_data[scan].num_impulse = num_impulse;
439  }
440 
441  return PGS_S_SUCCESS;
442 }
integer, parameter int16
Definition: cubeio.f90:3
int GEO_abs_limit_check(double data_samples[], int const number_of_samples, double data_limits[2], int sample_flags[])
#define SUCCESS
Definition: ObpgReadGrid.h:15
int GEO_del_limit_check(double data_samples[], int const number_of_samples, double const delta_limit, int sample_flags[])
#define NULL
Definition: decode_rs.h:63
#define MODIS_E_BAD_INPUT_ARG
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
int16_t * l1a_data
Definition: l1a_seawifs.c:84
const int BAD_DATA
character(len=1000) if
Definition: names.f90:13
#define MAX_UINT16_VAL
Definition: GEO_geo.h:175
const int SECTOR_LENGTH
#define MODIS_E_DATA_SCAN
#define CHAN_A
const int GOOD_DATA
PGSt_SMF_status GEO_prepare_mirr_data(uint16 earth_encoder_times[MAX_SCAN_NUMBER][ENCODER_LENGTH], int16 view_sector_start[MAX_SCAN_NUMBER][SECTOR_LENGTH], const mirror_preparation_struct *mirr_params, double const t_frame, l1a_data_struct *l1a_data)
integer, parameter double
subroutine phase
Definition: phase.f:2
const int MAX_SCAN_NUMBER
#define MAX_IMPULSE_NUMBER
Definition: GEO_geo.h:91
double mirr_abs_limit[MAX_LIMIT_CHECK]
#define DBL_MAX
Definition: make_L3_v1.1.c:60
l2prod offset
#define ENCODER_LENGTH
Definition: GEO_geo.h:111
int num_impulse[MAX_SCAN_NUMBER]