NASA Logo
Ocean Color Science Software

ocssw V2022
polcor_oci.cpp
Go to the documentation of this file.
1 // Polarization Correction for OCI
2 // File is the same as polcor_hawkeye.cpp, but with some modifications
3 
4 #include <vector>
5 #include <map>
6 #include <netcdf>
7 #include <math.h>
8 #include <allocate2d.h>
9 #include "l12_proto.h"
10 #include "polcor_oci.h"
11 
12 using namespace std;
13 using namespace netCDF;
14 using namespace netCDF::exceptions;
15 
16 // global variables that will be used repeately per scan
17 static bool firstRun = true; // first run to grab m12, m13 coef and ham sides
18 static bool skipPolarization = false; // when ccd pix != swir pix, dont do polarization
19 
20 static int currScanLine = -1;
21 static int numPolCoef = -1;
22 
23 static int numScans = -1;
24 static int numCcdPixels = -1;
25 static int numSwirPixels = -1;
26 static int numBands = -1;
27 static int numBlueBands = -1;
28 static int numRedBands = -1;
29 static int numSwirBands = -1;
30 
31 // grab m12 and m13 values on first run. The key will be the ham side.
32 static map<int, vector<vector<float>>> m12;
33 static map<int, vector<vector<float>>> m13;
34 
35 // grab the SWIR quality flag for the current line. The keys
36 // will be the band number and gets upated every new line
37 static map<int, vector<unsigned char>> qualSwir;
38 
39 // updates every new scan line
40 static vector<unsigned char> hamSide;
41 static vector<float> ccdScanAngle;
42 
55 void getPolarizationCoefs(NcVar &ncVar, vector<vector<float>> &aggVec,
56  int start, int stop, int numPolCoef, int currHamSide) {
57 
58  for (int band = start; band < stop; band++) {
59 
60  vector<float> coefs(numPolCoef, 0.0);
61 
62  // start at the current band, current ham side for this line and first element of coefs
63  vector<size_t> start = {
64  (size_t)band, // start index of band
65  (size_t)currHamSide, // start index of ham side
66  0 // start index of pol coef.
67  };
68 
69  // want 1 line for current band, 1 line of ham side and all of the coefs (max 3)
70  vector<size_t> count = {
71  (size_t) 1, // read 1 line of band
72  (size_t) 1, // read 1 line of ham side
73  (size_t)numPolCoef // read 3 items of coefs.
74  };
75 
76  // read it into coefs
77  ncVar.getVar(start, count, coefs.data());
78 
79  // add band to m12 and m13
80  aggVec.push_back(coefs);
81 
82  }
83 }
84 
85 
92 void initializePolCorOci(NcFile &ncFile) {
93 
94  try {
95  // read in the dimensions used for m12 and m13. Also read in dims
96  // that will be used to fetch other variables later on
97  numBlueBands = ncFile.getDim("blue_bands").getSize();
98  numRedBands = ncFile.getDim("red_bands").getSize();
99  numSwirBands = ncFile.getDim("SWIR_bands").getSize();
100  numPolCoef = ncFile.getDim("polarization_coefficients").getSize();
101  numCcdPixels = ncFile.getDim("ccd_pixels").getSize();
102  numScans = ncFile.getDim("number_of_scans").getSize();
103  numSwirPixels = ncFile.getDim("SWIR_pixels").getSize();
104 
105  // OCI has 3 coefficients. Bad file if it doesn't.
106  if (numPolCoef != 3 || numCcdPixels != numSwirPixels) {
107  printf("-E- %s:%d - Problem with the number of polarization coefficients. Should be 3, but got %d\n", __FILE__, __LINE__, numPolCoef);
108  exit(EXIT_FAILURE);
109  }
110  // If ccd pixels != swir pixels, polarization will not happen
111  // -- Will fix and add if we need it to work when ccd pix != swir pix ``
112  if (numCcdPixels != numSwirPixels) {
113  printf("-WARNING- %s:%d - Number of CCD pixels and SWIR pixels don't match. CCD: %d vs. SWIR: %d. Polarization will be 1 for all pixels.\n", __FILE__, __LINE__, numCcdPixels, numSwirPixels);
114  skipPolarization = true;
115  return;
116  }
117 
118  /*----READING HAM SIDES----*/
119 
120  numScans = ncFile.getDim("number_of_scans").getSize();
121  hamSide = vector<unsigned char>(numScans, 255);
122 
123  // read in HAM side
124  NcVar hamVar = ncFile.getGroup("scan_line_attributes").getVar("HAM_side");
125  hamVar.getVar(hamSide.data());
126 
127 
128  /*----READING BLUE, RED, SWIR M12 AND M13----*/
129  NcGroup sensorBandGroup = ncFile.getGroup("sensor_band_parameters");
130 
131  NcVar blueM12Var = sensorBandGroup.getVar("blue_m12_coef");
132  NcVar blueM13Var = sensorBandGroup.getVar("blue_m13_coef");
133 
134  NcVar redM12Var = sensorBandGroup.getVar("red_m12_coef");
135  NcVar redM13Var = sensorBandGroup.getVar("red_m13_coef");
136 
137  NcVar swirM12Var = sensorBandGroup.getVar("SWIR_m12_coef");
138  NcVar swirM13Var = sensorBandGroup.getVar("SWIR_m13_coef");
139 
140  // grab m12 and m13 values for both ham sides 0 and 1
141  for (int ham = 0; ham < 2; ham++) {
142 
143  // -- BLUE -- skip last 3 because it overlaps with red
144  getPolarizationCoefs(blueM12Var, m12[ham], 0, numBlueBands-3, numPolCoef, ham);
145  getPolarizationCoefs(blueM13Var, m13[ham], 0, numBlueBands-3, numPolCoef, ham);
146 
147  // -- RED -- reads in all
148  getPolarizationCoefs(redM12Var, m12[ham], 0, numRedBands, numPolCoef, ham);
149  getPolarizationCoefs(redM13Var, m13[ham], 0, numRedBands, numPolCoef, ham);
150 
151  // -- SWIR -- read all, but 2 will be dropped during calculation
152  getPolarizationCoefs(swirM12Var, m12[ham], 0, numSwirBands, numPolCoef, ham);
153  getPolarizationCoefs(swirM12Var, m13[ham], 0, numSwirBands, numPolCoef, ham);
154 
155  }
156 
157  } catch (NcException& e) {
158  printf("-E- %s:%d - Problem reading polfile %s\n", __FILE__, __LINE__, input->polfile);
159  printf(" %s\n", e.what());
160  exit(EXIT_FAILURE);
161  }
162 }
163 
168 
169  // clear the previous scan line's CCD scan angle for each pixel
170  ccdScanAngle.clear();
171 
172  // initalize it to be the size of the number of pixels
173  ccdScanAngle = vector<float>(numCcdPixels, -32767.0);
174 
175  // read in the angles
176  NcVar ccdScanAngleVar = ncFile.getGroup("navigation_data").getVar("CCD_scan_angles");
177 
178  // start at scan line and the first pixel
179  vector<size_t> start = {(size_t)currScanLine, 0};
180  // read 1 line and all CCD pixels
181  vector<size_t>count = {1, (size_t)numCcdPixels};
182  // save it to vector
183  ccdScanAngleVar.getVar(start, count, ccdScanAngle.data());
184 
185 
186 }
187 
196 void getQualSwirForScan(NcFile &ncFile) {
197  // clear previous scans data
198  qualSwir.clear();
199 
200  NcVar qualSwirVar = ncFile.getGroup("observation_data").getVar("qual_SWIR");
201 
202  for (int band = 2; band < 7; band++) {
203 
204  // not the band we want, skip
205  if (band != 2 && band != 3 && band != 5 && band != 6) {
206  continue;
207  }
208 
209  // everything processed here should be bands: 2, 3, 5 and 6
210  // save the band's qual flag here
211  vector<unsigned char> qualSwirForBand(numSwirPixels, 255);
212 
213  vector<size_t> start = {
214  (size_t) band, // start at curr band
215  (size_t) currScanLine, // start at curr scan line
216  0 // start from beginning of pixels
217  };
218 
219  // how many from the start to read in
220  vector<size_t> count = {
221  (size_t) 1, // entire band
222  (size_t) 1, // entire scan line
223  (size_t) numSwirPixels // all pixels
224  };
225 
226  // read it into the vector
227  qualSwirVar.getVar(start, count, qualSwirForBand.data());
228 
229  // save it to the map
230  qualSwir[band] = qualSwirForBand;
231 
232  }
233 
234 }
235 
242 bool areHighGainBandsSaturated(int currPix) {
243 
244  // cast to int because reading in from NetCDF file is unsigned byte
245  int band3Sat = (int) qualSwir[3][currPix];
246  int band6Sat = (int) qualSwir[6][currPix];
247 
248  // true if either are saturated.
249  return band3Sat || band6Sat;
250 }
251 
267 void calculateAndSetPolCor(l1str* &l1rec, int currPix, int index, vector<float> m12Coefs, vector<float> m13Coefs) {
268 
269  double alpha = l1rec->alpha[currPix] / RADEG;
270  double L_x = l1rec->Lt[index] / l1rec->tg_sol[index] / l1rec->tg_sen[index];
271  double L_qp = l1rec->L_q[index] * cos(2 * alpha) + l1rec->L_u[index] * sin(2 * alpha);
272  double L_up = l1rec->L_u[index] * cos(2 * alpha) - l1rec->L_q[index] * sin(2 * alpha);
273 
274  /*
275  If L_qp and L_up are NaN, skip. polcor == 1.0.
276  --
277  l1rec->L_u and L_q are NaN when airmass is negative because log(negative) is undefined.
278  l1rec->L_u and L_q are calculated in rayleigh.c and it uses the function ray_press_wang to
279  calculate a fraction. If airmass is negative, that fraction is NaN since it uses log of airmass.
280 
281  This instance happens when solar_zenith (solz) is >= 90, resulting in a negative cos after
282  it is converted into radian. This value carries over when calculating airmass.
283  */
284  if (isnan(L_qp) || isnan(L_up)) return;
285 
286  // get the scan angle for current pixel and the coef
287  float scanAngle = ccdScanAngle[currPix];
288 
289  // calculate m12
290  float A = m12Coefs[2];
291  float B = m12Coefs[1];
292  float C = m12Coefs[0];
293  double m12 = (A * pow(scanAngle, 2.0)) + (B * scanAngle) + C;
294 
295  // calculate m13
296  A = m13Coefs[2];
297  B = m13Coefs[1];
298  C = m13Coefs[0];
299  double m13 = (A * pow(scanAngle, 2.0)) + (B * scanAngle) + C;
300 
301  // calculate polcor
302  l1rec->polcor[index] = 1.0 / (1.0 - m12 * L_qp / L_x - m13 * L_up / L_x);
303  l1rec->dpol[index] = sqrt(pow(l1rec->L_q[index], 2.0) + pow(l1rec->L_u[index], 2.0)) / L_x;
304 }
305 
314 extern "C" void polcor_oci(l1str *l1rec, int32_t currPix) {
315 
316  // happens when ccd pixel != swir pixels. leave polcof as 1.000
317  if (skipPolarization) {
318  return;
319  }
320 
321  // open NC file
322  NcFile ncFile(input->ifile[0], NcFile::read);
323 
324  // num bands from the l1file is 286 after dropping 3 bands from blue
325  // and 2 from SWIR
326  numBands = l1rec->l1file->nbands;
327 
328  // grab all the m12 and m13 values for blue, red and SWIR
329  // also get ham side so we know which m12 or m13 side to use
330  if (firstRun) {
331  firstRun = false;
333  }
334 
335  // new scan line, need to grab new CCD_Scan_Angles
336  if (currScanLine < l1rec->iscan) {
337  currScanLine = l1rec->iscan;
340  }
341 
342 
343  // ---- APPLYING POLARIZATION CORRECTION ----
344 
345 
346  // track the number of bands that were used in calculating polcor because
347  // m12 and m13 are merged
348  int bandCount = 0;
349  int currHamSide = hamSide[currScanLine];
350 
351 
352  // Process Blue Bands first, skipping last 3 bands
353  // NOTE: ORDER MATTERS -- when merging the band coefs, the order is blue, red, swir
354  // Skip last 3 bands because it overlaps with red. Use red over blue. m12 and m13
355  // already skips the blue bands, but not the SWIR ones.
356  for (int currBand = 0; currBand < numBlueBands-3; currBand++) {
357 
358  // relative position for this pixel in the polcor array.
359  // polcor = numBands * numpixels for the scan line.
360  // to get the index of currPix relative to it, it will be
361  // currpix * numBands + curr band position.
362  // not using currBand but bandCount bc some bands are skipped and because
363  // this calculation is done for each band color
364  int relativeIndex = currPix * numBands + bandCount;
365  vector<float> m12Coef = m12[currHamSide][bandCount];
366  vector<float> m13Coef = m13[currHamSide][bandCount];
367  calculateAndSetPolCor(l1rec, currPix, relativeIndex, m12Coef, m13Coef);
368  bandCount++;
369  }
370 
371 
372  // Process Red Bands
373  for (int currBand = 0; currBand < numRedBands; currBand++) {
374 
375  int relativeIndex = currPix * numBands + bandCount;
376  // m12 and m13 are merged, so use the current band count to get the red band
377  vector<float> m12Coef = m12[currHamSide][bandCount];
378  vector<float> m13Coef = m13[currHamSide][bandCount];
379  calculateAndSetPolCor(l1rec, currPix, relativeIndex, m12Coef, m13Coef);
380  bandCount++;
381  }
382 
383 
384  // check if the current pixels SWIR high gain bands are satured
385  // if they are, we skip bands 3 and 6
386  bool skipHighGain = areHighGainBandsSaturated(currPix);
387 
388  //this var will track the band for polcor, which will NOT increment when dropping
389  // the high or low gain bands. bandCount will increment because it accounts for
390  // all SWIR bands and it needs to update to grab the correct m12 and m13 coefs
391  int polcorBandCount = bandCount;
392 
393  // Process SWIR Bands
394  for (int currBand = 0; currBand < numSwirBands; currBand++) {
395 
396  // if skipping high gain, skip bands 3 and 6
397  if (skipHighGain && (currBand == 3 || currBand == 6)) {
398  // no need to set polcor[pix] because polcor takes into account that
399  // 2 of the SWIRs will be dropped for the current file
400  bandCount++; // need to update to grab correct m12 and m13 coef
401  continue;
402  }
403 
404  // if not skipping high gain, throw out the low gain ones
405  if (!skipHighGain && (currBand == 2 || currBand == 4)) {
406  bandCount++; // need to update to grab correct m12 and m13 coef
407  continue;
408  }
409 
410  // band count because it is used to save into polcor arr
411  int relativeIndex = currPix * numBands + polcorBandCount;
412  vector<float> m12Coef = m12[currHamSide][bandCount];
413  vector<float> m13Coef = m13[currHamSide][bandCount];
414  calculateAndSetPolCor(l1rec, currPix, relativeIndex, m12Coef, m13Coef);
415 
416  // increment bands
417  polcorBandCount++;
418  bandCount++;
419 
420  }
421 
422 }
an array had not been initialized Several spelling and grammar corrections were which is read from the appropriate MCF the above metadata values were hard coded A problem calculating the average background DN for SWIR bands when the moon is in the space view port was corrected The new algorithm used to calculate the average background DN for all reflective bands when the moon is in the space view port is now the same as the algorithm employed by the thermal bands For non SWIR changes in the averages are typically less than Also for non SWIR the black body DNs remain a backup in case the SV DNs are not available For SWIR the changes in computed averages were larger because the old which used the black body suffered from contamination by the micron leak As a consequence of the if SV DNs are not available for the SWIR the EV pixels will not be the granule time is used to identify the appropriate tables within the set given for one LUT the first two or last two tables respectively will be used for the interpolation If there is only one LUT in the set of it will be treated as a constant LUT The manner in which Earth View data is checked for saturation was changed Previously the raw Earth View DNs and Space View DNs were checked against the lookup table values contained in the table dn_sat The change made is to check the raw Earth and Space View DNs to be sure they are less than the maximum saturation value and to check the Space View subtracted Earth View dns against a set of values contained in the new lookup table dn_sat_ev The metadata configuration and ASSOCIATEDINSTRUMENTSHORTNAME from the MOD02HKM product The same metatdata with extensions and were removed from the MOD021KM and MOD02OBC products ASSOCIATEDSENSORSHORTNAME was set to MODIS in all products These changes are reflected in new File Specification which users may consult for exact the pow functions were eliminated in Emissive_Cal and Emissive bands replaced by more efficient code Other calculations throughout the code were also made more efficient Aside from a few round off there was no difference to the product The CPU time decreased by about for a day case and for a night case A minor bug in calculating the uncertainty index for emissive bands was corrected The frame index(0-based) was previously being used the frame number(1-based) should have been used. There were only a few minor changes to the uncertainty index(maximum of 1 digit). 3. Some inefficient arrays(Sigma_RVS_norm_sq) were eliminated and some code lines in Preprocess_L1A_Data were moved into Process_OBCEng_Emiss. There were no changes to the product. Required RAM was reduced by 20 MB. Now
float * vector(long nl, long nh)
Definition: nrutil.c:15
void initializePolCorOci(NcFile &ncFile)
Definition: polcor_oci.cpp:92
const double C
Definition: calc_par.c:102
void getPolarizationCoefs(NcVar &ncVar, vector< vector< float >> &aggVec, int start, int stop, int numPolCoef, int currHamSide)
Definition: polcor_oci.cpp:55
read l1rec
PARAM_TYPE_NONE Default value No parameter is buried in the product name name_prefix is case insensitive string compared to the product name PARAM_TYPE_VIS_WAVE The visible wavelength bands from the sensor are buried in the product name The product name is compared by appending and name_suffix ie aph_412_giop where prod_ix will be set to PARAM_TYPE_IR_WAVE same search method as PARAM_TYPE_VIS_WAVE except only wavelength above are looped through but prod_ix is still based ie aph_2_giop for the second band
void getQualSwirForScan(NcFile &ncFile)
Definition: polcor_oci.cpp:196
real, dimension(fncs, fnm, fnr) coefs
instr * input
const float A
Definition: calc_par.c:100
void polcor_oci(l1str *l1rec, int32_t currPix)
Definition: polcor_oci.cpp:314
void calculateAndSetPolCor(l1str *&l1rec, int currPix, int index, vector< float > m12Coefs, vector< float > m13Coefs)
Definition: polcor_oci.cpp:267
NcFile * ncFile
#define RADEG
Definition: czcs_ctl_pt.c:5
Utility functions for allocating and freeing two-dimensional arrays of various types.
const float B
Definition: calc_par.c:101
int32_t iscan
void getScanAngleForScan(NcFile &ncFile)
Definition: polcor_oci.cpp:167
bool areHighGainBandsSaturated(int currPix)
Definition: polcor_oci.cpp:242
int count
Definition: decode_rs.h:79