OB.DAAC Logo
NASA Logo
Ocean Color Science Software

ocssw V2022
get_zenaz.c
Go to the documentation of this file.
1 #include <libnav.h>
2 
3 #include <math.h>
4 #include <gsl/gsl_matrix.h>
5 #include <gsl/gsl_blas.h>
6 
7 #define RAD_TO_DEG 57.295779513082321
8 #define DEG_TO_RAD .017453292519943296
9 
10 void euler(float *a,float *xm[]);
11 
12 /*
13  * get_zenaz
14  *
15  * Created on: Aug 23, 2013
16  * Author: dshea
17  *
18  * Calculate the zenith and azimuth for input lat/lon values
19  * given the satellite position.
20  *
21  * pos(3) Orbit Position Vector (km)
22  * lat Pixel geodetic latitudes
23  * lon Pixel geodetic longitudes
24  * zenith Pixel sensor zenith angle
25  * azimuth Pixel sensor azimuth angle
26  *
27  */
28 void get_zenaz(float *pos, float lon, float lat, float *senz, float *sena) {
29  int i;
30  double gv[3];
31  float ea[3];
32  float no[3];
33  float up[3];
34  double rl[3];
35 
36  double re = 6378.137;
37  double f = 1 / 298.257;
38  double omf2 = (1.0 - f) * (1.0 - f);
39  double xlat = lat * DEG_TO_RAD;
40  double xlon = lon * DEG_TO_RAD;
41 
42  // Compute the local vertical, East and North unit vectors
43  up[0] = cos(xlat) * cos(xlon);
44  up[1] = cos(xlat) * sin(xlon);
45  up[2] = sin(xlat);
46  double upxy = sqrt(up[0] * up[0] + up[1] * up[1]);
47  ea[0] = -up[1] / upxy;
48  ea[1] = up[0] / upxy;
49  ea[2] = 0.0;
50  crossp_(up, ea, no);
51 
52  // Compute geocentric position vector
53  double xlatg = atan(tan(xlat) * omf2);
54  gv[0] = cos(xlatg) * cos(xlon);
55  gv[1] = cos(xlatg) * sin(xlon);
56  gv[2] = sin(xlatg);
57  double r = re * (1.0 - f) / sqrt(1.0 - (2.0 - f) * f * pow(cos(xlatg), 2));
58 
59  //Transform the pixel-to-spacecraft and Sun vectors into the local frame
60  gsl_matrix* xlMatrix = gsl_matrix_alloc(3, 3);
61  gsl_matrix* rhMatrix = gsl_matrix_alloc(3, 1);
62  gsl_matrix* rlMatrix = gsl_matrix_alloc(3, 1);
63 
64  for (i = 0; i < 3; i++) {
65  gsl_matrix_set(xlMatrix, 0, i, ea[i]);
66  gsl_matrix_set(xlMatrix, 1, i, no[i]);
67  gsl_matrix_set(xlMatrix, 2, i, up[i]);
68 
69  gsl_matrix_set(rhMatrix, i, 0, pos[i] - r * gv[i]);
70  gsl_matrix_set(rlMatrix, i, 0, 0.0);
71  }
72  gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, xlMatrix, rhMatrix, 0.0,
73  rlMatrix);
74 
75  for (i = 0; i < 3; i++) {
76  rl[i] = gsl_matrix_get(rlMatrix, i, 0);
77  }
78 
79  gsl_matrix_free(xlMatrix);
80  gsl_matrix_free(rhMatrix);
81  gsl_matrix_free(rlMatrix);
82 
83  // Compute the sensor zenith and azimuth
84  *senz = RAD_TO_DEG * atan2(sqrt(rl[0] * rl[0] + rl[1] * rl[1]), rl[2]);
85 
86  // Check for zenith close to zero
87  if (*senz > 0.05)
88  *sena = RAD_TO_DEG * atan2(rl[0], rl[1]);
89  else
90  *sena = 0.0;
91 
92  if (*sena < 0.0)
93  *sena += 360.0;
94 
95 }
96 
97 void transpose(float *in[], float *out[]) {
98  int i,j;
99 
100  for (i=0;i<3;i++)
101  for (j=0;j<3;j++)
102  out[j][i] = in[i][j];
103 }
104 
105 void nav_get_vel(float ilat, float mlat, float ilon, float mlon, float *vel) {
106 
107  /*
108  * Determine the velocity vectors
109  *
110  * ilat, ilon (in) - latitude and longitude intercept of the lsq fitted lat lon positions
111  * mlat, mlon (in) - latitude and longitude slope " " " " " " "
112  *
113  * float vel[3] - (out) velocity vector
114  *
115  * R. Healy 11/2/2016
116  */
117  float heading, chead, shead;
118  float clat,slat,clon,slon;
119 
120  heading = atan2(mlon*cos(ilat*DEG_TO_RAD),mlat);
121  chead = cos(heading);
122  shead = sin(heading);
123  clat = cos(ilat*DEG_TO_RAD);
124  clon = cos(ilon*DEG_TO_RAD);
125  slat = sin(ilat*DEG_TO_RAD);
126  slon = sin(ilon*DEG_TO_RAD);
127  // V is computed from a 3-2-1 Euler rotation
128  // lon is phi, lat is -theta, head is -psi
129  vel[0] = -clon*slat*chead - slon*shead;
130  vel[1] = -slon*slat*chead + clon*shead;
131  vel[2] = clat*chead;
132 
133 }
134 void nav_get_pos(float lat, float lon, float alt, float *p) {
135 
136  /*
137  * Determine the position vectors
138  *
139  * lat, lon (in)- latitude and longitude of the pixel
140  * latr, lonr (in) - latitude and longitude of position at time > time of lat, lon
141  *
142  * float pos[3] - (out) position and velocity vectors
143  *
144  * R. Healy 11/2/2016
145  */
146  double re = 6378.137;
147  double f = 1 / 298.257;
148  double omf2 = (1.0 - f) * (1.0 - f);
149  double glat,cglat,rl;
150  double olon=lon*DEG_TO_RAD,olat=lat*DEG_TO_RAD;
151 
152  glat = atan(tan(olat)*omf2);
153  cglat = cos(glat);
154  rl = re*(1.-f)/sqrt(1.-(2.-f)*f*cglat*cglat);
155  p[0] = cos(olon)*(rl*cglat + alt*cos(olat));
156  p[1] = sin(olon)*(rl*cglat + alt*cos(olat));
157  p[2] = rl*sin(glat) + alt*sin(olat);
158 
159 }
160 void nav_gd_orient(float *pos, float *vel, float *att, float *smat[], float *coef) {
161  /*
162  This subroutine performs a simple calculation of the sensor
163  orientation from the orbit position vector and input values of the
164  attitude offset angles. The calculations assume that the angles
165  represent the roll, pitch and yaw offsets between the orbit
166  reference frame (at the spacecraft position) and the sensor frame.
167  Sensor tilt angles are assumed to be included in the pitch angle.
168  The outputs are the matrix which represents the transformation from
169  the geocentric rotating to sensor frame, and the coefficients which
170  represent the Earth scan track in the sensor frame.
171 
172  The reference ellipsoid uses an equatorial radius of 6378.137 km and
173  a flattening factor of 1/298.257 (WGS 1984).
174 
175  Calling Arguments
176 
177  Name Type I/O Description
178 
179  pos(3) R*4 I ECR Orbit Position Vector (km)
180  vel(3) R*4 I ECR Orbit Velocity Vector (km/sec)
181  att(3) R*4 I Attitude angles roll, pitch, yaw
182  smat(3,3) R*4 O Sensor Orientation Matrix
183  coef(10) R*4 O Scan path coefficients
184 
185  Translated from IDL function
186  R. Healy 11/2/2016
187 
188 
189  */
190  double re = 6378.137,rem = 6371.0;
191  double f = 1 / 298.257;
192  double omf2 = (1.0 - f) * (1.0 - f);
193  double omegae = 7.29211585494e-5;
194  double rd = 1.0/omf2;
195 
196  double velm[3],pm,omf2p,pxy,temp,x[3],y[3],z[3];
197  double on[3],onm;
198  float *sm1[3],sm2[3][3];
199  int i,j;
200 
201  // Determine local vertical reference axes
202  velm[0] = vel[0] - pos[1]*omegae;
203  velm[1] = vel[1] + pos[0]*omegae;
204  velm[2] = vel[2];
205 
206  for (i=0;i<3;i++)
207  sm1[i] = (float *)malloc(3*sizeof(float));
208 
209  //Compute Z axis as local nadir vector
210  pm = sqrt(pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2]);
211  omf2p = (omf2*rem + pm - rem)/pm;
212  pxy = pos[0]*pos[0]+pos[1]*pos[1];
213  temp = sqrt(pos[2]*pos[2] + omf2p*omf2p*pxy);
214  z[0] = -omf2p*pos[0]/temp;
215  z[1] = -omf2p*pos[1]/temp;
216  z[2] = -pos[2]/temp;
217 
218  //Compute Y axis along negative orbit normal
219  // Do the cross product
220 
221  on[0] = velm[1]*z[2]-velm[2]*z[1];
222  on[1] = velm[2]*z[0]-velm[0]*z[2];
223  on[2] = velm[0]*z[1]-velm[1]*z[0];
224 
225  onm=sqrt(on[0]*on[0]+on[1]*on[1]+on[2]*on[2]);
226 
227  for (i=0;i<3;i++) y[i] = -on[i]/onm;
228 
229  // Compute X axis to complete orthonormal triad (velocity direction)
230 
231  x[0] = y[1]*z[2]-y[2]*z[1];
232  x[1] = y[2]*z[0]-y[0]*z[2];
233  x[2] = y[0]*z[1]-y[1]*z[0];
234 
235  euler(att,sm1);
236 
237  // Compute attitude matrix in geocentric frame
238  // Store local vertical reference vectors in matrix
239  for (i=0;i<3;i++) {
240  sm2[0][i]=x[i];
241  sm2[1][i]=y[i];
242  sm2[2][i]=z[i];
243  }
244 
245  for (i=0;i<3;i++)
246  for (j=0;j<3;j++)
247  smat[i][j]=sm1[i][0]*sm2[0][j]+sm1[i][1]*sm2[1][j]+sm1[i][2]*sm2[2][j];
248 
249  // Compute coefficients of intersection ellipse in scan plane
250  coef[0] = 1.0+(rd-1.0)*smat[0][2]*smat[0][2];
251  coef[1] = 1.0+(rd-1.0)*smat[1][2]*smat[1][2];
252  coef[2] = 1.0+(rd-1.0)*smat[2][2]*smat[2][2];
253  coef[3] = (rd-1.0)*smat[0][2]*smat[1][2]*2.0;
254  coef[4] = (rd-1.0)*smat[0][2]*smat[2][2]*2.0;
255  coef[5] = (rd-1.0)*smat[1][2]*smat[2][2]*2.0;
256  coef[6] = (smat[0][0]*pos[0]+smat[0][1]*pos[1]+smat[0][2]*pos[2]*rd)*2.0;
257  coef[7] = (smat[1][0]*pos[0]+smat[1][1]*pos[1]+smat[1][2]*pos[2]*rd)*2.0;
258  coef[8] = (smat[2][0]*pos[0]+smat[2][1]*pos[1]+smat[2][2]*pos[2]*rd)*2.0;
259  coef[9] = pos[0]*pos[0]+pos[1]*pos[1]+pos[2]*pos[2]*rd-re*re;
260 
261 }
262 
263 void euler(float *a,float *xm[]) {
264  // Computes coordinate transformation matrix corresponding to Euler
265  // sequence; assumes order of rotations is X, Y, Z
266  //
267  // Reference: Wertz, Appendix E
268  // Translated from IDL function
269  // R. Healy 11/2/2016
270  //
271 
272  double xm1[3][3],xm2[3][3],xm3[3][3],xmm[3][3];
273  double c1,c2,c3,s1,s2,s3;
274  int i,j;
275  c1=cos(a[0]*DEG_TO_RAD);
276  s1=sin(a[0]*DEG_TO_RAD);
277  c2=cos(a[1]*DEG_TO_RAD);
278  s2=sin(a[1]*DEG_TO_RAD);
279  c3=cos(a[2]*DEG_TO_RAD);
280  s3=sin(a[2]*DEG_TO_RAD);
281  // Convert individual rotations to matrices
282  xm1[0][0]=1.0;
283  xm1[1][1]= c1;
284  xm1[2][2]= c1;
285  xm1[1][2]= s1;
286  xm1[2][1]=-s1;
287  xm2[1][1]= 1.0;
288  xm2[0][0]= c2;
289  xm2[2][2]= c2;
290  xm2[2][0]= s2;
291  xm2[0][2]=-s2;
292  xm3[2][2]= 1.0;
293  xm3[1][1]= c3;
294  xm3[0][0]= c3;
295  xm3[0][1]= s3;
296  xm3[1][0]=-s3;
297 
298  // Compute total rotation as xm3*xm2*xm1
299  for (i=0;i<3;i++)
300  for (j=0;j<3;j++)
301  xmm[i][j]=xm2[i][0]*xm1[0][j]+xm2[i][1]*xm1[1][j]+xm2[i][2]*xm1[2][j];
302 
303  for (i=0;i<3;i++)
304  for (j=0;j<3;j++)
305  xm[i][j]=xm3[i][0]*xmm[0][j]+xm3[i][1]*xmm[1][j]+xm3[i][2]*xmm[2][j];
306 
307  return;
308 
309 }
310 void nav_get_geonav(float *sunr, float *pos, float *pview, float *coef, float *smat[], float *xlon, float *xlat, float *solz, float *sola, float *senz, float *sena) {
311 // ; This subroutine performs navigation of a scanning sensor on the
312 // ; surface of an ellipsoid based on an input orbit position vector and
313 // ; spacecraft orientation matrix. It uses a closed-form algorithm for
314 // ; determining points on the ellipsoidal surface which involves
315 // ; determining the intersection of the scan plan with the ellipsoid.
316 // ; The sensor view vectors in the sensor frame are passed in as a 3xN array.
317 // ;
318 // ; The reference ellipsoid is set according to the scan
319 // ; intersection coefficients in the calling sequence; an equatorial
320 // ; radius of 6378.137 km. and a flattening factor of 1/298.257 are
321 // ; used by both the Geodetic Reference System (GRS) 1980 and the
322 // ; World Geodetic System (WGS) 1984.
323 // ;
324 // ; It then computes geometric parameters using the pixel locations on
325 // ; the Earth, the spaecraft position vector. The outputs are arrays of
326 // ; geodetic latitude and longitude, sensor
327 // ; zenith and azimuth. The azimuth angles are measured from local
328 // ; North toward East. Flag values of 999. are returned for any pixels
329 // ; whose scan angle is past the Earth's horizon.
330 // ;
331 // ; Reference: "Exact closed-form geolocation algorithm for
332 // ; Earth survey sensors", F. S. Patt and W. W. Gregg, IJRS, Vol. 15
333 // ; No. 18, 1994.
334 //
335 // Translated from IDL function uni_geonav - solar angles removed
336 // R. Healy 11/2/2016
337 //
338  int i;
339  float ea[3];
340  float no[3];
341  double up[3];
342  double rh[3];
343  double rl[3];
344  double sl[3];
345 
346  double f = 1 / 298.257;
347  double omf2 = (1.0 - f) * (1.0 - f);
348  double temp,d,o,p,q,r,uxy,x1[3],vv[3],geovec[3];
349  float *smatt[3];
350  for (i=0;i<3;i++)
351  smatt[i] = (float *) malloc(3*sizeof(float));
352 
353  // Move scan view vectors to local array
354  vv[0] = pview[0];
355  vv[1] = pview[1];
356  vv[2] = pview[2];
357 
358  //Compute sensor-to-surface vectors for all scan angles
359  // Compute terms for quadratic equation
360  o = coef[0]*vv[0]*vv[0] + coef[1]*vv[1]*vv[1]
361  + coef[2]*vv[2]*vv[2] + coef[3]*vv[0]*vv[1]
362  + coef[4]*vv[0]*vv[2] + coef[5]*vv[1]*vv[2];
363 
364  p = coef[6]*vv[0] + coef[7]*vv[1] + coef[8]*vv[2];
365  q = coef[9];
366  r = p*p-4.0*q*o;
367 
368  // Solve for magnitude of sensor-to-pixel vector and compute components
369 
370  d = (-p-sqrt(r))/(2.0*o);
371  x1[0]=d*vv[0];
372  x1[1]=d*vv[1];
373  x1[2]=d*vv[2];
374  // Transform vector from sensor to geocentric frame
375 
376  transpose(smat, smatt);
377  for (i=0;i<3;i++)
378  rh[i]=smatt[i][0]*x1[0]+smatt[i][1]*x1[1]+smatt[i][2]*x1[2];
379  for (i=0;i<3;i++)
380  geovec[i] = pos[i] + rh[i];
381  // Compute the local vertical, East and North unit vectors
382  uxy = geovec[0]*geovec[0]+geovec[1]*geovec[1];
383  temp = sqrt(geovec[2]*geovec[2] + omf2*omf2*uxy);
384 
385  up[0] = omf2*geovec[0]/temp;
386  up[1] = omf2*geovec[1]/temp;
387  up[2] = geovec[2]/temp;
388  double upxy = sqrt(up[0] * up[0] + up[1] * up[1]);
389  ea[0] = -up[1] / upxy;
390  ea[1] = up[0] / upxy;
391  ea[2] = 0.0;
392  no[0] = -up[2]*ea[1];
393  no[1] = up[2]*ea[0];
394  no[2] = up[0]*ea[1] - up[1]*ea[0];
395 
396  // Compute geodetic latitude and longitude
397  *xlat = asin(up[2])*RAD_TO_DEG;
398  *xlon = atan2(up[1],up[0])*RAD_TO_DEG;
399  //range = d
400 
401  // Transform the pixel-to-spacecraft and Sun vectors into the local
402  // frame
403  rl[0] = -ea[0]*rh[0] - ea[1]*rh[1] - ea[2]*rh[2];
404  rl[1] = -no[0]*rh[0] - no[1]*rh[1] - no[2]*rh[2];
405  rl[2] = -up[0]*rh[0] - up[1]*rh[1] - up[2]*rh[2];
406 
407  sl[0] = ea[0]*sunr[0] + ea[1]*sunr[1] + ea[2]*sunr[2];
408  sl[1] = no[0]*sunr[0] + no[1]*sunr[1] + no[2]*sunr[2];
409  sl[2] = up[0]*sunr[0] + up[1]*sunr[1] + up[2]*sunr[2];
410 
411  *solz = RAD_TO_DEG * atan2(sqrt(sl[0] * sl[0] + sl[1] * sl[1]), sl[2]);
412  *sola = RAD_TO_DEG * atan2(sl[0], sl[1]);
413 
414  // Compute the sensor zenith and azimuth
415  *senz = RAD_TO_DEG * atan2(sqrt(rl[0] * rl[0] + rl[1] * rl[1]), rl[2]);
416 
417  // Check for zenith close to zero
418  if (*senz > 0.02)
419  *sena = RAD_TO_DEG * atan2(rl[0], rl[1]);
420  else
421  *sena = 0.0;
422 
423 
424 }
data_t q
Definition: decode_rs.h:74
int r
Definition: decode_rs.h:73
int crossp_(float *v1, float *v2, float *v3)
int j
Definition: decode_rs.h:73
#define RAD_TO_DEG
Definition: get_zenaz.c:7
void nav_gd_orient(float *pos, float *vel, float *att, float *smat[], float *coef)
Definition: get_zenaz.c:160
void nav_get_geonav(float *sunr, float *pos, float *pview, float *coef, float *smat[], float *xlon, float *xlat, float *solz, float *sola, float *senz, float *sena)
Definition: get_zenaz.c:310
no change in intended resolving MODur00064 Corrected handling of bad ephemeris attitude resolving resolving GSFcd00179 Corrected handling of fill values for[Sensor|Solar][Zenith|Azimuth] resolving MODxl01751 Changed to validate LUT version against a value retrieved from the resolving MODxl02056 Changed to calculate Solar Diffuser angles without adjustment for estimated post launch changes in the MODIS orientation relative to incidentally resolving defects MODxl01766 Also resolves MODxl01947 Changed to ignore fill values in SCI_ABNORM and SCI_STATE rather than treating them as resolving MODxl01780 Changed to use spacecraft ancillary data to recognise when the mirror encoder data is being set by side A or side B and to change calculations accordingly This removes the need for seperate LUTs for Side A and Side B data it makes the new LUTs incompatible with older versions of the and vice versa Also resolves MODxl01685 A more robust GRing algorithm is being which will create a non default GRing anytime there s even a single geolocated pixel in a granule Removed obsolete messages from seed as required for compatibility with version of the SDP toolkit Corrected test output file names to end in out
Definition: HISTORY.txt:422
float32 * pos
Definition: l1_czcs_hdf.c:35
float * lat
void euler(float *a, float *xm[])
Definition: get_zenaz.c:263
double precision function f(R1)
Definition: tmd.lp.f:1454
#define DEG_TO_RAD
Definition: get_zenaz.c:8
float rd(float x, float y, float z)
#define re
Definition: l1_czcs_hdf.c:701
void get_zenaz(float *pos, float lon, float lat, float *senz, float *sena)
Definition: get_zenaz.c:28
float xlon[LAC_PIXEL_NUM]
Definition: l1a_seawifs.c:93
float pm[MODELMAX]
#define omf2
Definition: l1_czcs_hdf.c:703
float * lon
void nav_get_pos(float lat, float lon, float alt, float *p)
Definition: get_zenaz.c:134
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).
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
void nav_get_vel(float ilat, float mlat, float ilon, float mlon, float *vel)
Definition: get_zenaz.c:105
float p[MODELMAX]
Definition: atrem_corl1.h:131
void transpose(float *in[], float *out[])
Definition: get_zenaz.c:97