OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
l1a_hawkeye.c
Go to the documentation of this file.
1 /* ============================================================================ */
2 /* module l1a_hawkeye.c - functions to read HAWKEYE L1A for MSL12 */
3 /* Written By: Steve Lockhart SAIC May 2018 */
4 /* -Started with l1_viirs_nc.c and made changes to support hawkeye. */
5 /* */
6 /* ============================================================================ */
7 
8 /* Issues:
9  open:
10  -What is spatial resolution? 120m?
11  missing fields:
12  -Setting orbit number to 0, as it is currently not in L1A.
13  -scanQualityId skipped for now
14  -Also skipping att angle, so ripples to "Compute polarization rotation angles"
15  general:
16  -Clean up commented out sections (i.e. the ones that are VIIRS-specific)
17  -What are finder_pixels, finder_lines in L1A file?
18  -What are dark_pixels in L1A file?
19  -Does last line get run twice?
20 */
21 
22 // Includes from l1_viirs_nc.c
23 #include <nc4utils.h>
24 #include "l1.h"
25 #include "libnav.h"
26 #include <productInfo.h>
27 #include <allocate2d.h>
28 #include <allocate3d.h>
29 
30 // Includes from demo_cal.c
31 #include <stdio.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <stdlib.h>
35 #include <fcntl.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include <libgen.h>
40 #include <math.h>
41 #include <timeutils.h>
42 #include <gsl/gsl_math.h>
43 #include <gsl/gsl_spline.h>
44 #include <gsl/gsl_sort.h>
45 #include <gsl/gsl_statistics.h>
46 #include <stdbool.h>
47 
48 // New includes
49 #include "l1a_hawkeye.h"
50 
51 static int geoFileId;
52 static int geoNavigationGrp;
53 static int geoGeolocationGrp;
54 static int geoScanLineGrp;
55 static int l1aScanLineGrp;
56 static int l1aTelemetryGrp;
57 static int l1aNavigationGrp;
58 static int l1aEarthViewGrp;
59 static int lonId, latId, senzId, senaId, solzId, solaId,
60  angId, posId, velId, pixelQualityId;
61 static int scanDeltaTimeId;
62 static float *Fobar; // reflectance to radiance conversion factors
63 
64 static short *tmpShort;
65 static unsigned char *tmpByte;
66 static size_t num_scans, num_pixels, num_bands;
67 
68 static int firstCall = 1;
69 static double starttime;
70 static double lastvalidtime;
71 static int lastvalidscan = 0;
72 static double time_interval;
73 
74 static float latGeoFillValue = -999.9;
75 static float lonGeoFillValue = -999.9;
76 static short senzGeoFillValue = -32768;
77 static short senaGeoFillValue = -32768;
78 static short solzGeoFillValue = -32768;
79 static short solaGeoFillValue = -32768;
80 
81 static float latL2FillValue = -999.0;
82 static float lonL2FillValue = -999.0;
83 static float senzL2FillValue = -32767;
84 static float senaL2FillValue = -32767;
85 static float solzL2FillValue = -32767;
86 static float solaL2FillValue = -32767;
87 
88 static int32_t scanDeltaTimeFillValue = -999;
89 static int32_t scanDeltaTimeValidMin = 0;
90 static int32_t scanDeltaTimeValidMax = 200000;
91 
92 // Declarations added for hawkeye L1A.
93 static size_t num_ccd_temps, num_tlm_blocks, num_tlm_blocks_cleansed;
94 static double **CCD_temperatures; // [num_tlm_blocks][num_ccd_temps]
95 static double **CCD_temperatures_cleansed; // up to [num_tlm_blocks][num_ccd_temps]
96 static double CCD_temperature_default = 35.0; // same as K3T[3], used iff all CCD_temperatures are FILL
97 static double *tlm_delta_time_ms; // [num_tlm_blocks], ms since start of image
98 static double *tlm_delta_time_ms_cleansed; // up to [num_tlm_blocks]
99 static float fill_value_in_CCD_T;
100 static int *dn_varid; // [num_bands]
101 static double *CCD_temperatures_this_scan; // [num_ccd_temps] i.e. interpolated in time
102 static unsigned short *dn; // [num_pixels], one scan, one band, before cal
103 static double **bkg_avg; // [num_bands][num_pixels], avg dn to subtract
104 static double **Lt; // [num_bands][num_pixels], one scan, after cal
105 static uint32_t darkSubtracted;
106 static uint32_t darkHeight;
107 
108 // radiometric calibration parameters
109 static size_t cal_num_dates;
110 static size_t cal_num_temperatures;
111 static double *time_ref; // [cal_num_dates]
112 static double *temperature_ref; // [cal_num_temperatures]
113 static double *K1; // Gain at nadir pixel, [num_bands]
114 static double ***K2; // Temporal correction, BEFORE interpolation, [num_bands][num_pixels][cal_num_dates]
115 static double **K3; // Temperature correction BEFORE interpolation, [num_bands][cal_num_temperatures]
116 static float **K4; // RVS, [num_bands][num_pixels]
117 static double **K5; // Non-linearity (quadratic term), [num_bands][num_pixels]
118 static float **K7; // smile correction [num_bands][num_pixels]
119 static unsigned short **nominal_dark_counts;
120 
121 // band co-registration parameters
122 static int *track_offset; // [num_bands]
123 static int *ccd_offset; // [num_bands]
124 static float *focal_length; // [num_bands]
125 static int reference_band;
126 static int reference_pixel;
127 
128 // background subtraction and cropping parameters
129 static int skip_beglines;
130 static int skip_endlines;
131 static int shutter_rampdown;
132 static int default_darkrows;
133 static uint32_t firstbad;
134 
135 // Data cleansing (one-time)
136 void qc_hawkeye_CCD_T();
137 // Calibration-related (one-time)
138 void read_cal_hawkeye(char *cal_path);
139 // Calibration-related (per scan)
140 void interp_hawkeye_CCD_T(double delta_time_ms_i);
141 void calibrate_hawkeye(double current_julian_date, int line);
142 // Misc utilities
143 double nan_wmean(double *weights, double *data, size_t n, double fill_value);
144 void prep_for_interp_double(double *xi, double *x, int N);
145 
161 int openl1a_hawkeye(filehandle *file) {
162  char *fltime;
163 
164  int ncid_L1A, varid, dimid, status;
165  size_t att_len;
166  int orbit_number;
167  int band_num;
168  char nc_search_string[10] = ""; // band_X
169 
170  // Open the netcdf4 input file
171  if(want_verbose)
172  printf("Opening Hawkeye L1A file\n");
173  status = nc_open(file->name, NC_NOWRITE, &ncid_L1A);
174  if (status != NC_NOERR) {
175  fprintf(stderr, "-E- Error opening L1A file \"%s\": %s\n",
176  file->name, nc_strerror(status));
177  exit(EXIT_FAILURE);
178  }
179 
180  file->private_data = malloc(sizeof(hawkeye_t));
181  hawkeye_t *data = (hawkeye_t*)(file->private_data);
182  // Get dims from L1A file
183  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_L1A, "number_of_scans", &dimid));
184  nc_inq_dimlen(ncid_L1A, dimid, &num_scans);
185  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_L1A, "number_of_bands", &dimid));
186  nc_inq_dimlen(ncid_L1A, dimid, &num_bands);
187  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_L1A, "number_of_pixels", &dimid));
188  nc_inq_dimlen(ncid_L1A, dimid, &num_pixels);
189  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_L1A, "ccd_temps", &dimid));
190  nc_inq_dimlen(ncid_L1A, dimid, &num_ccd_temps);
191  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_L1A, "number_of_tlm_blocks", &dimid));
192  nc_inq_dimlen(ncid_L1A, dimid, &num_tlm_blocks);
193  if (want_verbose) {
194  printf("Hawkeye L1A npix = %d; nscans = %d; nbands = %d\n",
195  (int) num_pixels, (int) num_scans, (int) num_bands);
196  }
197 
198  // Get hawkeye calibration coefficients from cal file
199  // This sets static cal coeffs: K1-K5 as well as time_ref and temperature_ref
200  // Do now to get start, end line adjustments
201  read_cal_hawkeye(l1_input->calfile);
202 
203  // Now that we know dims, prep for additional hawkeye one-time reads by allocating memory
204  // for arrays (declared static above). (Memory is freed in closel1a_hawkeye.)
205  CCD_temperatures = allocate2d_double(num_tlm_blocks, num_ccd_temps);
206  CCD_temperatures_cleansed = allocate2d_double(num_tlm_blocks, num_ccd_temps);
207  tlm_delta_time_ms = (double *) calloc(num_tlm_blocks, sizeof(double));
208  tlm_delta_time_ms_cleansed = (double *) calloc(num_tlm_blocks, sizeof(double));
209  dn_varid = (int *) calloc(num_bands, sizeof(int));
210 
211  // Get group id from L1A file for GROUP scan_line_attributes.
212  if ((nc_inq_grp_ncid(ncid_L1A, "scan_line_attributes", &l1aScanLineGrp))
213  != NC_NOERR) {
214  fprintf(stderr, "-E- Error finding group \"scan_line_attributes\".\n");
215  exit(EXIT_FAILURE);
216  }
217  TRY_NC(__FILE__, __LINE__,
218  nc_inq_varid(l1aScanLineGrp, "delta_time", &scanDeltaTimeId));
219  TRY_NC(__FILE__, __LINE__,
220  nc_get_att_int(l1aScanLineGrp, scanDeltaTimeId, "_FillValue",
221  &scanDeltaTimeFillValue));
222  TRY_NC(__FILE__, __LINE__,
223  nc_get_att_int(l1aScanLineGrp, scanDeltaTimeId, "valid_min",
224  &scanDeltaTimeValidMin));
225  TRY_NC(__FILE__, __LINE__,
226  nc_get_att_int(l1aScanLineGrp, scanDeltaTimeId, "valid_max",
227  &scanDeltaTimeValidMax));
228 
229  // Get CCD_temperatures, tlm_delta_time_ms from L1A file from GROUP parameters_telemetry_data
230  if ((nc_inq_grp_ncid(ncid_L1A, "parameters_telemetry_data", &l1aTelemetryGrp))
231  != NC_NOERR) {
232  fprintf(stderr, "-E- Error finding group \"parameters_telemetry_data\".\n");
233  exit(EXIT_FAILURE);
234  }
235  if ((nc_inq_grp_ncid(ncid_L1A, "navigation_data", &l1aNavigationGrp))
236  != NC_NOERR) {
237  fprintf(stderr, "-E- Error finding group \"navigation_data\" in the L1A file.\n");
238  exit(EXIT_FAILURE);
239  }
240  // Get CCD_temperatures from this GROUP
241  TRY_NC(__FILE__, __LINE__,
242  nc_inq_varid(l1aTelemetryGrp, "CCD_temperatures", &varid));
243  TRY_NC(__FILE__, __LINE__,
244  nc_get_var_double(l1aTelemetryGrp, varid, &CCD_temperatures[0][0]));
245  if (nc_inq_var_fill(l1aTelemetryGrp, varid, NULL, &fill_value_in_CCD_T)
246  != NC_NOERR) {
247  fprintf(stderr, "-W- Using default fill value of -999 for CCD_temperatures.\n");
248  fill_value_in_CCD_T = -999.0;
249  }
250  // Get tlm_time_stamp from this GROUP
251  TRY_NC(__FILE__, __LINE__,
252  nc_inq_varid(l1aTelemetryGrp, "tlm_time_stamp", &varid));
253  TRY_NC(__FILE__, __LINE__,
254  nc_get_var_double(l1aTelemetryGrp, varid, &tlm_delta_time_ms[0]));
255 
256  // was background subtracted on spacecraft?
257  if (nc_get_att_uint(l1aTelemetryGrp, NC_GLOBAL, "darkSubtracted", &darkSubtracted)
258  != NC_NOERR)
259  darkSubtracted = 0; // assume no, if attribute not present
260 
261  // determine number of dark lines at end of image
262  if (nc_get_att_uint(l1aTelemetryGrp, NC_GLOBAL, "darkHeight", &darkHeight)
263  != NC_NOERR)
264  darkHeight = default_darkrows; // default, if attribute not present
265 
266  // Adjust start and end lines
267  if (want_verbose) {
268  printf("Will skip first %d and last %d scans.\n",
269  (int) skip_beglines, (int) darkHeight);
270  }
271  firstbad = num_scans - darkHeight;
272 
273  // Cleanse CCD_temperatures (and corresponding tlm_delta_time_ms), handling fill values
274  // This sets static vars CCD_temperatures_cleansed, tlm_delta_time_ms_cleansed, num_tlm_blocks_cleansed
276 
277  // Get attribute values (from l1_viirs_nc.c) e.g. "time_coverage_start" and "time_coverage_end" to derive
278  // time_interval etc. Note that time_coverage_end is the START of the last scan.
279 
280  // get start time
281  TRY_NC(__FILE__, __LINE__,
282  nc_inq_attlen(ncid_L1A, NC_GLOBAL, "time_coverage_start", &att_len));
283  fltime = (char *) malloc(att_len + 1); // required space + 1 for trailing null
284  TRY_NC(__FILE__, __LINE__,
285  nc_get_att_text(ncid_L1A, NC_GLOBAL, "time_coverage_start", fltime));
286  fltime[att_len] = '\0';
287 
288  // Convert "time_coverage_start" ISO string to unix (seconds since 1/1/1970)
289  starttime = lastvalidtime = isodate2unix(fltime);
290  free(fltime);
291 
292  // get end time
293  TRY_NC(__FILE__, __LINE__,
294  nc_inq_attlen(ncid_L1A, NC_GLOBAL, "time_coverage_end", &att_len));
295  fltime = (char *) malloc(att_len + 1); // required space + 1 for trailing null
296  TRY_NC(__FILE__, __LINE__,
297  nc_get_att_text(ncid_L1A, NC_GLOBAL, "time_coverage_end", fltime));
298  fltime[att_len] = '\0';
299 
300  // Convert "time_coverage_stop" ISO string to unix (seconds since 1/1/1970)
301  double stoptime = isodate2unix(fltime);
302  free(fltime);
303 
304  // time_interval may be used in readl1a_hawkeye if there is not a good scan time (per scan)
305  time_interval = (stoptime - starttime) / (num_scans - 1); // secs per scan
306 
307  // orbit number
308  if (nc_get_att_int(ncid_L1A, NC_GLOBAL, "orbit_number", &orbit_number) != NC_NOERR)
309  orbit_number = 0;
310 
311  // exposure_ID, roll and time offset
312  if (nc_get_att_int(l1aTelemetryGrp, NC_GLOBAL, "exposureID", &data->exposureID) != NC_NOERR)
313  data->exposureID = 0;
314  if (nc_get_att_float(l1aNavigationGrp, NC_GLOBAL, "time_offset", &data->time_offset) != NC_NOERR)
315  data->time_offset = 0.;
316  if (nc_get_att_float(l1aNavigationGrp, NC_GLOBAL, "roll_offset", &data->roll_offset) != NC_NOERR)
317  data->roll_offset = 0.;
318 
319  // Identify the "earth_view_data" GROUP and its "band_%d" vars, to be used later by readl1a_hawkeye
320  // Store the ids in static variables so we don't have to do nc_inq_grp_ncid per scan.
321  if ((nc_inq_grp_ncid(ncid_L1A, "earth_view_data", &l1aEarthViewGrp)) != NC_NOERR) {
322  fprintf(stderr, "-E- Error finding group \"earth_view_data\".\n");
323  exit(EXIT_FAILURE);
324  }
325  for (band_num = 0; band_num < num_bands; band_num++) {
326  sprintf(nc_search_string, "band_%d", band_num + 1);
327  TRY_NC(__FILE__, __LINE__,
328  nc_inq_varid(l1aEarthViewGrp, nc_search_string, &dn_varid[band_num]));
329  }
330 
331  file->sd_id = ncid_L1A;
332  file->nbands = num_bands;
333  file->npix = num_pixels;
334  file->nscan = num_scans - skip_beglines - darkHeight;
335  file->ndets = 1;
336  file->terrain_corrected = 0;
337  file->orbit_number = orbit_number;
338  strcpy(file->spatialResolution, "120 m");
339 
340  rdsensorinfo(file->sensorID, l1_input->evalmask,
341  "Fobar", (void **) &Fobar);
342 
343  // Setup geofile pointers
344  if (file->geofile && file->geofile[0]) {
345  if (want_verbose)
346  printf("Opening Hawkeye GEO file\n");
347  status = nc_open(file->geofile, NC_NOWRITE, &geoFileId);
348  if (status != NC_NOERR) {
349  fprintf(stderr, "-E- Error opening GEO file \"%s\": %s\n",
350  file->geofile,
351  nc_strerror(status));
352  exit(EXIT_FAILURE);
353  }
354 
355  if ((nc_inq_grp_ncid(geoFileId, "geolocation_data", &geoGeolocationGrp))
356  != NC_NOERR) {
357  fprintf(stderr, "-E- Error finding group \"geolocation_data\".\n");
358  exit(EXIT_FAILURE);
359  }
360  TRY_NC(__FILE__, __LINE__,
361  nc_inq_varid(geoGeolocationGrp, "longitude", &lonId));
362  TRY_NC(__FILE__, __LINE__,
363  nc_inq_var_fill(geoGeolocationGrp, lonId, NULL, &lonGeoFillValue));
364  TRY_NC(__FILE__, __LINE__,
365  nc_inq_varid(geoGeolocationGrp, "latitude", &latId));
366  TRY_NC(__FILE__, __LINE__,
367  nc_inq_var_fill(geoGeolocationGrp, latId, NULL, &latGeoFillValue));
368  TRY_NC(__FILE__, __LINE__,
369  nc_inq_varid(geoGeolocationGrp, "sensor_zenith", &senzId));
370  TRY_NC(__FILE__, __LINE__,
371  nc_inq_var_fill(geoGeolocationGrp, senzId, NULL, &senzGeoFillValue));
372  TRY_NC(__FILE__, __LINE__,
373  nc_inq_varid(geoGeolocationGrp, "sensor_azimuth", &senaId));
374  TRY_NC(__FILE__, __LINE__,
375  nc_inq_var_fill(geoGeolocationGrp, senaId, NULL, &senaGeoFillValue));
376  TRY_NC(__FILE__, __LINE__,
377  nc_inq_varid(geoGeolocationGrp, "solar_zenith", &solzId));
378  TRY_NC(__FILE__, __LINE__,
379  nc_inq_var_fill(geoGeolocationGrp, solzId, NULL, &solzGeoFillValue));
380  TRY_NC(__FILE__, __LINE__,
381  nc_inq_varid(geoGeolocationGrp, "solar_azimuth", &solaId));
382  TRY_NC(__FILE__, __LINE__,
383  nc_inq_var_fill(geoGeolocationGrp, solaId, NULL, &solaGeoFillValue));
384  TRY_NC(__FILE__, __LINE__,
385  nc_inq_varid(geoGeolocationGrp, "quality_flag", &pixelQualityId));
386 
387  if ((nc_inq_grp_ncid(geoFileId, "navigation_data", &geoNavigationGrp))
388  != NC_NOERR) {
389  fprintf(stderr, "-E- Error finding group \"navigation_data\".\n");
390  exit(EXIT_FAILURE);
391  }
392 
393  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoNavigationGrp, "att_ang", &angId));
394  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoNavigationGrp, "orb_pos", &posId));
395  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoNavigationGrp, "orb_vel", &velId));
396 
397  if ((nc_inq_grp_ncid(geoFileId, "scan_line_attributes", &geoScanLineGrp))
398  != NC_NOERR) {
399  fprintf(stderr, "-E- Error finding group \"scan_line_attributes\".\n");
400  exit(EXIT_FAILURE);
401  }
402  // The following field is not yet in the GEO file
403  // TRY_NC(__FILE__, __LINE__,
404  // nc_inq_varid(geoScanLineGrp, "scan_quality", &scanQualityId));
405  } // geofile
406 
407  // Setup the fill values for the geo products
408  productInfo_t* info = allocateProductInfo();
409  status = findProductInfo("lat", HAWKEYE, info);
410  if (status)
411  latL2FillValue = info->fillValue;
412  status = findProductInfo("lon", HAWKEYE, info);
413  if (status)
414  lonL2FillValue = info->fillValue;
415  status = findProductInfo("sena", HAWKEYE, info);
416  if (status)
417  senaL2FillValue = info->fillValue;
418  status = findProductInfo("senz", HAWKEYE, info);
419  if (status)
420  senzL2FillValue = info->fillValue;
421  status = findProductInfo("sola", HAWKEYE, info);
422  if (status)
423  solaL2FillValue = info->fillValue;
424  status = findProductInfo("solz", HAWKEYE, info);
425  if (status)
426  solzL2FillValue = info->fillValue;
427 
428  freeProductInfo(info);
429  return (LIFE_IS_GOOD);
430 }
431 
436  int band_num, pixel_num;
437 
438  // initialize
439  for (pixel_num = 0; pixel_num < num_pixels; pixel_num++)
440  for (band_num = 0; band_num < num_bands; band_num++)
441  bkg_avg[band_num][pixel_num] = nominal_dark_counts[band_num][pixel_num];
442 
443 // if dark was not captured, actually earth view, then apply the nominal dark counts in .cal file
444  if (!darkSubtracted) {
445 
446  // if a full 6000 line scene, calculate dark otherwise assume no valid dark scans
447  // and stick with the nominal_dark_counts from the LUT
448  if (num_scans == 6000) {
449  // find dark lines boundaries
450  size_t begline = num_scans - darkHeight + shutter_rampdown;
451  size_t endline = num_scans - skip_endlines - 1;
452  size_t num_darklines = endline - begline + 1;
453 
454  size_t start[] = { 0, 0 };
455  size_t count[] = { 1, 1 };
456  start[0] = begline;
457  count[0] = num_darklines;
458  start[1] = 0;
459  count[1] = num_pixels;
460 
461  // read data for each band
462  unsigned short **dn_dark = (unsigned short**) allocate2d_short(num_darklines,
463  num_pixels);
464  memset((&dn_dark[0][0]), 0, num_darklines*num_pixels*sizeof(unsigned short));
465 
466  for (band_num = 0; band_num < num_bands; band_num++) {
467  nc_get_vara_ushort(l1aEarthViewGrp, dn_varid[band_num],
468  start, count, &dn_dark[0][0]);
469 
470  // calculate simple average in pixel dimension
471  for (pixel_num = 0; pixel_num < num_pixels; pixel_num++) {
472  unsigned long sum_vals = 0;
473  for (size_t line_num = 0; line_num < num_darklines; line_num++)
474  sum_vals += dn_dark[line_num][pixel_num];
475  bkg_avg[band_num][pixel_num] = (double) sum_vals
476  / (double) num_darklines;
477  }
478 
479  }
480  free2d_short((short**) dn_dark);
481  }
482  }
483 }
484 
499 int readl1a_hawkeye(filehandle *file, int32_t oline, l1str *l1rec) {
500 
501  int i;
502  double scan_sec;
503  int16_t scan_year, scan_day;
504  double Lt_interp;
505 
506  size_t start[] = { 0, 0 };
507  size_t count[] = { 1, 1 };
508 
509  // Additional declarations for hawkeye
510  int band_num, pixel_num;
511  int16_t scan_month, scan_dom;
512  double current_julian_date;
513  int32_t scan_delta_time_ms; // ms since start of image per scan
514 
515  l1rec->npix = file->npix;
516  for (int32_t ip = 0; ip < num_pixels; ip++) {
517  l1rec->pixnum[ip] = ip;
518  }
519 
520  // If first call,
521  if (firstCall) {
522  firstCall = 0;
523 
524  // One-time memory allocations
525  tmpShort = (short *) malloc(num_pixels * sizeof(short));
526  tmpByte = (unsigned char *) malloc(num_pixels);
527  dn = (unsigned short*) calloc(num_pixels, sizeof(unsigned short));
528  bkg_avg = allocate2d_double(num_bands, num_pixels);
529  Lt = allocate2d_double(num_bands, num_pixels);
530  CCD_temperatures_this_scan = (double *) calloc(num_ccd_temps, sizeof(double));
531 
532  // Calculate dark counts for background subtraction
533  calc_bkg_hawkeye(); // sets bkg_avg
534 
535  }
536  int32_t line = oline + skip_beglines;
537 
538  // Time
539  // Get delta_time
540  start[0] = line;
541  count[0] = 1; // 1 scan at a time
542  count[1] = 0;
543  TRY_NC(__FILE__, __LINE__,
544  nc_get_vara_int(l1aScanLineGrp, scanDeltaTimeId, start, count,
545  &scan_delta_time_ms));
546  // Set lastvalidtime, the start of this scan (in secs since 1/1/1970)
547  if ((scan_delta_time_ms == scanDeltaTimeFillValue) ||
548  (scan_delta_time_ms < scanDeltaTimeValidMin) ||
549  (scan_delta_time_ms > scanDeltaTimeValidMax)) {
550  l1rec->scantime = lastvalidtime + (time_interval * (line - lastvalidscan));
551  } else {
552  lastvalidtime = starttime + scan_delta_time_ms / 1000.0;
553  lastvalidscan = line;
554  l1rec->scantime = lastvalidtime;
555  }
556 
557  // Set scan_year, scan_day, scan_sec
558  unix2yds(l1rec->scantime, &scan_year, &scan_day, &scan_sec);
559  // Convert lastvalidtime to julian date
560  yd2md(scan_year, scan_day, &scan_month, &scan_dom);
561  current_julian_date = jday(scan_year, scan_month, scan_dom)
562  + (scan_sec / (24 * 3600.0)) - 0.5; //subtract half a day, as julian time is referenced to noon not midnight
563 
564  // Before calibrating this hawkeye scan, we need to interpolate the CCD_temperatures_cleansed (in time).
565  // The resulting interpolated array will be CCD_temperatures_this_scan[num_ccd_temps], which is needed by
566  // calibrate_hawkeye. Note that the interpolation routine requires the arrays to be of type double.
567  interp_hawkeye_CCD_T((double) scan_delta_time_ms);
568 
569  /*
570  Apply instrument calibration to this scan
571  includes:
572  - dark count subtraction
573  - along-track offset registration
574  - application of calibration coefficients
575  */
576  calibrate_hawkeye(current_julian_date, line);
577 
578  for (band_num = 0; band_num < num_bands; band_num++) {
579 
580  // select input line according to track offset
581  // read only if valid
582  int inputline = line + track_offset[band_num];
583  if ((-1 < inputline) && (inputline < firstbad)) {
584  // apply along-CCD offset and focal length adustments to co-register each pixel
585  for (pixel_num = 0; pixel_num < num_pixels; pixel_num++) {
586 
587  float inputpix = pixel_num + ccd_offset[band_num] +
588  ((focal_length[band_num] / focal_length[reference_band] - 1) *
589  (pixel_num - reference_pixel));
590  int ipix = floor(inputpix);
591  float fpix = inputpix - ipix;
592 
593  // if valid, interpolate and store into l1rec->Lt
594  if ((-1 < ipix) && (ceil(inputpix) < num_pixels)) {
595  Lt_interp = Lt[band_num][ipix] * (1 - fpix);
596  if (fpix > 0)
597  Lt_interp += Lt[band_num][ipix + 1] * fpix;
598 
599  // ipb = ip * nbands + ib
600  l1rec->Lt[pixel_num * num_bands + band_num] = (float) Lt_interp / 10.0;
601  }
602  } // pixel_num
603 
604  } // valid adjusted input line
605 
606  } // band_num
607 
608  // If a GEO file was provided, use it...
609  if (file->geofile && file->geofile[0]) {
610 
611  // set up to read all pixels of the line.
612  start[0] = line;
613  start[1] = 0;
614  count[0] = 1;
615  count[1] = num_pixels; // read all pixels
616 
617  TRY_NC(__FILE__, __LINE__,
618  nc_get_vara_float(geoGeolocationGrp, latId, start, count, l1rec->lat));
619  for (i = 0; i < num_pixels; i++)
620  if (l1rec->lat[i] == latGeoFillValue)
621  l1rec->lat[i] = latL2FillValue;
622 
623  TRY_NC(__FILE__, __LINE__,
624  nc_get_vara_float(geoGeolocationGrp, lonId, start, count, l1rec->lon));
625  for (i = 0; i < num_pixels; i++)
626  if (l1rec->lon[i] == lonGeoFillValue)
627  l1rec->lon[i] = lonL2FillValue;
628 
629  TRY_NC(__FILE__, __LINE__,
630  nc_get_vara_short(geoGeolocationGrp, solzId, start, count, tmpShort));
631  for (i = 0; i < num_pixels; i++)
632  if (tmpShort[i] == solzGeoFillValue)
633  l1rec->solz[i] = solzL2FillValue;
634  else
635  l1rec->solz[i] = tmpShort[i] * 0.01;
636 
637  TRY_NC(__FILE__, __LINE__,
638  nc_get_vara_short(geoGeolocationGrp, solaId, start, count, tmpShort));
639  for (i = 0; i < num_pixels; i++)
640  if (tmpShort[i] == solaGeoFillValue)
641  l1rec->sola[i] = solaL2FillValue;
642  else
643  l1rec->sola[i] = tmpShort[i] * 0.01;
644 
645  TRY_NC(__FILE__, __LINE__,
646  nc_get_vara_short(geoGeolocationGrp, senzId, start, count, tmpShort));
647  for (i = 0; i < num_pixels; i++)
648  if (tmpShort[i] == senzGeoFillValue)
649  l1rec->senz[i] = senzL2FillValue;
650  else
651  l1rec->senz[i] = tmpShort[i] * 0.01;
652 
653  TRY_NC(__FILE__, __LINE__,
654  nc_get_vara_short(geoGeolocationGrp, senaId, start, count, tmpShort));
655  for (i = 0; i < num_pixels; i++)
656  if (tmpShort[i] == senaGeoFillValue)
657  l1rec->sena[i] = senaL2FillValue;
658  else
659  l1rec->sena[i] = tmpShort[i] * 0.01;
660 
661  // Load Angles
662  float ang[3]; // degrees
663  float pos[3]; // km
664  float vel[3]; // km/sec
665  size_t s3[] = { line, 0 };
666  size_t c3[] = { 1, 3 };
667 
668  TRY_NC(__FILE__, __LINE__, nc_get_vara_float(geoNavigationGrp, angId, s3, c3, ang));
669  TRY_NC(__FILE__, __LINE__, nc_get_vara_float(geoNavigationGrp, posId, s3, c3, pos));
670  TRY_NC(__FILE__, __LINE__, nc_get_vara_float(geoNavigationGrp, velId, s3, c3, vel));
671  for (i = 0; i < 3; i++) {
672  pos[i] /= 1000.; // m -> km
673  vel[i] /= 1000.; // m/s -> km/s
674  }
675 
676  // Compute polarization rotation angles
677  float sen_mat[3][3], coeff[10];
678  double mnorm[3];
679  ocorient_(pos, vel, ang, sen_mat, coeff);
680  for (i = 0; i < 3; i++)
681  mnorm[i] = sen_mat[i][0];
682  compute_alpha(l1rec->lon, l1rec->lat,
683  l1rec->senz, l1rec->sena,
684  mnorm, l1rec->npix, l1rec->alpha);
685 
686  // Check pixel values
687  TRY_NC(__FILE__, __LINE__,
688  nc_get_vara_uchar(geoGeolocationGrp, pixelQualityId, start, count, tmpByte));
689  // 0 Earth_intersection
690  // 1 Sensor_zenith
691  // 2 Invalid_input
692  //
693  // make all of the NAVFAIL for now
694  for (i = 0; i < num_pixels; i++) {
695  if (tmpByte[i])
696  l1rec->flags[i] |= NAVFAIL;
697  }
698  }
699  // Earth-sun distance correction for this scan
700  int32_t yr = (int32_t) scan_year;
701  int32_t dy = (int32_t) scan_day;
702  int32_t msec = (int32_t) (scan_sec * 1000.0);
703  double esdist = esdist_(&yr, &dy, &msec);
704  l1rec->fsol = pow(1.0 / esdist, 2);
705  l1rec->detnum = 0;
706 
707  return (LIFE_IS_GOOD);
708 }
709 
715 int closel1a_hawkeye(filehandle *file) {
716 
717  printf("Closing hawkeye L1A file\n");
718  TRY_NC(__FILE__, __LINE__, nc_close(file->sd_id));
719 
720  if (file->geofile && file->geofile[0]) {
721  printf("Closing Hawkeye GEO file\n");
722  TRY_NC(__FILE__, __LINE__, nc_close(geoFileId));
723 
724  // Free memory
725  // From readl1a_hawkeye
726  if (tmpShort) free(tmpShort);
727  if (tmpByte) free(tmpByte);
728  if (CCD_temperatures_this_scan) free(CCD_temperatures_this_scan);
729  if (dn) free(dn);
730  if (bkg_avg) free2d_double(bkg_avg);
731  if (Lt) free2d_double(Lt);
732  // From read_cal_hawkeye
733  if (temperature_ref) free(temperature_ref);
734  if (time_ref) free(time_ref);
735  if (K1) free(K1);
736  if (K2) free3d_double(K2);
737  if (K3) free2d_double(K3);
738  if (K4) free2d_float(K4);
739  if (K5) free2d_double(K5);
740  // From openl1a_hawkeye
741  if (CCD_temperatures) free2d_double(CCD_temperatures);
742  if (CCD_temperatures_cleansed) free2d_double(CCD_temperatures_cleansed);
743  if (tlm_delta_time_ms) free(tlm_delta_time_ms);
744  if (tlm_delta_time_ms_cleansed) free(tlm_delta_time_ms_cleansed);
745  if (dn_varid) free(dn_varid);
746 
747  }
748  free (file->private_data);
749  return (LIFE_IS_GOOD);
750 }
751 
764 
765  // Misc declarations
766  size_t tlm_block_num, tlm_block_cleansed_num, ccd_temp_num;
767  bool *tlm_block_good;
768  double *CCD_temperatures_Dim2, *nan_mean_weights;
769  double CCD_temperature_max_deviation = 10.0; // deviation from median value
770  double *CCD_temperature_sorted;
771  size_t *CCD_temperature_sort_index;
772  double CCD_temperature_median;
773 
774  // Allocate memory to arrays
775  tlm_block_good = (bool *) calloc(num_tlm_blocks, sizeof(bool));
776  CCD_temperatures_Dim2 = (double *) calloc(num_ccd_temps, sizeof(double));
777  nan_mean_weights = (double *) calloc(num_ccd_temps, sizeof(double));
778  CCD_temperature_sorted = (double *) calloc(num_ccd_temps, sizeof(double));
779  CCD_temperature_sort_index = (size_t *) calloc(num_ccd_temps, sizeof(size_t));
780 
781  // If a given row has all FILL values, skip that row.
782  for (tlm_block_num = 0; tlm_block_num < num_tlm_blocks; tlm_block_num++) { // Dim1
783  // Apply rules. If at least one of the CCD_temperatures in this tlm_block is good, do not skip this tlm_block.
784  tlm_block_good[tlm_block_num] = false; // Assume worst case
785  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) {
786  if (CCD_temperatures[tlm_block_num][ccd_temp_num] != fill_value_in_CCD_T) {
787  tlm_block_good[tlm_block_num] = true; // At least one good value, so don't skip this row
788  break;
789  }
790  }
791  }
792 
793  // If one or more CCD_temperatures for a given tlm block are FILL, replace them with the mean value of the
794  // non-FILL temperatures.
795  // First, set nan_mean_weights
796  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) {
797  nan_mean_weights[ccd_temp_num] = 1.0;
798  }
799  tlm_block_cleansed_num = 0;
800  for (tlm_block_num = 0; tlm_block_num < num_tlm_blocks; tlm_block_num++) { // Dim1
801  if (tlm_block_good[tlm_block_num]) {
802  // This is a good row, so populate CCD_temperatures_cleansed, tlm_delta_time_ms_cleansed and increment
803  // tlm_block_cleansed_num
804  tlm_delta_time_ms_cleansed[tlm_block_cleansed_num] =
805  tlm_delta_time_ms[tlm_block_num];
806  // If any CCD_temperatures are FILL, replace them with the mean value of CCD_temperatures_Dim2
807  // We have already removed any rows that have ALL values = FILL, so this should work.
808  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) { // Dim2
809  // First pass to build an array just for this row i.e. CCD_temperatures_Dim2
810  CCD_temperatures_Dim2[ccd_temp_num] =
811  CCD_temperatures[tlm_block_num][ccd_temp_num];
812  }
813  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) { // Dim2
814  // Second pass to replace FILL values with the mean for this row
815  if (CCD_temperatures_Dim2[ccd_temp_num] == fill_value_in_CCD_T) {
816  CCD_temperatures_cleansed[tlm_block_cleansed_num][ccd_temp_num] =
817  nan_wmean(nan_mean_weights, CCD_temperatures_Dim2,
818  num_ccd_temps, (double) fill_value_in_CCD_T);
819  } else {
820  CCD_temperatures_cleansed[tlm_block_cleansed_num][ccd_temp_num] =
821  CCD_temperatures_Dim2[ccd_temp_num];
822  }
823  }
824  tlm_block_cleansed_num = tlm_block_cleansed_num + 1;
825  } else {
826  // This is not a good row i.e. all FILL, so do nothing. This row is skipped.
827  }
828  }
829 
830  // At this point, FILL values have been replaced. Identify possibly bad (extreme) values for CCD temperatures e.g. if one
831  // of the 4 thermistors has a value much different from the others.
832  for (tlm_block_num = 0; tlm_block_num < num_tlm_blocks; tlm_block_num++) { // Dim1
833  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) { // Dim2
834  // First pass to build an array just for this row i.e. CCD_temperatures_Dim2
835  CCD_temperatures_Dim2[ccd_temp_num] =
836  CCD_temperatures[tlm_block_num][ccd_temp_num];
837  }
838  // Sort before calculating median value
839  gsl_sort_index(CCD_temperature_sort_index, CCD_temperatures_Dim2, 1,
840  num_ccd_temps);
841  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) {
842  CCD_temperature_sorted[ccd_temp_num] =
843  CCD_temperatures_Dim2[CCD_temperature_sort_index[ccd_temp_num]];
844  }
845  // Calculate the median value
846  CCD_temperature_median = gsl_stats_median_from_sorted_data(CCD_temperature_sorted,
847  1, num_ccd_temps);
848  // Loop through again to see if any values are too far from median value.
849  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) { // Dim2
850  if (fabs(CCD_temperatures_Dim2[ccd_temp_num] - CCD_temperature_median)
851  > CCD_temperature_max_deviation) {
852  fprintf(stderr,
853  "-W- In tlm block %d, CCD %d records a temperature of %f, more than %f degrees different than the median value of %f\n",
854  (int) tlm_block_num,
855  (int) ccd_temp_num, CCD_temperatures_Dim2[ccd_temp_num],
856  CCD_temperature_max_deviation, CCD_temperature_median);
857  }
858  }
859  }
860 
861  // Handle special case where there are no good tlm blocks by setting CCD temperatures to a default value
862  if (tlm_block_cleansed_num < 2) {
863  // There were NO good tlm blocks
864  for (tlm_block_num = 0; tlm_block_num < num_tlm_blocks; tlm_block_num++) { // Dim1
865  tlm_delta_time_ms_cleansed[tlm_block_num] = tlm_delta_time_ms[tlm_block_num];
866  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) {
867  CCD_temperatures_cleansed[tlm_block_num][ccd_temp_num] =
868  CCD_temperature_default;
869  }
870  }
871  num_tlm_blocks_cleansed = num_tlm_blocks;
872  fprintf(stderr,
873  "-W- There were fewer than 2 good tlm_blocks of CCD_temperatures, so using the default value of %f\n",
874  CCD_temperature_default);
875  } else {
876  // There WAS one or more good tlm blocks
877  num_tlm_blocks_cleansed = tlm_block_cleansed_num;
878  }
879 
880  // Clean up
881  free(tlm_block_good);
882  free(CCD_temperatures_Dim2);
883  free(nan_mean_weights);
884  free(CCD_temperature_sorted);
885  free(CCD_temperature_sort_index);
886 }
887 
895 void read_cal_hawkeye(char *cal_path) {
896 
897  // Declarations for reading cal file
898  int status, ncid_CAL, varid, dimid;
899  int calGrp, geoGrp;
900 
901  // Misc declarations
902  size_t cal_num_bands = 0;
903  size_t cal_num_pixels = 0;
904 
905  // Open LUT for calibrations
906  printf("Reading Hawkeye calibration LUT\n");
907  status = nc_open(cal_path, NC_NOWRITE, &ncid_CAL);
908  if (status != NC_NOERR) {
909  fprintf(stderr, "-E- Error opening CAL file \"%s\": %s\n",
910  cal_path, nc_strerror(status));
911  exit(EXIT_FAILURE);
912  }
913 
914  // Get dims from cal file
915  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_CAL, "number_of_bands", &dimid));
916  nc_inq_dimlen(ncid_CAL, dimid, &cal_num_bands);
917  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_CAL, "number_of_pixels", &dimid));
918  nc_inq_dimlen(ncid_CAL, dimid, &cal_num_pixels);
919  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_CAL, "number_of_times", &dimid));
920  nc_inq_dimlen(ncid_CAL, dimid, &cal_num_dates);
921  TRY_NC(__FILE__, __LINE__, nc_inq_dimid(ncid_CAL, "number_of_temperatures", &dimid));
922  nc_inq_dimlen(ncid_CAL, dimid, &cal_num_temperatures);
923 
924  // Check to see if cal file dims = L1A file dims
925  if (cal_num_bands != num_bands) {
926  fprintf(stderr, "-E- num_bands in cal file not equal to num_bands in L1A file\n");
927  exit(EXIT_FAILURE);
928  }
929  if (cal_num_pixels != num_pixels) {
930  fprintf(stderr,
931  "-E- num_pixels in cal file not equal to num_pixels in L1A file\n");
932  exit(EXIT_FAILURE);
933  }
934 
935  // Allocate space for arrays used for calibration coefficients
936  // 1D arrays
937  time_ref = (double *) calloc(cal_num_dates, sizeof(double));
938  temperature_ref = (double *) calloc(cal_num_temperatures, sizeof(double));
939  K1 = (double *) calloc(num_bands, sizeof(double));
940  // 2D arrays
941  K3 = allocate2d_double(num_bands, cal_num_temperatures);
942  K4 = allocate2d_float(num_bands, num_pixels);
943  K5 = allocate2d_double(num_bands, num_pixels);
944  // 3D arrays
945  K2 = allocate3d_double(num_bands, num_pixels, cal_num_dates);
946  nominal_dark_counts = (unsigned short**) allocate2d_short(num_bands, num_pixels);
947 
948  // Get variables from group radiometric_calibration
949  if ((nc_inq_grp_ncid(ncid_CAL, "radiometric_calibration", &calGrp)) != NC_NOERR) {
950  fprintf(stderr, "-E- Error finding group \"radiometric_calibration\".\n");
951  exit(EXIT_FAILURE);
952  }
953  // Get K1
954  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K1", &varid));
955  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, K1));
956  // Get K2
957  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K2", &varid));
958  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, &K2[0][0][0]));
959  // Get K2t
960  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K2t", &varid));
961  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, time_ref));
962  // Get K3
963  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K3", &varid));
964  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, &K3[0][0]));
965  // Get K3T
966  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K3T", &varid));
967  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, temperature_ref));
968  // Get K4
969  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K4", &varid));
970  TRY_NC(__FILE__, __LINE__, nc_get_var_float(calGrp, varid, &K4[0][0]));
971  // Get K5
972  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K5", &varid));
973  TRY_NC(__FILE__, __LINE__, nc_get_var_double(calGrp, varid, &K5[0][0]));
974 
975  // Get K7
976  if (l1_input->rad_opt) {
977  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "K7", &varid));
978  K7 = allocate2d_float(num_pixels, num_bands);
979  TRY_NC(__FILE__, __LINE__, nc_get_var_float(calGrp, varid, &K7[0][0]));
980  }
981  // Get nominal_dark_counts
982  TRY_NC(__FILE__, __LINE__, nc_inq_varid(calGrp, "nominal_dark_counts", &varid));
983  TRY_NC(__FILE__, __LINE__, nc_get_var_ushort(calGrp, varid, &nominal_dark_counts[0][0]));
984 
985  // Get variables from group geometric_parameters
986  if ((nc_inq_grp_ncid(ncid_CAL, "geometric_parameters", &geoGrp)) != NC_NOERR) {
987  fprintf(stderr, "-E- Error finding group \"geometric_parameters\".\n");
988  exit(EXIT_FAILURE);
989  }
990  // band co-registration parameters
991  track_offset = calloc(num_bands, sizeof(int));
992  ccd_offset = calloc(num_bands, sizeof(int));
993  focal_length = (float *) calloc(num_bands, sizeof(float));
994  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoGrp, "track_offset", &varid));
995  TRY_NC(__FILE__, __LINE__, nc_get_var_int(geoGrp, varid, track_offset));
996  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoGrp, "CCD_offset", &varid));
997  TRY_NC(__FILE__, __LINE__, nc_get_var_int(geoGrp, varid, ccd_offset));
998  TRY_NC(__FILE__, __LINE__, nc_inq_varid(geoGrp, "focal_length", &varid));
999  TRY_NC(__FILE__, __LINE__, nc_get_var_float(geoGrp, varid, focal_length));
1000  TRY_NC(__FILE__, __LINE__,
1001  nc_get_att_int(geoGrp, NC_GLOBAL, "reference_band", &reference_band));
1002  reference_band -= 1; // set to zero-based
1003  TRY_NC(__FILE__, __LINE__,
1004  nc_get_att_int(geoGrp, NC_GLOBAL, "reference_pixel", &reference_pixel));
1005  reference_pixel -= 1; // set to zero-based
1006  // background subtraction and cropping parameters
1007  TRY_NC(__FILE__, __LINE__,
1008  nc_get_att_int(geoGrp, NC_GLOBAL, "skip_beglines", &skip_beglines));
1009  TRY_NC(__FILE__, __LINE__,
1010  nc_get_att_int(geoGrp, NC_GLOBAL, "skip_endlines", &skip_endlines));
1011  TRY_NC(__FILE__, __LINE__,
1012  nc_get_att_int(geoGrp, NC_GLOBAL, "shutter_rampdown", &shutter_rampdown));
1013  TRY_NC(__FILE__, __LINE__,
1014  nc_get_att_int(geoGrp, NC_GLOBAL, "default_darkrows", &default_darkrows));
1015 
1016  // Close file
1017  TRY_NC(__FILE__, __LINE__, nc_close(ncid_CAL));
1018 
1019 }
1020 
1027 void interp_hawkeye_CCD_T(double delta_time_ms_i) {
1028 
1029  // Misc declarations
1030  size_t tlm_block_num, ccd_temp_num;
1031  double *CCD_temperatures_Dim1;
1032 
1033  // Allocate memory for arrays
1034  CCD_temperatures_Dim1 = (double *) calloc(num_tlm_blocks_cleansed, sizeof(double));
1035 
1036  gsl_interp_accel *acc_delta_time = gsl_interp_accel_alloc();
1037  gsl_spline *spline_delta_time = gsl_spline_alloc(gsl_interp_linear,
1038  num_tlm_blocks_cleansed);
1039 
1040  // First, before interpolating, make sure delta_time_ms_i is not outside the tlm_delta_time_ms_cleansed array
1041  prep_for_interp_double(&delta_time_ms_i, tlm_delta_time_ms_cleansed,
1042  num_tlm_blocks_cleansed);
1043  for (ccd_temp_num = 0; ccd_temp_num < num_ccd_temps; ccd_temp_num++) { // Dim2
1044  for (tlm_block_num = 0; tlm_block_num < num_tlm_blocks_cleansed;
1045  tlm_block_num++) { // Dim1
1046  // Temporarily store a column in CCD_temperatures_Dim1
1047  CCD_temperatures_Dim1[tlm_block_num] =
1048  CCD_temperatures_cleansed[tlm_block_num][ccd_temp_num];
1049  }
1050  // Interpolate this column in time to get CCD_temperatures_i[ccd_temp_num]
1051  gsl_spline_init(spline_delta_time, tlm_delta_time_ms_cleansed,
1052  &CCD_temperatures_Dim1[0], num_tlm_blocks_cleansed);
1053  CCD_temperatures_this_scan[ccd_temp_num] = gsl_spline_eval(spline_delta_time,
1054  delta_time_ms_i,
1055  acc_delta_time);
1056  }
1057 
1058  // Clean up
1059  free(CCD_temperatures_Dim1);
1060  gsl_spline_free(spline_delta_time);
1061  gsl_interp_accel_free(acc_delta_time);
1062 }
1063 
1069 void calibrate_hawkeye(double current_julian_date, int line) {
1070 
1071  double this_dn;
1072  double current_temp_C;
1073 
1074  size_t start[] = { 0, 0 };
1075  size_t count[] = { 1, 1 };
1076 
1077  int32_t band_num, pixel_num, ccd_temp_num;
1078  double cal_coeff;
1079 
1080  // Declarations for interpolation
1081  double **K2i; // Temporal correction AFTER interpolation, [num_bands][num_pixels]
1082  double *K3i; // Temperature correction AFTER interpolation, [num_bands]
1083  gsl_interp_accel *acc_T = gsl_interp_accel_alloc();
1084  gsl_interp_accel *acc_date = gsl_interp_accel_alloc();
1085  gsl_spline *spline_T = gsl_spline_alloc(gsl_interp_linear, cal_num_temperatures);
1086  //gsl_spline *spline_date = gsl_spline_alloc(gsl_interp_linear, cal_num_dates);
1087 
1088  // Allocate memory for arrays
1089  K2i = allocate2d_double(num_bands, num_pixels);
1090  K3i = (double *) calloc(num_bands, sizeof(double));
1091 
1092  // The original intent of K2 was to account for a temporal decay.
1093  // This is being overridden here to apply a simple time based
1094  // incremental (stepwise) adjustment to K4. Should a decay be necessary,
1095  // this will need to be rethought...
1096  int32_t K2t_idx;
1097  for (K2t_idx = 0; K2t_idx < cal_num_dates; K2t_idx++){
1098  // find the index where current time is greater than the index time
1099  if ((time_ref[K2t_idx] - current_julian_date) >= 0)
1100  break;
1101  }
1102  K2t_idx--;
1103 
1104  // Interpolate in time for K2, picking some time just for demo.
1105  prep_for_interp_double(&current_julian_date, time_ref, cal_num_dates);
1106 
1107  for (band_num = 0; band_num < num_bands; band_num++) {
1108  for (pixel_num = 0; pixel_num < num_pixels; pixel_num++) {
1109  K2i[band_num][pixel_num] = K2[band_num][pixel_num][K2t_idx];
1110  // See note above about change to K2 behavior This loop left in just in case
1111  // (this could have been accomplished without K2i, just index K2 by K2t_idx
1112  // in the calibration loop below...)
1113 
1114  // gsl_spline_init(spline_date, time_ref, &K2[band_num][pixel_num][0],
1115  // cal_num_dates);
1116  // K2i[band_num][pixel_num] = gsl_spline_eval(spline_date, current_julian_date,
1117  // acc_date);
1118  }
1119  }
1120 
1121  // Interpolate in temperature for K3
1122  for (band_num = 0; band_num < num_bands; band_num++) {
1123  // Given the band_num, pick the correct CCD_temperature
1124  ccd_temp_num = (int) (band_num / 2);
1125  current_temp_C = CCD_temperatures_this_scan[ccd_temp_num];
1126  // Make sure this temperature is in bounds
1127  prep_for_interp_double(&current_temp_C, temperature_ref, cal_num_temperatures);
1128  // Interpolate K3, given this temperature
1129  gsl_spline_init(spline_T, temperature_ref, &K3[band_num][0],
1130  cal_num_temperatures);
1131  K3i[band_num] = gsl_spline_eval(spline_T, current_temp_C, acc_T);
1132  }
1133 
1134  // Apply cal to sample scan
1135  count[0] = 1; // 1 line at a time
1136  count[1] = num_pixels;
1137  for (band_num = 0; band_num < num_bands; band_num++) {
1138  // select input line according to track offset
1139  // read only if valid
1140  int inputline = line + track_offset[band_num];
1141  if ((-1 < inputline) && (inputline < firstbad)) {
1142  start[0] = inputline;
1143  TRY_NC(__FILE__, __LINE__,
1144  nc_get_vara_ushort(l1aEarthViewGrp, dn_varid[band_num],
1145  start, count, &dn[0]));
1146 
1147  for (pixel_num = 0; pixel_num < num_pixels; pixel_num++) {
1148  // Dark subtraction
1149  this_dn = (double) dn[pixel_num] - bkg_avg[band_num][pixel_num];
1150  /* if (this_dn < (-1 * bkg_avg[band_num][pixel_num])) */
1151  /* printf("B%d\tP%d\tdn - bkg_avg = %f\n", band_num, pixel_num, this_dn); */
1152  if (this_dn < 0)
1153  this_dn = 0;
1154 
1155  cal_coeff = (K1[band_num]) * (K2i[band_num][pixel_num]) * (K3i[band_num])
1156  * (K4[band_num][pixel_num]);
1157 
1158  // apply smile correction
1159  if (l1_input->rad_opt)
1160  cal_coeff *= K7[band_num][pixel_num];
1161 
1162  Lt[band_num][pixel_num] = this_dn * cal_coeff
1163  * (1 + (K5[band_num][pixel_num]) * this_dn);
1164  }
1165  }
1166  }
1167 
1168  // Clean up e.g. free memory
1169  free2d_double(K2i);
1170  free(K3i);
1171  gsl_spline_free(spline_T);
1172  gsl_interp_accel_free(acc_T);
1173  //gsl_spline_free(spline_date);
1174  gsl_interp_accel_free(acc_date);
1175 
1176 }
1177 
1178 double nan_wmean(double *weights, double *data, size_t n, double fill_value) {
1179  size_t i, fill_count;
1180  double wmean;
1181  // Set weight to zero if corresponding data element is FILL
1182  fill_count = 0;
1183  for (i = 0; i < n; i++) {
1184  if (data[i] == fill_value) {
1185  weights[i] = 0;
1186  fill_count++;
1187  }
1188  }
1189  // Call gsl routine for weighted mean
1190  if (fill_count < n) {
1191  wmean = gsl_stats_wmean(weights, 1, data, 1, n);
1192  } else {
1193  wmean = fill_value;
1194  }
1195  return wmean;
1196 
1197 }
1198 
1205 void prep_for_interp_double(double *xi, double *x, int N) {
1206  // Adjust xi to be within range of x
1207  double xmin, xmax;
1208  gsl_stats_minmax(&xmin, &xmax, x, 1, N);
1209  if (*xi < xmin) {
1210  *xi = xmin;
1211  }
1212  if (*xi > xmax) {
1213  *xi = xmax;
1214  }
1215 }
void free2d_double(double **p)
Free a two-dimensional array created by allocate2d_double.
Definition: allocate2d.c:170
Utility functions for allocating and freeing three-dimensional arrays of various types.
void qc_hawkeye_CCD_T()
Definition: l1a_hawkeye.c:763
#define TRY_NC(file, line, ncstat)
Definition: nc4utils.h:31
void freeProductInfo(productInfo_t *info)
int status
Definition: l1_czcs_hdf.c:32
int openl1a_hawkeye(filehandle *file)
Definition: l1a_hawkeye.c:161
double mnorm[3]
int closel1a_hawkeye(filehandle *file)
Definition: l1a_hawkeye.c:715
int N
Definition: Usds.c:60
#define NULL
Definition: decode_rs.h:63
void ocorient_(float *pos, float *vel, float *att, float(*)[3], float *coef)
read l1rec
void calibrate_hawkeye(double current_julian_date, int line)
Definition: l1a_hawkeye.c:1069
float32 * pos
Definition: l1_czcs_hdf.c:35
int32 * msec
Definition: l1_czcs_hdf.c:31
double esdist_(int32_t *year, int32_t *day, int32_t *msec)
int32_t jday(int16_t i, int16_t j, int16_t k)
Definition: jday.c:4
void free2d_short(short **p)
Free a two-dimensional array created by allocate2d_short.
Definition: allocate2d.c:114
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 file
Definition: HISTORY.txt:413
character(len=1000) if
Definition: names.f90:13
#define K1
Definition: get_cal_swf.c:88
#define LIFE_IS_GOOD
Definition: passthebuck.h:4
void compute_alpha(float lon[], float lat[], float senz[], float sena[], double mnorm[3], int npix, float alpha[])
Definition: compute_alpha.c:8
void yd2md(int16_t year, int16_t doy, int16_t *month, int16_t *dom)
Definition: yd2md.c:6
int readl1a_hawkeye(filehandle *file, int32_t oline, l1str *l1rec)
Definition: l1a_hawkeye.c:499
void prep_for_interp_double(double *xi, double *x, int N)
Definition: l1a_hawkeye.c:1205
double nan_wmean(double *weights, double *data, size_t n, double fill_value)
Definition: l1a_hawkeye.c:1178
productInfo_t * allocateProductInfo()
void calc_bkg_hawkeye()
Definition: l1a_hawkeye.c:435
void free2d_float(float **p)
Free a two-dimensional array created by allocate2d_float.
Definition: allocate2d.c:142
#define HAWKEYE
Definition: sensorDefs.h:39
l1_input_t * l1_input
Definition: l1_options.c:9
int want_verbose
void unix2yds(double usec, short *year, short *day, double *secs)
integer, parameter double
constexpr float focal_length
void interp_hawkeye_CCD_T(double delta_time_ms_i)
Definition: l1a_hawkeye.c:1027
Utility functions for allocating and freeing two-dimensional arrays of various types.
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude data
Definition: HISTORY.txt:356
void read_cal_hawkeye(char *cal_path)
Definition: l1a_hawkeye.c:895
int findProductInfo(const char *productName, int sensorId, productInfo_t *info)
#define fabs(a)
Definition: misc.h:93
double ** allocate2d_double(size_t h, size_t w)
Allocate a two-dimensional array of type double of a given size.
Definition: allocate2d.c:153
#define K4
Definition: get_cal_swf.c:90
void free3d_double(double ***p)
Free a three-dimensional array created by allocate3d_double.
Definition: allocate3d.c:135
real *8 function esdist(iyr, iday, msec)
Definition: esdist.f:3
short ** allocate2d_short(size_t h, size_t w)
Allocate a two-dimensional array of type short of a given size.
Definition: allocate2d.c:97
int32_t rdsensorinfo(int32_t, int32_t, const char *, void **)
Definition: rdsensorinfo.c:69
float ** allocate2d_float(size_t h, size_t w)
Allocate a two-dimensional array of type float of a given size.
Definition: allocate2d.c:125
double *** allocate3d_double(size_t nz, size_t ny, size_t nx)
Allocate a three-dimensional array of type double of a given size.
Definition: allocate3d.c:109
int i
Definition: decode_rs.h:71
#define K3
Definition: get_cal_swf.c:89
How many dimensions is the output array Default is Not sure if anything above will work correctly strcpy(l2prod->title, "no title yet")
double isodate2unix(const char *isodate)
Definition: unix2isodate.c:61
int count
Definition: decode_rs.h:79