OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
geolocate_hawkeye.cpp
Go to the documentation of this file.
1 #include <fstream>
2 #include <iostream>
3 #include <sstream>
4 
5 #include <gsl/gsl_blas.h>
6 #include <gsl/gsl_linalg.h>
7 #include <gsl/gsl_matrix_double.h>
8 
9 #include "geolocate_hawkeye.h"
10 #include "hawkeyeUtil.h"
11 #include "netcdf.h"
12 
13 #define VERSION "0.772"
14 
15 // Modification history:
16 // Programmer Organization Date Ver Description of change
17 // ---------- ------------ ---- --- ---------------------
18 // Joel Gales FutureTech 05/21/18 0.10 Original development
19 // Joel Gales FutureTech 10/18/18 0.20 Change offset in alp
20 // Joel Gales FutureTech 10/23/18 0.30 Add support for rpy
21 // Joel Gales SAIC 11/02/18 0.40 Initialize output arrays
22 // to fill values. Skip rpy
23 // processing if stime = -999.
24 //
25 // Joel Gales SAIC 11/06/18 0.50 Set bit 2 (=4) of qfl when
26 // stime = -999
27 // Gwyn Fireman SAIC 02/21/20 0.55 Allow different att, orb dims
28 // Joel Gales SAIC 03/21/20 0.56 Pass sdim to createFile() to
29 // set number_of_scans
30 // Liang Hong SAIC 11/02/20 0.561 quatr memory allocation correction
31 // Liang Hong SAIC 02/24/21 0.661 added time and roll offset
32 // Liang Hong SAIC 07/29/21 0.761 corrected center-pixel offset and
33 // roll offset, added static pitch offset
34 // Liang Hong SAIC 01/12/22 0.771 added scale and offset attributes
35 // Liang Hong SAIC 04/06/22 0.772 default output file name if not specified
36 
37 using namespace std;
38 
39 int main(int argc, char *argv[]) {
40  string l1a_name;
41  string geo_name;
42 
43  cout << "geolocate_hawkeye " << VERSION << " ("
44  << __DATE__ << " " << __TIME__ << ")" << endl;
45 
46  if (argc == 1) {
47  cout << endl
48  << "geolocate_hawkeye input_l1a_filename output_geo_filename" << endl;
49 
50  return 0;
51  } else if (argc == 2) {
52  l1a_name.assign(argv[1]);
53  string str_L1A("L1A");
54  size_t L1A_found = l1a_name.find(str_L1A);
55  if ((L1A_found == string::npos)) {
56  geo_name.assign(l1a_name+".GEO");
57  } else {
58  geo_name = l1a_name;
59  geo_name.replace(L1A_found,str_L1A.length(),"GEO");
60  }
61  } else {
62  l1a_name.assign(argv[1]);
63  geo_name.assign(argv[2]);
64  }
65 
66  int status;
67  int grpid, varid, dimid;
68  int dimids[NC_MAX_VAR_DIMS];
69 
70 
71  int l1a_ncid;
72  int l1a_ngrps;
73  int l1a_gid[10];
74  size_t n_att_rec, n_orb_rec, sdim, pdim, t_len;
75  nc_type t_type;
76  float rolloff, timeoff;
77 
78  enum l1a_grps { scan_attr,
79  telem,
80  navigation,
81  earth_view };
82  enum geo_grps { scan_attr_geo,
83  nav_geo,
84  geolocation };
85 
86  status = nc_open(l1a_name.c_str(), NC_NOWRITE, &l1a_ncid);
87  check_err(status, __LINE__, __FILE__);
88 
89  status = nc_inq_grps(l1a_ncid, &l1a_ngrps, l1a_gid);
90  check_err(status, __LINE__, __FILE__);
91 
92  // number of pixels
93  status = nc_inq_dimid(l1a_ncid, "number_of_pixels", &dimid);
94  check_err(status, __LINE__, __FILE__);
95  nc_inq_dimlen(l1a_ncid, dimid, &pdim);
96 
97  grpid = l1a_gid[navigation]; // navigation_data group
98 
99  // att time
100  status = nc_inq_varid(grpid, "att_time", &varid);
101  check_err(status, __LINE__, __FILE__);
102  nc_inq_vardimid(grpid, varid, dimids);
103  nc_inq_dimlen(l1a_ncid, dimids[0], &n_att_rec);
104 
105  double *att_time = new double[n_att_rec]();
106  status = nc_get_var_double(grpid, varid, att_time);
107  check_err(status, __LINE__, __FILE__);
108 
109  // att quat
110  status = nc_inq_varid(grpid, "att_quat", &varid);
111  check_err(status, __LINE__, __FILE__);
112 
113  quat_array *att_quat = new quat_array[n_att_rec]();
114  status = nc_get_var_float(grpid, varid, &att_quat[0][0]);
115  check_err(status, __LINE__, __FILE__);
116 
117  // orb time
118  status = nc_inq_varid(grpid, "orb_time", &varid);
119  check_err(status, __LINE__, __FILE__);
120  nc_inq_vardimid(grpid, varid, dimids);
121  nc_inq_dimlen(l1a_ncid, dimids[0], &n_orb_rec);
122 
123  double *orb_time = new double[n_orb_rec]();
124  status = nc_get_var_double(grpid, varid, orb_time);
125  check_err(status, __LINE__, __FILE__);
126 
127  // orb pos
128  status = nc_inq_varid(grpid, "orb_pos", &varid);
129  check_err(status, __LINE__, __FILE__);
130 
131  orb_array *orb_pos = new orb_array[n_orb_rec]();
132  status = nc_get_var_float(grpid, varid, &orb_pos[0][0]);
133  check_err(status, __LINE__, __FILE__);
134 
135  // orb vel
136  status = nc_inq_varid(grpid, "orb_vel", &varid);
137  check_err(status, __LINE__, __FILE__);
138 
139  orb_array *orb_vel = new orb_array[n_orb_rec]();
140  status = nc_get_var_float(grpid, varid, &orb_vel[0][0]);
141  check_err(status, __LINE__, __FILE__);
142 
143  // Check for time and roll offset attributes
144  status = nc_inq_att(grpid, NC_GLOBAL, "roll_offset", &t_type, &t_len);
145  if (status != NC_NOERR) {
146  rolloff = 0.0;
147  } else {
148  status = nc_get_att_float(grpid, NC_GLOBAL, "roll_offset", &rolloff);
149  }
150  cout << "roll_offset=" <<rolloff << endl;
151 
152  status = nc_inq_att(grpid, NC_GLOBAL, "time_offset", &t_type, &t_len);
153  if (status != NC_NOERR) {
154  timeoff = 0.0;
155  } else {
156  status = nc_get_att_float(grpid, NC_GLOBAL, "time_offset", &timeoff);
157  }
158  cout << "time_offset=" <<timeoff << endl;
159 
160  // scan_time
161  grpid = l1a_gid[scan_attr]; // scan_line_attributes group
162  status = nc_inq_varid(grpid, "scan_time", &varid);
163  check_err(status, __LINE__, __FILE__);
164  nc_inq_vardimid(grpid, varid, dimids);
165  nc_inq_dimlen(l1a_ncid, dimids[0], &sdim);
166 
167  double *stime = new double[sdim]();
168  status = nc_get_var_double(grpid, varid, stime);
169  check_err(status, __LINE__, __FILE__);
170 
171  // Get L1A start date
172  char tstart[25] = "";
173  char tend[25] = "";
174  status = nc_get_att_text(l1a_ncid, NC_GLOBAL, "time_coverage_start", tstart);
175  check_err(status, __LINE__, __FILE__);
176 
177  status = nc_get_att_text(l1a_ncid, NC_GLOBAL, "time_coverage_end", tend);
178  check_err(status, __LINE__, __FILE__);
179 
180  int32_t iyr, iday, msec;
181  isodate2ydmsec(tstart, &iyr, &iday, &msec);
182 
183  // Transform orbit and attitude from J2000 to ECR
184  quat_array *quatr = new quat_array[n_att_rec]();
185  orb_array *posr = new orb_array[n_orb_rec]();
186  orb_array *velr = new orb_array[n_orb_rec]();
187 
188  double omegae = 7.29211585494e-5;
189  double ecmat[3][3];
190 
191  // Orbit
192  for (size_t i = 0; i < n_orb_rec; i++) {
193  j2000_to_ecr(iyr, iday, orb_time[i], ecmat);
194 
195  for (size_t j = 0; j < 3; j++) {
196  posr[i][j] = ecmat[j][0] * orb_pos[i][0] +
197  ecmat[j][1] * orb_pos[i][1] +
198  ecmat[j][2] * orb_pos[i][2];
199  velr[i][j] = ecmat[j][0] * orb_vel[i][0] +
200  ecmat[j][1] * orb_vel[i][1] +
201  ecmat[j][2] * orb_vel[i][2];
202  }
203  velr[i][0] += posr[i][1] * omegae;
204  velr[i][1] -= posr[i][0] * omegae;
205  } // i loop
206 
207  // Attitude
208  for (size_t i = 0; i < n_att_rec; i++) {
209  double ecq[4], qt2[4];
210  float qt1[4];
211  j2000_to_ecr(iyr, iday, att_time[i], ecmat);
212  mtoq(ecmat, ecq);
213 
214  memcpy(qt1, &att_quat[i][1], 3 * sizeof(float));
215  qt1[3] = att_quat[i][0];
216 
217  qprod(ecq, qt1, qt2);
218  for (size_t j = 0; j < 4; j++) quatr[i][j] = qt2[j];
219  } // i loop
220 
221  // Interpolate orbit and attitude to scan times
222  quat_array *quati = new quat_array[sdim]();
223  q_interp(n_att_rec, sdim, att_time, quatr, stime, quati);
224 
225  // apply offsets to orbit times
226  for (size_t i = 0; i < sdim; i++) {
227  stime[i] = stime[i] + timeoff;
228  }
229 
230  orb_array *posi = new orb_array[sdim]();
231  orb_array *veli = new orb_array[sdim]();
232  orb_array *rpy = new orb_array[sdim]();
233  orb_array *ang = new orb_array[sdim]();
234  orb_interp(n_orb_rec, sdim, orb_time, posr, velr, stime, posi, veli);
235 
236 
237 
238  float *xlon = new float[sdim * pdim]();
239  float *xlat = new float[sdim * pdim]();
240  short *solz = new short[sdim * pdim]();
241  short *sola = new short[sdim * pdim]();
242  short *senz = new short[sdim * pdim]();
243  short *sena = new short[sdim * pdim]();
244  short *range = new short[sdim * pdim]();
245  uint8_t *qfl = new uint8_t[sdim * pdim]();
246 
247  // Initialize output arrays
248  for (size_t i = 0; i < sdim * pdim; i++) {
249  xlon[i] = -999.9;
250  xlat[i] = -999.9;
251 
252  solz[i] = -32768;
253  sola[i] = -32768;
254  senz[i] = -32768;
255  sena[i] = -32768;
256  range[i] = -32768;
257  }
258 
259  // Generate pointing vector array
260  orb_array *pview = new orb_array[pdim]();
261  for (size_t i = 0; i < pdim; i++) {
262  double alp = atan(((double)pdim / 2 - i + 40) * 0.01 / focal_length); // Ver 0.761
263  pview[i][0] = 0.0;
264  pview[i][1] = sin(alp);
265  pview[i][2] = cos(alp);
266  }
267 
268  // Get Sun vectors
269  orb_array *sunr = new orb_array[sdim]();
270  l_sun(sdim, iyr, iday, stime, sunr);
271 
272  // Get S/C-to-sensor matrix (placeholder for now)
273  double sc_to_hwk[3][3];
274  for (size_t i = 0; i < 3; i++)
275  for (size_t j = 0; j < 3; j++) sc_to_hwk[i][j] = 0.0;
276  // double sr = sin(rolloff/RADEG);
277  // double cr = cos(rolloff/RADEG);
278  sc_to_hwk[0][2] = 1.0;
279  // sc_to_hwk[1][0] = -sr;
280  sc_to_hwk[1][1] = 1.0; //cr;
281  sc_to_hwk[2][0] = -1.0; //-cr;
282  // sc_to_hwk[2][1] = -sr;
283  float pitchoff = -0.8;
284  float rpyoff[3] = {rolloff,pitchoff,0.0}; // Ver 0.761
285 
286  // Geolocate each scan line
287  gsl_matrix *smat = gsl_matrix_alloc(3, 3);
288 
289  for (size_t i = 0; i < sdim; i++) {
290  if (stime[i] > -900.0) { // replaced if (stime[i] != -999.0) due to stime + timeoff shift
291  // Convert quaternion to matrix
292  double om[3][3];
293  double rpym[3][3];
294  double qmat[3][3];
295  qtom(quati[i], qmat);
296 
297  gsl_matrix_view A = gsl_matrix_view_array(&sc_to_hwk[0][0], 3, 3);
298  gsl_matrix_view B = gsl_matrix_view_array(&qmat[0][0], 3, 3);
299 
300  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
301  &A.matrix, &B.matrix, 0.0, smat);
302 
303  // Compute and adjust attitude angles
304  double *ptr_C = gsl_matrix_ptr(smat, 0, 0);
305  mat2rpy(posi[i], veli[i], (double(*)[3])ptr_C, rpy[i], om);
306 
307  for (size_t j = 0; j < 3; j++) ang[i][j] = rpy[i][j] + rpyoff[j];
308 
309  // Get sensor orientation matrix and scan ellipse coefficients
310  euler(ang[i],rpym);
311 
312  //smat = rpym#om
313  gsl_matrix_view C = gsl_matrix_view_array(&rpym[0][0], 3, 3);
314  gsl_matrix_view D = gsl_matrix_view_array(&om[0][0], 3, 3);
315 
316  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
317  &C.matrix, &D.matrix, 0.0, smat);
318 
319  double coef[10];
320  ptr_C = gsl_matrix_ptr(smat, 0, 0);
321  scan_ell(posi[i], (double(*)[3])ptr_C, coef);
322 
323  // Geolocate pixels
324  size_t ip = i * pdim;
325  uni_geonav(posi[i], veli[i], (double(*)[3])ptr_C, coef,
326  sunr[i], pview, pdim, &xlat[ip], &xlon[ip],
327  &solz[ip], &sola[ip], &senz[ip], &sena[ip], &range[ip]);
328  // mat2rpy(posi[i], veli[i], (double(*)[3])ptr_C, rpy[i]);
329  } else {
330  qfl[i] |= 4;
331  }
332  } // scan loop
333  gsl_matrix_free(smat);
334 
335  int geo_ncid;
336  int geo_gid[10];
337  createFile(geo_name.c_str(),
338  "$OCDATAROOT/hawkeye/Hawkeye_GEO_Data_Structure.cdl",
339  sdim, &geo_ncid, geo_gid);
340 
341  char buf[32];
342  strcpy(buf, unix2isodate(now(), 'G'));
343  nc_put_att_text(geo_ncid, NC_GLOBAL, "date_created", strlen(buf), buf);
344 
345  string varname;
346 
347  varname.assign("scan_time");
348  status = nc_inq_varid(geo_gid[scan_attr_geo], varname.c_str(), &varid);
349  status = nc_put_var_double(geo_gid[scan_attr_geo], varid, stime);
350  check_err(status, __LINE__, __FILE__);
351 
352  status = nc_put_att_text(geo_ncid, NC_GLOBAL,
353  "time_coverage_start", strlen(tstart), tstart);
354 
355  status = nc_put_att_text(geo_ncid, NC_GLOBAL,
356  "time_coverage_end", strlen(tend), tend);
357 
358  varname.assign("att_quat");
359  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
360  status = nc_put_var_float(geo_gid[nav_geo], varid, (float *)quati);
361  check_err(status, __LINE__, __FILE__);
362 
363  varname.assign("att_ang");
364  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
365  status = nc_put_var_float(geo_gid[nav_geo], varid, (float *)ang);
366  check_err(status, __LINE__, __FILE__);
367 
368  varname.assign("orb_pos");
369  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
370  status = nc_put_var_float(geo_gid[nav_geo], varid, (float *)posi);
371  check_err(status, __LINE__, __FILE__);
372 
373  varname.assign("orb_vel");
374  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
375  status = nc_put_var_float(geo_gid[nav_geo], varid, (float *)veli);
376  check_err(status, __LINE__, __FILE__);
377 
378  varname.assign("sun_ref");
379  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
380  status = nc_put_var_float(geo_gid[nav_geo], varid, (float *)sunr);
381  check_err(status, __LINE__, __FILE__);
382 
383  varname.assign("sc_to_hawkeye");
384  status = nc_inq_varid(geo_gid[nav_geo], varname.c_str(), &varid);
385  status = nc_put_var_double(geo_gid[nav_geo], varid, (double *)sc_to_hwk);
386  check_err(status, __LINE__, __FILE__);
387 
388  varname.assign("latitude");
389  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
390  status = nc_put_var_float(geo_gid[geolocation], varid, xlat);
391  check_err(status, __LINE__, __FILE__);
392 
393  varname.assign("longitude");
394  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
395  status = nc_put_var_float(geo_gid[geolocation], varid, xlon);
396  check_err(status, __LINE__, __FILE__);
397 
398  varname.assign("range");
399  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
400  status = nc_put_var_short(geo_gid[geolocation], varid, range);
401  check_err(status, __LINE__, __FILE__);
402 
403  varname.assign("quality_flag");
404  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
405  status = nc_put_var_ubyte(geo_gid[geolocation], varid, qfl);
406  check_err(status, __LINE__, __FILE__);
407 
408  varname.assign("sensor_azimuth");
409  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
410  status = nc_put_var_short(geo_gid[geolocation], varid, sena);
411  check_err(status, __LINE__, __FILE__);
412 
413  varname.assign("sensor_zenith");
414  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
415  status = nc_put_var_short(geo_gid[geolocation], varid, senz);
416  check_err(status, __LINE__, __FILE__);
417 
418  varname.assign("solar_azimuth");
419  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
420  status = nc_put_var_short(geo_gid[geolocation], varid, sola);
421  check_err(status, __LINE__, __FILE__);
422 
423  varname.assign("solar_zenith");
424  status = nc_inq_varid(geo_gid[geolocation], varname.c_str(), &varid);
425  status = nc_put_var_short(geo_gid[geolocation], varid, solz);
426  check_err(status, __LINE__, __FILE__);
427 
428  delete[](att_time);
429  delete[](orb_time);
430  delete[](att_quat);
431  delete[](orb_pos);
432  delete[](orb_vel);
433  delete[](stime);
434  delete[](quatr);
435  delete[](posr);
436  delete[](velr);
437  delete[](posi);
438  delete[](veli);
439  delete[](rpy);
440  delete[](quati);
441  delete[](xlon);
442  delete[](xlat);
443  delete[](solz);
444  delete[](sola);
445  delete[](senz);
446  delete[](sena);
447  delete[](range);
448  delete[](qfl);
449  delete[](pview);
450  delete[](sunr);
451 
452  return 0;
453 }
454 
455 int j2000_to_ecr(int32_t iyr, int32_t idy, double sec, double ecmat[3][3]) {
456  // Get J2000 to ECEF transformation matrix
457 
458  // Arguments:
459 
460  // Name Type I/O Description
461  // --------------------------------------------------------
462  // iyr I I Year, four digits
463  // idy I I Day of year
464  // sec R*8 I Seconds of day
465  // ecmat(3,3) R O J2000 to ECEF matrix
466 
467  // Get transformation from J2000 to mean-of-date inertial
468  double j2mod[3][3];
469  j2000_to_mod(iyr, idy, sec, j2mod);
470 
471  // Get nutation and UT1-UTC (once per run)
472  double xnut[3][3], ut1utc;
473  get_nut(iyr, idy, xnut);
474  get_ut1(iyr, idy, ut1utc);
475 
476  // Compute Greenwich hour angle for time of day
477  double day = idy + (sec + ut1utc) / 86400;
478  double gha, gham[3][3];
479  gha2000(iyr, day, gha);
480 
481  gham[0][0] = cos(gha / RADEG);
482  gham[1][1] = cos(gha / RADEG);
483  gham[2][2] = 1;
484  gham[0][1] = sin(gha / RADEG);
485  gham[1][0] = -sin(gha / RADEG);
486 
487  gham[0][2] = 0;
488  gham[2][0] = 0;
489  gham[1][2] = 0;
490  gham[2][1] = 0;
491 
492  // Combine all transformations
493  gsl_matrix_view A = gsl_matrix_view_array(&gham[0][0], 3, 3); // gham
494  gsl_matrix_view B = gsl_matrix_view_array(&xnut[0][0], 3, 3); // xnut
495  gsl_matrix *C = gsl_matrix_alloc(3, 3);
496 
497  gsl_blas_dgemm(CblasNoTrans, CblasTrans, 1.0, &A.matrix, &B.matrix, 0.0, C);
498 
499  gsl_matrix_view D = gsl_matrix_view_array(&j2mod[0][0], 3, 3); // j2mod
500  gsl_matrix *E = gsl_matrix_alloc(3, 3);
501  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, C, &D.matrix, 0.0, E);
502  double *ptr_E = gsl_matrix_ptr(E, 0, 0);
503 
504  memcpy(ecmat, ptr_E, 9 * sizeof(double));
505 
506  gsl_matrix_free(C);
507  gsl_matrix_free(E);
508 
509  return 0;
510 }
511 
512 int j2000_to_mod(int32_t iyr, int32_t idy, double sec, double j2mod[3][3]) {
513  // Get J2000 to MOD (precession) transformation
514 
515  // Arguments:
516 
517  // Name Type I/O Description
518  // --------------------------------------------------------
519  // iyr I I Year, four digits
520  // idy I I Day of year
521  // sec R*8 I Seconds of day
522  // j2mod(3,3) R O J2000 to MOD matrix
523 
524  int16_t iyear = iyr;
525  int16_t iday = idy;
526 
527  double t = jday(iyear, 1, iday) - (double)2451545.5 + sec / 86400;
528  t /= 36525;
529 
530  double zeta0 = t * (2306.2181 + 0.302 * t + 0.018 * t * t) / RADEG / 3600;
531  double thetap = t * (2004.3109 - 0.4266 * t - 0.04160 * t * t) / RADEG / 3600;
532  double xip = t * (2306.2181 + 1.095 * t + 0.018 * t * t) / RADEG / 3600;
533 
534  j2mod[0][0] = -sin(zeta0) * sin(xip) + cos(zeta0) * cos(xip) * cos(thetap);
535  j2mod[0][1] = -cos(zeta0) * sin(xip) - sin(zeta0) * cos(xip) * cos(thetap);
536  j2mod[0][2] = -cos(xip) * sin(thetap);
537  j2mod[1][0] = sin(zeta0) * cos(xip) + cos(zeta0) * sin(xip) * cos(thetap);
538  j2mod[1][1] = cos(zeta0) * cos(xip) - sin(zeta0) * sin(xip) * cos(thetap);
539  j2mod[1][2] = -sin(xip) * sin(thetap);
540  j2mod[2][0] = cos(zeta0) * sin(thetap);
541  j2mod[2][1] = -sin(zeta0) * sin(thetap);
542  j2mod[2][2] = cos(thetap);
543 
544  return 0;
545 }
546 
547 int get_nut(int32_t iyr, int32_t idy, double xnut[3][3]) {
548  int16_t iyear = iyr;
549  int16_t iday = idy;
550 
551  double t = jday(iyear, 1, iday) - (double)2451545.5;
552 
553  double xls, gs, xlm, omega;
554  double dpsi, eps, epsm;
555  ephparms(t, xls, gs, xlm, omega);
556  nutate(t, xls, gs, xlm, omega, dpsi, eps, epsm);
557 
558  xnut[0][0] = cos(dpsi / RADEG);
559  xnut[1][0] = -sin(dpsi / RADEG) * cos(epsm / RADEG);
560  xnut[2][0] = -sin(dpsi / RADEG) * sin(epsm / RADEG);
561  xnut[0][1] = sin(dpsi / RADEG) * cos(eps / RADEG);
562  xnut[1][1] = cos(dpsi / RADEG) * cos(eps / RADEG) * cos(epsm / RADEG) +
563  sin(eps / RADEG) * sin(epsm / RADEG);
564  xnut[2][1] = cos(dpsi / RADEG) * cos(eps / RADEG) * sin(epsm / RADEG) -
565  sin(eps / RADEG) * cos(epsm / RADEG);
566  xnut[0][2] = sin(dpsi / RADEG) * sin(eps / RADEG);
567  xnut[1][2] = cos(dpsi / RADEG) * sin(eps / RADEG) * cos(epsm / RADEG) -
568  cos(eps / RADEG) * sin(epsm / RADEG);
569  xnut[2][2] = cos(dpsi / RADEG) * sin(eps / RADEG) * sin(epsm / RADEG) +
570  cos(eps / RADEG) * cos(epsm / RADEG);
571 
572  return 0;
573 }
574 
575 int ephparms(double t, double &xls, double &gs, double &xlm, double &omega) {
576  // This subroutine computes ephemeris parameters used by other Mission
577  // Operations routines: the solar mean longitude and mean anomaly, and
578  // the lunar mean longitude and mean ascending node. It uses the model
579  // referenced in The Astronomical Almanac for 1984, Section S
580  // (Supplement) and documented for the SeaWiFS Project in "Constants
581  // and Parameters for SeaWiFS Mission Operations", in TBD. These
582  // parameters are used to compute the solar longitude and the nutation
583  // in longitude and obliquity.
584 
585  // Sun Mean Longitude
586  xls = (double)280.46592 + ((double)0.9856473516) * t;
587  xls = fmod(xls, (double)360);
588 
589  // Sun Mean Anomaly
590  gs = (double)357.52772 + ((double)0.9856002831) * t;
591  gs = fmod(gs, (double)360);
592 
593  // Moon Mean Longitude
594  xlm = (double)218.31643 + ((double)13.17639648) * t;
595  xlm = fmod(xlm, (double)360);
596 
597  // Ascending Node of Moon's Mean Orbit
598  omega = (double)125.04452 - ((double)0.0529537648) * t;
599  omega = fmod(omega, (double)360);
600 
601  return 0;
602 }
603 
604 int nutate(double t, double xls, double gs, double xlm, double omega,
605  double &dpsi, double &eps, double &epsm) {
606  // This subroutine computes the nutation in longitude and the obliquity
607  // of the ecliptic corrected for nutation. It uses the model referenced
608  // in The Astronomical Almanac for 1984, Section S (Supplement) and
609  // documented for the SeaWiFS Project in "Constants and Parameters for
610  // SeaWiFS Mission Operations", in TBD. These parameters are used to
611  // compute the apparent time correction to the Greenwich Hour Angle and
612  // for the calculation of the geocentric Sun vector. The input
613  // ephemeris parameters are computed using subroutine ephparms. Terms
614  // are included to 0.1 arcsecond.
615 
616  // Nutation in Longitude
617  dpsi = -17.1996 * sin(omega / RADEG) + 0.2062 * sin(2. * omega / RADEG) -
618  1.3187 * sin(2. * xls / RADEG) + 0.1426 * sin(gs / RADEG) -
619  0.2274 * sin(2. * xlm / RADEG);
620 
621  // Mean Obliquity of the Ecliptic
622  epsm = (double)23.439291 - ((double)3.560e-7) * t;
623 
624  // Nutation in Obliquity
625  double deps = 9.2025 * cos(omega / RADEG) + 0.5736 * cos(2. * xls / RADEG);
626 
627  // True Obliquity of the Ecliptic
628  eps = epsm + deps / 3600;
629 
630  dpsi = dpsi / 3600;
631 
632  return 0;
633 }
634 
635 int get_ut1(int32_t iyr, int32_t idy, double &ut1utc) {
636  int16_t iyear = iyr;
637  int16_t iday = idy;
638 
639  static int32_t ijd[25000];
640  static double ut1[25000];
641  string utcpole = "$OCVARROOT/modis/utcpole.dat";
642  static bool first = true;
643  int k = 0;
644  if (first) {
645  string line;
647  istringstream istr;
648 
649  ifstream utcpfile(utcpole.c_str());
650  if (utcpfile.is_open()) {
651  getline(utcpfile, line);
652  getline(utcpfile, line);
653  while (getline(utcpfile, line)) {
654  // cout << line << '\n';
655  istr.clear();
656  istr.str(line.substr(0, 5));
657  istr >> ijd[k];
658  istr.clear();
659  istr.str(line.substr(44, 9));
660  istr >> ut1[k];
661  k++;
662  }
663  ijd[k] = 0;
664  utcpfile.close();
665  first = false;
666  } else {
667  cout << utcpole.c_str() << " not found" << endl;
668  exit(1);
669  }
670  } // if (first)
671 
672  k = 0;
673  int mjd = jday(iyear, 1, iday) - 2400000;
674  while (ijd[k] > 0) {
675  if (mjd == ijd[k]) {
676  ut1utc = ut1[k];
677  break;
678  }
679  k++;
680  }
681 
682  return 0;
683 }
684 
685 int gha2000(int32_t iyr, double day, double &gha) {
686  // This subroutine computes the Greenwich hour angle in degrees for the
687  // input time. It uses the model referenced in The Astronomical Almanac
688  // for 1984, Section S (Supplement) and documented for the SeaWiFS
689  // Project in "Constants and Parameters for SeaWiFS Mission Operations",
690  // in TBD. It includes the correction to mean sidereal time for nutation
691  // as well as precession.
692  //
693 
694  // Compute days since J2000
695  int16_t iday = day;
696  double fday = day - iday;
697  int jd = jday(iyr, 1, iday);
698  double t = jd - (double)2451545.5 + fday;
699 
700  // Compute Greenwich Mean Sidereal Time (degrees)
701  double gmst = (double)100.4606184 + (double)0.9856473663 * t +
702  (double)2.908e-13 * t * t;
703 
704  // Check if need to compute nutation correction for this day
705  double xls, gs, xlm, omega;
706  double dpsi, eps, epsm;
707  ephparms(t, xls, gs, xlm, omega);
708  nutate(t, xls, gs, xlm, omega, dpsi, eps, epsm);
709 
710  // Include apparent time correction and time-of-day
711  gha = gmst + dpsi * cos(eps / RADEG) + fday * 360;
712  gha = fmod(gha, (double)360);
713 
714  return 0;
715 }
716 
717 int euler(float a[3], double xm[3][3]) {
718  // Computes coordinate transformation matrix corresponding to Euler
719  // sequence; assumes order of rotations is X, Y, Z
720  //
721  // Reference: Wertz, Appendix E
722 
723  double xm1[3][3];
724  double xm2[3][3];
725  double xm3[3][3];
726  double xmm[3][3];
727 
728  for(size_t i=0;i<3;i++) {
729  for (size_t j=0;j<3;j++) {
730  xm1[i][j]=0;
731  xm2[i][j]=0;
732  xm3[i][j]=0;
733  xmm[i][j]=0;
734  xm[i][j]=0;
735  }
736  }
737 
738  double c1=cos(a[0]/RADEG);
739  double s1=sin(a[0]/RADEG);
740  double c2=cos(a[1]/RADEG);
741  double s2=sin(a[1]/RADEG);
742  double c3=cos(a[2]/RADEG);
743  double s3=sin(a[2]/RADEG);
744 
745  // Convert individual rotations to matrices
746  xm1[0][0]=1.0;
747  xm1[1][1]=c1;
748  xm1[2][2]=c1;
749  xm1[1][2]=s1;
750  xm1[2][1]=-s1;
751  xm2[1][1]=1.0;
752  xm2[0][0]=c2;
753  xm2[2][2]=c2;
754  xm2[2][0]=s2;
755  xm2[0][2]=-s2;
756  xm3[2][2]=1.0;
757  xm3[1][1]=c3;
758  xm3[0][0]=c3;
759  xm3[0][1]=s3;
760  xm3[1][0]=-s3;
761 
762  // Compute total rotation as xm3*xm2*xm1
763  //xmm=xm2#xm1
764  gsl_matrix_view A = gsl_matrix_view_array(&xm2[0][0], 3, 3);
765  gsl_matrix_view B = gsl_matrix_view_array(&xm1[0][0], 3, 3);
766  gsl_matrix *E = gsl_matrix_alloc(3, 3);
767  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
768  &A.matrix, &B.matrix, 0.0, E);
769  double *ptr_E = gsl_matrix_ptr(E, 0, 0);
770  memcpy(xmm, ptr_E, 9 * sizeof(double));
771 
772  //xm=xm3#xmm
773  gsl_matrix_view C = gsl_matrix_view_array(&xm3[0][0], 3, 3);
774  gsl_matrix_view D = gsl_matrix_view_array(&xmm[0][0], 3, 3);
775  gsl_matrix *F = gsl_matrix_alloc(3, 3);
776  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0,
777  &C.matrix, &D.matrix, 0.0, F);
778  double *ptr_F = gsl_matrix_ptr(F, 0, 0);
779  memcpy(xm, ptr_F, 9 * sizeof(double));
780 
781  gsl_matrix_free(E);
782  gsl_matrix_free(F);
783  return 0;
784 }
785 
786 int mtoq(double rm[3][3], double q[4]) {
787  // Convert direction cosine matrix to equivalent quaternion
788 
789  double e[3];
790 
791  // Compute Euler angle
792  double phi;
793  double cphi = (rm[0][0] + rm[1][1] + rm[2][2] - 1) / 2;
794  if (fabs(cphi) < 0.98) {
795  phi = acos(cphi);
796  } else {
797  double ssphi = (pow(rm[0][1] - rm[1][0], 2) +
798  pow(rm[2][0] - rm[0][2], 2) +
799  pow(rm[1][2] - rm[2][1], 2)) /
800  4;
801  phi = asin(sqrt(ssphi));
802  if (cphi < 0) phi = PI - phi;
803  }
804 
805  // Compute Euler axis
806  e[0] = (rm[2][1] - rm[1][2]) / (sin(phi) * 2);
807  e[1] = (rm[0][2] - rm[2][0]) / (sin(phi) * 2);
808  e[2] = (rm[1][0] - rm[0][1]) / (sin(phi) * 2);
809  double norm = sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
810  e[0] = e[0] / norm;
811  e[1] = e[1] / norm;
812  e[2] = e[2] / norm;
813 
814  // Compute quaternion
815  q[0] = e[0] * sin(phi / 2);
816  q[1] = e[1] * sin(phi / 2);
817  q[2] = e[2] * sin(phi / 2);
818  q[3] = cos(phi / 2);
819 
820  return 0;
821 }
822 
823 int qprod(double q1[4], float q2[4], double q3[4]) {
824  // Compute the product of two quaternions q3 = q1*q2
825 
826  q3[0] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
827  q3[1] = -q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
828  q3[2] = q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3] + q1[3] * q2[2];
829  q3[3] = -q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] + q1[3] * q2[3];
830 
831  return 0;
832 }
833 
834 int qprod(float q1[4], float q2[4], float q3[4]) {
835  // Compute the product of two quaternions q3 = q1*q2
836 
837  q3[0] = q1[0] * q2[3] + q1[1] * q2[2] - q1[2] * q2[1] + q1[3] * q2[0];
838  q3[1] = -q1[0] * q2[2] + q1[1] * q2[3] + q1[2] * q2[0] + q1[3] * q2[1];
839  q3[2] = q1[0] * q2[1] - q1[1] * q2[0] + q1[2] * q2[3] + q1[3] * q2[2];
840  q3[3] = -q1[0] * q2[0] - q1[1] * q2[1] - q1[2] * q2[2] + q1[3] * q2[3];
841 
842  return 0;
843 }
844 
845 int orb_interp(size_t n_orb_rec, size_t sdim, double *torb, orb_array *p,
846  orb_array *v, double *time, orb_array *posi, orb_array *veli) {
847  // Purpose: Interpolate orbit position and velocity vectors to scan line
848  // times
849  //
850  //
851  // Calling Arguments:
852  //
853  // Name Type I/O Description
854  // -------- ---- --- -----------
855  // torb(*) double I Array of orbit vector times in seconds of day
856  // p(3,*) float I Array of orbit position vectors for
857  // each time torb
858  // v(3,*) float I Array of orbit velocity vectors for
859  // each time torb
860  // time(*) double I Array of time in seconds of day
861  // for every scan line
862  // pi(3,*) float O Array of interpolated positions
863  // vi(3,*) float O Array of interpolated velocities
864  //
865  //
866  // By: Frederick S. Patt, GSC, August 10, 1993
867  //
868  // Notes: Method uses cubic polynomial to match positions and velocities
869  // at input data points.
870  //
871  // Modification History:
872  //
873  // Created IDL version from FORTRAN code. F.S. Patt, SAIC, November 29, 2006
874  //
875 
876  double a0[3], a1[3], a2[3], a3[3];
877 
878  /*
879  // Make sure that first orbit vector precedes first scan line
880  k = where (time lt torb(0))
881  if (k(0) ne -1) then begin
882  posi(*,k) = 0.0
883  veli(*,k) = 0.0
884  orbfl(k) = 1
885  print, 'Scan line times before available orbit data'
886  i1 = max(k) + 1
887  endif
888  */
889 
890  size_t ind = 0;
891  for (size_t i = 0; i < sdim; i++) {
892  // Find input orbit vectors bracketing scan
893  for (size_t j = ind; j < n_orb_rec; j++) {
894  if (time[i] > torb[j] && time[i] <= torb[j + 1]) {
895  ind = j;
896  break;
897  }
898  }
899 
900  // Set up cubic interpolation
901  double dt = torb[ind + 1] - torb[ind];
902  for (size_t j = 0; j < 3; j++) {
903  a0[j] = p[ind][j];
904  a1[j] = v[ind][j] * dt;
905  a2[j] = 3 * p[ind + 1][j] - 3 * p[ind][j] - 2 * v[ind][j] * dt -
906  v[ind + 1][j] * dt;
907  a3[j] = 2 * p[ind][j] - 2 * p[ind + 1][j] + v[ind][j] * dt +
908  v[ind + 1][j] * dt;
909  }
910 
911  // Interpolate orbit position and velocity components to scan line time
912  double x = (time[i] - torb[ind]) / dt;
913  double x2 = x * x;
914  double x3 = x2 * x;
915  for (size_t j = 0; j < 3; j++) {
916  posi[i][j] = a0[j] + a1[j] * x + a2[j] * x2 + a3[j] * x3;
917  veli[i][j] = (a1[j] + 2 * a2[j] * x + 3 * a3[j] * x2) / dt;
918  }
919  } // i-loop
920 
921  return 0;
922 }
923 
924 int q_interp(size_t n_att_rec, size_t sdim, double *tq, quat_array *q,
925  double *time, quat_array *qi) {
926  // Purpose: Interpolate quaternions to scan line times
927  //
928  //
929 
930  size_t ind = 0;
931  for (size_t i = 0; i < sdim; i++) {
932  // Find input attitude vectors bracketing scan
933  for (size_t j = ind; j < n_att_rec; j++) {
934  if (time[i] > tq[j] && time[i] <= tq[j + 1]) {
935  ind = j;
936  break;
937  }
938  }
939 
940  // Set up quaternion interpolation
941  double dt = tq[ind + 1] - tq[ind];
942  double qin[4];
943  qin[0] = -q[ind][0];
944  qin[1] = -q[ind][1];
945  qin[2] = -q[ind][2];
946  qin[3] = q[ind][3];
947 
948  double e[3], qr[4];
949  qprod(qin, q[ind + 1], qr);
950  memcpy(e, qr, 3 * sizeof(double));
951  double sto2 = sqrt(e[0] * e[0] + e[1] * e[1] + e[2] * e[2]);
952  for (size_t j = 0; j < 3; j++) e[j] /= sto2;
953 
954  // Interpolate quaternion to scan times
955  double x = (time[i] - tq[ind]) / dt;
956  float qri[4], qp[4];
957  for (size_t j = 0; j < 3; j++) qri[j] = e[j] * sto2 * x;
958  qri[3] = 1.0;
959  qprod(q[ind], qri, qp);
960  memcpy(qi[i], qp, 4 * sizeof(float));
961  }
962 
963  return 0;
964 }
965 
966 int l_sun(size_t sdim, int32_t iyr, int32_t iday, double *sec,
967  orb_array *sunr) {
968  // Computes unit Sun vector in geocentric rotating coordinates, using
969  // subprograms to compute inertial Sun vector and Greenwich hour angle
970 
971  // Get unit Sun vector in geocentric inertial coordinates
972  sun2000(sdim, iyr, iday, sec, sunr);
973 
974  // Get Greenwich mean sidereal angle
975  for (size_t i = 0; i < sdim; i++) {
976  double day = iday + sec[i] / 86400;
977  double gha;
978  gha2000(iyr, day, gha);
979  double ghar = gha / RADEG;
980 
981  // Transform Sun vector into geocentric rotating frame
982  float temp0 = sunr[i][0] * cos(ghar) + sunr[i][1] * sin(ghar);
983  float temp1 = sunr[i][1] * cos(ghar) - sunr[i][0] * sin(ghar);
984  sunr[i][0] = temp0;
985  sunr[i][1] = temp1;
986  }
987 
988  return 0;
989 }
990 
991 int sun2000(size_t sdim, int32_t iyr, int32_t idy, double *sec,
992  orb_array *sun) {
993  // This subroutine computes the Sun vector in geocentric inertial
994  // (equatorial) coordinates. It uses the model referenced in The
995  // Astronomical Almanac for 1984, Section S (Supplement) and documented
996  // for the SeaWiFS Project in "Constants and Parameters for SeaWiFS
997  // Mission Operations", in TBD. The accuracy of the Sun vector is
998  // approximately 0.1 arcminute.
999 
1000  float xk = 0.0056932; // Constant of aberration
1001 
1002  // Compute floating point days since Jan 1.5, 2000
1003  // Note that the Julian day starts at noon on the specified date
1004  int16_t iyear = iyr;
1005  int16_t iday = idy;
1006 
1007  for (size_t i = 0; i < sdim; i++) {
1008  double t =
1009  jday(iyear, 1, iday) - (double)2451545.0 + (sec[i] - 43200) / 86400;
1010 
1011  double xls, gs, xlm, omega;
1012  double dpsi, eps, epsm;
1013  // Compute solar ephemeris parameters
1014  ephparms(t, xls, gs, xlm, omega);
1015 
1016  // Compute nutation corrections for this day
1017  nutate(t, xls, gs, xlm, omega, dpsi, eps, epsm);
1018 
1019  // Compute planet mean anomalies
1020  // Venus Mean Anomaly
1021  double g2 = 50.40828 + 1.60213022 * t;
1022  g2 = fmod(g2, (double)360);
1023 
1024  // Mars Mean Anomaly
1025  double g4 = 19.38816 + 0.52402078 * t;
1026  g4 = fmod(g4, (double)360);
1027 
1028  // Jupiter Mean Anomaly
1029  double g5 = 20.35116 + 0.08309121 * t;
1030  g5 = fmod(g5, (double)360);
1031 
1032  // Compute solar distance (AU)
1033  double rs =
1034  1.00014 - 0.01671 * cos(gs / RADEG) - 0.00014 * cos(2. * gs / RADEG);
1035 
1036  // Compute Geometric Solar Longitude
1037  double dls = (6893. - 4.6543463e-4 * t) * sin(gs / RADEG) +
1038  72. * sin(2. * gs / RADEG) - 7. * cos((gs - g5) / RADEG) +
1039  6. * sin((xlm - xls) / RADEG) +
1040  5. * sin((4. * gs - 8. * g4 + 3. * g5) / RADEG) -
1041  5. * cos((2. * gs - 2. * g2) / RADEG) -
1042  4. * sin((gs - g2) / RADEG) +
1043  4. * cos((4. * gs - 8. * g4 + 3. * g5) / RADEG) +
1044  3. * sin((2. * gs - 2. * g2) / RADEG) - 3. * sin(g5 / RADEG) -
1045  3. * sin((2. * gs - 2. * g5) / RADEG);
1046 
1047  double xlsg = xls + dls / 3600;
1048 
1049  // Compute Apparent Solar Longitude// includes corrections for nutation
1050  // in longitude and velocity aberration
1051  double xlsa = xlsg + dpsi - xk / rs;
1052 
1053  // Compute unit Sun vector
1054  sun[i][0] = cos(xlsa / RADEG);
1055  sun[i][1] = sin(xlsa / RADEG) * cos(eps / RADEG);
1056  sun[i][2] = sin(xlsa / RADEG) * sin(eps / RADEG);
1057  } // i-loop
1058 
1059  return 0;
1060 }
1061 
1062 int qtom(float q[4], double rm[3][3]) {
1063  // Convert quaternion to equivalent direction cosine matrix
1064 
1065  rm[0][0] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
1066  rm[0][1] = 2 * (q[0] * q[1] + q[2] * q[3]);
1067  rm[0][2] = 2 * (q[0] * q[2] - q[1] * q[3]);
1068  rm[1][0] = 2 * (q[0] * q[1] - q[2] * q[3]);
1069  rm[1][1] = -q[0] * q[0] + q[1] * q[1] - q[2] * q[2] + q[3] * q[3];
1070  rm[1][2] = 2 * (q[1] * q[2] + q[0] * q[3]);
1071  rm[2][0] = 2 * (q[0] * q[2] + q[1] * q[3]);
1072  rm[2][1] = 2 * (q[1] * q[2] - q[0] * q[3]);
1073  rm[2][2] = -q[0] * q[0] - q[1] * q[1] + q[2] * q[2] + q[3] * q[3];
1074 
1075  return 0;
1076 }
1077 
1078 int scan_ell(float p[3], double sm[3][3], double coef[10]) {
1079  // This program calculates the coefficients which
1080  // represent the Earth scan track in the sensor frame.
1081 
1082  // The reference ellipsoid uses an equatorial radius of 6378.137 km and
1083  // a flattening factor of 1/298.257 (WGS 1984).
1084 
1085  // Calling Arguments
1086  //
1087  // Name Type I/O Description
1088  //
1089  // pos(3) R*4 I ECR Orbit Position Vector (km)
1090  // smat(3,3) R*4 I Sensor Orientation Matrix
1091  // coef(10) R*4 O Scan path coefficients
1092 
1093  double re = 6378.137;
1094  double f = 1 / 298.257;
1095  double omf2 = (1 - f) * (1 - f);
1096 
1097  // Compute constants for navigation model using Earth radius values
1098  double rd = 1 / omf2;
1099 
1100  // Compute coefficients of intersection ellipse in scan plane
1101  coef[0] = 1 + (rd - 1) * sm[0][2] * sm[0][2];
1102  coef[1] = 1 + (rd - 1) * sm[1][2] * sm[1][2];
1103  coef[2] = 1 + (rd - 1) * sm[2][2] * sm[2][2];
1104  coef[3] = (rd - 1) * sm[0][2] * sm[1][2] * 2;
1105  coef[4] = (rd - 1) * sm[0][2] * sm[2][2] * 2;
1106  coef[5] = (rd - 1) * sm[1][2] * sm[2][2] * 2;
1107  coef[6] = (sm[0][0] * p[0] + sm[0][1] * p[1] + sm[0][2] * p[2] * rd) * 2;
1108  coef[7] = (sm[1][0] * p[0] + sm[1][1] * p[1] + sm[1][2] * p[2] * rd) * 2;
1109  coef[8] = (sm[2][0] * p[0] + sm[2][1] * p[1] + sm[2][2] * p[2] * rd) * 2;
1110  coef[9] = p[0] * p[0] + p[1] * p[1] + p[2] * p[2] * rd - re * re;
1111 
1112  return 0;
1113 }
1114 
1115 int uni_geonav(float pos[3], float vel[3], double smat[3][3], double coef[10],
1116  float sunr[3], orb_array *pview, size_t npix, float *xlat,
1117  float *xlon, short *solz, short *sola, short *senz, short *sena,
1118  short *range) {
1119  // This subroutine performs navigation of a scanning sensor on the
1120  // surface of an ellipsoid based on an input orbit position vector and
1121  // spacecraft orientation matrix. It uses a closed-form algorithm for
1122  // determining points on the ellipsoidal surface which involves
1123  // determining the intersection of the scan plan with the ellipsoid.
1124  // The sensor view vectors in the sensor frame are passed in as a 3xN array.
1125 
1126  // The reference ellipsoid is set according to the scan
1127  // intersection coefficients in the calling sequence// an equatorial
1128  // radius of 6378.137 km. and a flattening factor of 1/298.257 are
1129  // used by both the Geodetic Reference System (GRS) 1980 and the
1130  // World Geodetic System (WGS) 1984.
1131 
1132  // It then computes geometric parameters using the pixel locations on
1133  // the Earth, the spacecraft position vector and the unit Sun vector in
1134  // the geocentric rotating reference frame. The outputs are arrays of
1135  // geodetic latitude and longitude, solar zenith and azimuth and sensor
1136  // zenith and azimuth. The azimuth angles are measured from local
1137  // North toward East. Flag values of 999. are returned for any pixels
1138  // whose scan angle is past the Earth's horizon.
1139 
1140  // Reference: "Exact closed-form geolocation geolocation algorithm for
1141  // Earth survey sensors", F. S. Patt and W. W. Gregg, IJRS, Vol. 15
1142  // No. 18, 1994.
1143 
1144  // Calling Arguments
1145 
1146  // Name Type I/O Description
1147  //
1148  // pos(3) R*4 I ECR Orbit Position Vector (km) at scan
1149  // mid-time
1150  // vel(3) R*4 I ECR Orbit Velocity Vector (km/sec)
1151  // smat(3,3) R*4 I Sensor Orientation Matrix
1152  // coef(10) R*4 I Scan path coefficients
1153  // sunr(3) R*4 I Sun unit vector in geocentric rotating
1154  // reference frame
1155  // pview(3,*) R*4 I Array of sensor view vectors
1156  // npix R*4 I Number of pixels to geolocate
1157  // xlat(*) R*4 O Pixel geodetic latitudes
1158  // xlon(*) R*4 O Pixel geodetic longitudes
1159  // solz(*) I*2 O Pixel solar zenith angles
1160  // sola(*) I*2 O Pixel solar azimuth angles
1161  // senz(*) I*2 O Pixel sensor zenith angles
1162  // sena(*) I*2 O Pixel sensor azimuth angles
1163  //
1164 
1165  // Program written by: Frederick S. Patt
1166  // General Sciences Corporation
1167  // October 20, 1992
1168  //
1169  // Modification History:
1170 
1171  // Created universal version of the SeaWiFS geolocation algorithm
1172  // by specifying view vectors as an input. F. S. Patt, SAIC, 11/17/09
1173 
1174  // Earth ellipsoid parameters
1175  float f = 1 / 298.257;
1176  float omf2 = (1 - f) * (1 - f);
1177  gsl_vector *C = gsl_vector_alloc(3);
1178 
1179  for (size_t i = 0; i < npix; i++) {
1180  // Compute sensor-to-surface vectors for all scan angles
1181  // Compute terms for quadratic equation
1182  float o = coef[0] * pview[i][0] * pview[i][0] +
1183  coef[1] * pview[i][1] * pview[i][1] +
1184  coef[2] * pview[i][2] * pview[i][2] +
1185  coef[3] * pview[i][0] * pview[i][1] +
1186  coef[4] * pview[i][0] * pview[i][2] +
1187  coef[5] * pview[i][1] * pview[i][2];
1188 
1189  float p =
1190  coef[6] * pview[i][0] + coef[7] * pview[i][1] + coef[8] * pview[i][2];
1191 
1192  float q = coef[9];
1193 
1194  float r = p * p - 4 * q * o;
1195 
1196  xlat[i] = -999;
1197  xlon[i] = -999;
1198 
1199  solz[i] = -999;
1200  sola[i] = -999;
1201  senz[i] = -999;
1202  sena[i] = -999;
1203  range[i] = -999;
1204 
1205  // Check for scan past edge of Earth
1206  if (r >= 0) {
1207  // Solve for magnitude of sensor-to-pixel vector and compute components
1208  float d = (-p - sqrt(r)) / (2 * o);
1209  double x1[3];
1210  for (size_t j = 0; j < 3; j++) x1[j] = d * pview[i][j];
1211 
1212  // Transform vector from sensor to geocentric frame
1213 
1214  gsl_matrix_view A = gsl_matrix_view_array((double *)smat, 3, 3);
1215  gsl_vector_view B = gsl_vector_view_array(x1, 3);
1216 
1217  gsl_blas_dgemv(CblasTrans, 1.0, &A.matrix, &B.vector, 0.0, C);
1218 
1219  float rh[3], geovec[3];
1220  double *ptr_C = gsl_vector_ptr(C, 0);
1221  for (size_t j = 0; j < 3; j++) {
1222  rh[j] = ptr_C[j];
1223  geovec[j] = pos[j] + rh[j];
1224  }
1225 
1226  // Compute the local vertical, East and North unit vectors
1227  float uxy = geovec[0] * geovec[0] + geovec[1] * geovec[1];
1228  float temp = sqrt(geovec[2] * geovec[2] + omf2 * omf2 * uxy);
1229 
1230  float up[3];
1231  up[0] = omf2 * geovec[0] / temp;
1232  up[1] = omf2 * geovec[1] / temp;
1233  up[2] = geovec[2] / temp;
1234  float upxy = sqrt(up[0] * up[0] + up[1] * up[1]);
1235 
1236  float ea[3];
1237  ea[0] = -up[1] / upxy;
1238  ea[1] = up[0] / upxy;
1239  ea[2] = 0.0;
1240 
1241  // no = crossp(up,ea)
1242  float no[3];
1243  no[0] = -up[2] * ea[1];
1244  no[1] = up[2] * ea[0];
1245  no[2] = up[0] * ea[1] - up[1] * ea[0];
1246 
1247  // Compute geodetic latitude and longitude
1248  xlat[i] = RADEG * asin(up[2]);
1249  xlon[i] = RADEG * atan2(up[1], up[0]);
1250  range[i] = (short)((d - 400) * 10);
1251 
1252  // Transform the pixel-to-spacecraft and Sun vectors into local frame
1253  float rl[3], sl[3];
1254  rl[0] = -ea[0] * rh[0] - ea[1] * rh[1] - ea[2] * rh[2];
1255  rl[1] = -no[0] * rh[0] - no[1] * rh[1] - no[2] * rh[2];
1256  rl[2] = -up[0] * rh[0] - up[1] * rh[1] - up[2] * rh[2];
1257 
1258  sl[0] = sunr[0] * ea[0] + sunr[1] * ea[1] + sunr[2] * ea[2];
1259  sl[1] = sunr[0] * no[0] + sunr[1] * no[1] + sunr[2] * no[2];
1260  sl[2] = sunr[0] * up[0] + sunr[1] * up[1] + sunr[2] * up[2];
1261 
1262  // Compute the solar zenith and azimuth
1263  solz[i] = (short)(100 * RADEG *
1264  atan2(sqrt(sl[0] * sl[0] + sl[1] * sl[1]), sl[2]));
1265 
1266  // Check for zenith close to zero
1267  if (solz[i] > 0.01) sola[i] = (short)(100 * RADEG * atan2(sl[0], sl[1]));
1268 
1269  // Compute the sensor zenith and azimuth
1270  senz[i] = (short)(100 * RADEG *
1271  atan2(sqrt(rl[0] * rl[0] + rl[1] * rl[1]), rl[2]));
1272 
1273  // Check for zenith close to zero
1274  if (senz[i] > 0.01) sena[i] = (short)(100 * RADEG * atan2(rl[0], rl[1]));
1275 
1276  } // if on-earth
1277  } // pixel loop
1278 
1279  gsl_vector_free(C);
1280 
1281  return 0;
1282 }
1283 
1284 int mat2rpy(float pos[3], float vel[3], double smat[3][3], float rpy[3], double om[3][3]) {
1285  // This program calculates the attitude angles from the ECEF orbit vector and
1286  // attitude matrix. The rotation order is (1,2,3).
1287 
1288  // The reference ellipsoid uses an equatorial radius of 6378.137 km and
1289  // a flattening factor of 1/298.257 (WGS 1984).
1290 
1291  // Calling Arguments
1292 
1293  // Name Type I/O Description
1294  //
1295  // pos(3) R*4 I Orbit position vector (ECEF)
1296  // vel(3) R*4 I Orbit velocity vector (ECEF)
1297  // smat(3,3) R*8 I Sensor attitude matrix (ECEF to sensor)
1298  // rpy(3) R*4 O Attitude angles (roll, pitch, yaw)
1299  // om(3,3) R*4 O ECEF-to-orbital frame matrix
1300 
1301  double rem = 6371;
1302  double f = 1 / (double)298.257;
1303  double omegae = 7.29211585494e-5;
1304  double omf2 = (1 - f) * (1 - f);
1305 
1306  // Determine local vertical reference axes
1307  double p[3], v[3];
1308  for (size_t j = 0; j < 3; j++) {
1309  p[j] = (double)pos[j];
1310  v[j] = (double)vel[j];
1311  }
1312  v[0] -= p[1] * omegae;
1313  v[1] += p[0] * omegae;
1314 
1315  // Compute Z axis as local nadir vector
1316  double pm = sqrt(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
1317  double omf2p = (omf2 * rem + pm - rem) / pm;
1318  double pxy = p[0] * p[0] + p[1] * p[1];
1319  double temp = sqrt(p[2] * p[2] + omf2p * omf2p * pxy);
1320 
1321  double z[3];
1322  z[0] = -omf2p * p[0] / temp;
1323  z[1] = -omf2p * p[1] / temp;
1324  z[2] = -p[2] / temp;
1325 
1326  // Compute Y axis along negative orbit normal
1327  double on[3];
1328  on[0] = v[1] * z[2] - v[2] * z[1];
1329  on[1] = v[2] * z[0] - v[0] * z[2];
1330  on[2] = v[0] * z[1] - v[1] * z[0];
1331 
1332  double onm = sqrt(on[0] * on[0] + on[1] * on[1] + on[2] * on[2]);
1333 
1334  double y[3];
1335  for (size_t j = 0; j < 3; j++) y[j] = -on[j] / onm;
1336 
1337  // Compute X axis to complete orthonormal triad (velocity direction)
1338  double x[3];
1339  x[0] = y[1] * z[2] - y[2] * z[1];
1340  x[1] = y[2] * z[0] - y[0] * z[2];
1341  x[2] = y[0] * z[1] - y[1] * z[0];
1342 
1343  // Store local vertical reference vectors in matrix
1344  // double om[3][3];
1345  memcpy(&om[0][0], &x, 3 * sizeof(double));
1346  memcpy(&om[1][0], &y, 3 * sizeof(double));
1347  memcpy(&om[2][0], &z, 3 * sizeof(double));
1348 
1349  // Compute orbital-to-spacecraft matrix
1350  double rm[3][3];
1351  gsl_matrix_view rm_mat = gsl_matrix_view_array(&rm[0][0], 3, 3);
1352 
1353  int s;
1354 
1355  gsl_permutation *perm = gsl_permutation_alloc(3);
1356 
1357  // Compute the LU decomposition of this matrix
1358 
1359  double B_mat[3][3];
1360  memcpy(&B_mat[0][0], &om[0][0], 9 * sizeof(double));
1361  gsl_matrix_view B = gsl_matrix_view_array(&B_mat[0][0], 3, 3);
1362 
1363  gsl_linalg_LU_decomp(&B.matrix, perm, &s);
1364 
1365  // Compute the inverse of the LU decomposition
1366  double inv[3][3];
1367  gsl_matrix_view inv_mat = gsl_matrix_view_array(&inv[0][0], 3, 3);
1368 
1369  gsl_linalg_LU_invert(&B.matrix, perm, &inv_mat.matrix);
1370 
1371  gsl_matrix_view A = gsl_matrix_view_array(&smat[0][0], 3, 3);
1372 
1373  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, &A.matrix, &inv_mat.matrix,
1374  0.0, &rm_mat.matrix);
1375 
1376  gsl_permutation_free(perm);
1377 
1378  // Compute attitude angles
1379  rpy[0] = RADEG * atan(-rm[2][1] / rm[2][2]);
1380  // double cosp = sqrt(rm[2][1] * rm[2][1] + rm[2][2] * rm[2][2]);
1381  // if (rm[2][2] < 0) cosp *= -1;
1382  // rpy[1] = RADEG * atan2(rm[2][0], cosp);
1383  rpy[1] = RADEG * asin(rm[2][0]);
1384  rpy[2] = RADEG * atan(-rm[1][0] / rm[0][0]);
1385 
1386  return 0;
1387 }
int qprod(double q1[4], float q2[4], double q3[4])
data_t q
Definition: decode_rs.h:74
int r
Definition: decode_rs.h:73
data_t t[NROOTS+1]
Definition: decode_rs.h:77
int j
Definition: decode_rs.h:73
int32_t day
int status
Definition: l1_czcs_hdf.c:32
int orb_interp(size_t n_orb_rec, size_t sdim, double *torb, orb_array *p, orb_array *v, double *time, orb_array *posi, orb_array *veli)
void check_err(const int stat, const int line, const char *file)
Definition: nc4utils.c:35
int j2000_to_mod(int32_t iyr, int32_t idy, double sec, double j2mod[3][3])
float orb_array[3]
float quat_array[4]
const double C
int createFile(const char *filename, const char *cdlfile, size_t sdim, int *ncid, int *gid)
Definition: hawkeyeUtil.cpp:39
int j2000_to_ecr(int32_t iyr, int32_t idy, double sec, double ecmat[3][3])
#define VERSION
int mat2rpy(float pos[3], float vel[3], double smat[3][3], float rpy[3], double om[3][3])
float32 * pos
Definition: l1_czcs_hdf.c:35
int32 * msec
Definition: l1_czcs_hdf.c:31
int32_t jday(int16_t i, int16_t j, int16_t k)
Definition: jday.c:4
const double F
#define PI
Definition: l3_get_org.c:6
int uni_geonav(float pos[3], float vel[3], double smat[3][3], double coef[10], float sunr[3], orb_array *pview, size_t npix, float *xlat, float *xlon, short *solz, short *sola, short *senz, short *sena, short *range)
int mtoq(double rm[3][3], double q[4])
int get_ut1(int32_t iyr, int32_t idy, double &ut1utc)
double precision function f(R1)
Definition: tmd.lp.f:1454
data_t omega[NROOTS+1]
Definition: decode_rs.h:77
float rd(float x, float y, float z)
int euler(float a[3], double xm[3][3])
#define re
Definition: l1_czcs_hdf.c:701
int main(int argc, char *argv[])
int expandEnvVar(string *sValue)
Definition: hawkeyeUtil.h:41
Definition: jd.py:1
int gha2000(int32_t iyr, double day, double &gha)
integer, parameter double
constexpr float focal_length
void isodate2ydmsec(char *date, int32_t *year, int32_t *day, int32_t *msec)
Definition: date2ydmsec.c:20
int qtom(float q[4], double rm[3][3])
#define RADEG
Definition: czcs_ctl_pt.c:5
int l_sun(size_t sdim, int32_t iyr, int32_t iday, double *sec, orb_array *sunr)
int sun2000(size_t sdim, int32_t iyr, int32_t idy, double *sec, orb_array *sun)
float xlon[LAC_PIXEL_NUM]
Definition: l1a_seawifs.c:93
int q_interp(size_t n_att_rec, size_t sdim, double *tq, quat_array *q, double *time, quat_array *qi)
float pm[MODELMAX]
#define omf2
Definition: l1_czcs_hdf.c:703
this program makes no use of any feature of the SDP Toolkit that could generate such a then geolocation is calculated at that and then aggregated up to Resolved feature request Bug by adding three new int8 SDSs for each high resolution offsets between the high resolution geolocation and a bi linear interpolation extrapolation of the positions This can be used to reconstruct the high resolution geolocation Resolved Bug by delaying cumulation of gflags until after validation of derived products Resolved Bug by setting Latitude and Longitude to the correct fill resolving to support Near Real Time because they may be unnecessary if use of entrained ephemeris and attitude data is turned resolving bug report Corrected to filter out Aqua attitude records with missing status helping resolve bug MOD_PR03 will still correctly write scan and pixel data that does not depend upon the start time
Definition: HISTORY.txt:248
#define fabs(a)
Definition: misc.h:93
int scan_ell(float p[3], double sm[3][3], double coef[10])
u5 which has been done in the LOCALGRANULEID metadata should have an extension NRT It is requested to identify the NRT production Changes from v6 which may affect scientific the sector rotation may actually occur during one of the scans earlier than the one where it is first reported As a the b1 values are about the LOCALGRANULEID metadata should have an extension NRT It is requested to identify the NRT to fill pixels affected by dead subframes with a special value Output the metadata of noisy and dead subframe Dead Subframe EV and Detector Quality Flag2 Removed the function call of Fill_Dead_Detector_SI to stop interpolating SI values for dead but also for all downstream products for science test only Changes from v5 which will affect scientific to conform to MODIS requirements Removed the Mixed option from the ScanType in the code because the L1A Scan Type is never Mixed Changed for ANSI C compliance and comments to better document the fact that when the HDF_EOS metadata is stricly the and products are off by and in the track respectively Corrected some misspelling of RCS swir_oob_sending_detector to the Reflective LUTs to enable the SWIR OOB correction detector so that if any of the sending detectors becomes noisy or non near by good detectors from the same sending band can be specified as the substitute in the new look up table Code change for adding an additional dimension of mirror side to the Band_21_b1 LUT to separate the coefficient of the two mirror sides for just like other thermal emissive so that the L1B code can calibrate Band scan to scan with mirror side dependency which leads better calibration result Changes which do not affect scientific when the EV data are not provided in this Crosstalk Correction will not be performed to the Band calibration data Changes which do not affect scientific and BB_500m in L1A Logic was added to turn off the or to spatial aggregation processes and the EV_250m_Aggr1km_RefSB and EV_500m_Aggr1km_RefSB fields were set to fill values when SDSs EV_250m and EV_500m are absent in L1A file Logic was added to skip the processing and turn off the output of the L1B QKM and HKM EV data when EV_250m and EV_500m are absent from L1A In this the new process avoids accessing and reading the and L1A EV skips and writing to the L1B and EV omits reading and subsampling SDSs from geolocation file and writing them to the L1B and omits writing metadata to L1B and EV and skips closing the L1A and L1B EV and SDSs Logic was added to turn off the L1B OBC output when the high resolution OBC SDSs are absent from L1A This is accomplished by skipping the openning the writing of metadata and the closing of the L1B OBC hdf which is Bit in the scan by scan bit QA has been changed Until now
Definition: HISTORY.txt:361
char * unix2isodate(double dtime, char zone)
Definition: unix2isodate.c:10
data_t s[NROOTS]
Definition: decode_rs.h:75
int32_t idy
Definition: atrem_corl1.h:161
int i
Definition: decode_rs.h:71
this program makes no use of any feature of the SDP Toolkit that could generate such a then geolocation is calculated at that and then aggregated up to Resolved feature request Bug by adding three new int8 SDSs for each high resolution offsets between the high resolution geolocation and a bi linear interpolation extrapolation of the positions This can be used to reconstruct the high resolution geolocation Resolved Bug by delaying cumulation of gflags until after validation of derived products Resolved Bug by setting Latitude and Longitude to the correct fill resolving to support Near Real Time because they may be unnecessary if use of entrained ephemeris and attitude data is turned on(as it will be in Near-Real-Time processing).
int get_nut(int32_t iyr, int32_t idy, double xnut[3][3])
How many dimensions is the output array Default is Not sure if anything above will work correctly strcpy(l2prod->title, "no title yet")
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
int32_t iyr
Definition: atrem_corl1.h:161
int k
Definition: decode_rs.h:73
int ephparms(double t, double &xls, double &gs, double &xlm, double &omega)
int npix
Definition: get_cmp.c:27
float p[MODELMAX]
Definition: atrem_corl1.h:131
int nutate(double t, double xls, double gs, double xlm, double omega, double &dpsi, double &eps, double &epsm)