/*********************************************************************

   ROUTINE: sat_tran.c
   PROGRAMMER: Dan Vietor, D. Farmer, Eric A. Smith & D. R. Phillips
   PROGRAM TYPE: WXP_LIBRARY
   VERSION: 1.0
   WXP VERSION: 4.8
   DATE: 920515

   DESCRIPTION: General routines to convert satellite navigation to 
      earth coordinates.

   modifications by D. Farmer
   Unidata Program Center in Boulder, CO

   Adapted C code from original Fortran code written by
   Eric A. Smith & D. R. Phillips
   Dept. of Atmospheric Science
   CSU/Foothills Campus
   Fort Collins, CO 80523

   COMPUTER:
      IBM RS/6000 running AIX 3.1 C Compiler
      IBM RT/PC running AIX 2.2.1 C Compiler
      IBM RT/PC running BSD 4.3 C Compiler
      IBM AT compatible running MSDOS 3.x using the
         Microsoft C 6.0 Compiler.
      SUN 3 and 4 running SUNOS 4.1
      DEC VaxStation running VMS 5.3 and GKS
      Interprocess communications based on either ATT UNIX System V
         Message queues or BSD Sockets.
      Asynchronous data stream function interface based on either
         System V ioctl, BSD 4.3 ioctl or Blaise Asynch Manager
         for MSDOS 3.x.
      Graphics calls based on ANSI C language Graphical Kernel
         System binding (revision dated July 1988)

   REVISIONS:                                    DATE:       INIT:
Version 1                                        920515      DEV

*********************************************************************/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "mc_area.h"
#include "wxp.h"

#define GOES 1
#define GVAR 2
#define PSTEREO 10

struct pt {
   double x,y,z;
   };

struct sp_pt {
   double lat,lon,hgt;
   };

struct angle {
   double ang;
   double sin,cos;
   };

struct datim {
   long date;        /* Date YYDDD */
   double tim;       /* Time HH.HHH */
   };
/*
********** GOES Parameters *************
*/
#define EL_LL 1
#define LL_EL 2
#define RADE_EQUA    6378.388
#define RADE_POLE    6356.912
#define RADE_MEAN    6371.221
#define SOLAR_YR     365.24219879
#define SIDER_YR     366.24219879
#define SOLSID       (SIDER_YR/SOLAR_YR)
#define REF_DATE     90001L
#define REF_TIME     171537L
#define PREC_VER_EQ  25781.
#define OBLIQ_ECLIP  23.45
#define NOMORB 42164.365      /* Nominal distance of datellite (km) */
#define REQ     6378.137
#define RPL     6356.7533
#define REQRPL2 ((REQ*REQ)/(RPL*RPL))
#define FER     (RPL/REQ)
#define FER2    (1-RPL/REQ)
#define REQRPL3 (REQRPL2-1)
#define REQRPL4 (FER*FER*FER*FER-1)

int image_type = 0;

struct goes_orbit {
   int sat_type;    /* Satellite type - iosat
                       set positive for initializing new satellite type
                       set negative for retaining old satellite type with new 
                          orbit parms, type is then set positive */
   int anom_type;   /* Anomaly type - imort
                       0 for orbit anomaly given as mean anomaly (e.g. NASA)
                       1 for orbit anomaly given as true anomaly (e.g. ESA) */
   int sec_ord;     /* Secular theory order - iosec
                       0 for zero order secular perturbation theory
                       1 for first order secular perturbation theory
                       2 for second order secular perturbation theory */
   long epoch_date; /* Epoch date - iedate (yymmdd in calendar form)
                       date for which following orbital parameters are valid */
   long epoch_time; /* Epoch time - ietime (hhmmss in GMT)
                       date for which following orbital parameters are valid */
   double semi_maj; /* Semi-major axis (km) - semima
                       half the distance between two apses of apo-focus and 
                       peri-focus */
   double eccent;   /* Eccentricity of earth orbit (unitless) - oeccen
                       degree of ellipticity of orbit */
   double inclin;   /* Orbit inclination (degrees) - orbinc
                       angle between the orbit and equatorial planes */
   double anomaly;  /* Orbit anomaly at epoch time (degrees) - oanoml
                       angle in orbital plane between peri-focus and 
                       satellite position given as either a mean anomaly 
                       or a true anomaly */
   double perigee;  /* Argument of perigee at epoch time (degrees) - perige
                       angle in orbit plane from ascending node to peri-focus */
   double asc_node; /* Right ascension of ascending node at epoch 
                       time (degrees) - asnode
                       angle in equatorial plane between vernal equinox 
                       (principle axis) and northward equator crossing */
   double period;   /* Period (minutes) - period
                       statement of Kepler's Third Law
                       this parameter calculated in satpos */
   double anom_per; /* Anomalistic period (minutes) - aperod
                       time between the passage from one peri-focus to the next
                       this parameter calculated in satpos */
   double nodal_per;/* Nodal period (minutes) - eperod
                       time between the passage from one equator passing 
                       to the next this parameter calculated in eqcros */
   } g_orb;

struct goes_navigate {
   long num_lines;  /* Total number of scan lines per frame and 
                       sensors (lines) - lintot */
   double deg_line; /* Angle of north-south frame scan (degrees) - deglin */
   long num_elem;   /* Total number of elements per sscan line 
                       (elements) - num_elem */
   double deg_elem; /* Angle of east-west frame scan (degrees) - degele */
   double spin_rate;/* Spin rate (milliseconds/line) - spinra */
   double declin;   /* Spin axis declination (degrees) - declin */
   double right_asc;/* Spin axis right ascension in celestial coor 
                       system (degrees) - rascen */
   long center_line;/* Picture center line (lines) - piclin */
   double prec_rate;/* Precession rate (degrees/day) - prerat */
   double prec_dir; /* Precession direction (degrees) - predir */
   double pitch;    /* North-south misalignment in principle axis of 
                       camera (degrees) - pitch */
   double yaw;      /* Skew misalignment in principle axis of 
                       camera (degrees) - yaw */
   double roll;     /* East-west misalignment in principle axis of 
                       camera (degrees) - roll */
   } g_nav;

static struct datim sat_time;
static double gamma_init;  /* Initial gamma value */
static double gamma_dot;   /* Derivative of gamma */
static double atms_hght;   /* Atmospheric height */
static double earth_rot;   /* Earth rotation speed */
static double prec_veq;
static struct datim ref;
static struct mc_area *sat_image;
static struct pt sat;
static struct sp_pt sp_sat;
static float lres,eres;
static float lcor,ecor;
/*
************ GVAR Parameters ************
*/
struct gvar_navigate {
  char sttype[TYPELEN];     /*   1     STTYPE = Satellite type */
  long idntfr;     /*   2     IDNTFR = */
  long imcact;     /*   3     IMCACT = IMC active flag */
  float reflon;    /*   6    +REFLON = Reference longitude */
  float refdis;    /*   7    +REFDIS = Reference distance from nominal */
  float reflat;    /*   8    +REFLAT = Reference latitude */
  float refyaw;    /*   9    +REFYAW = Reference yaw */
  float ratrol;    /*  10    +RATROL = Reference attitude roll */
  float ratptc;    /*  11    +RATPTC = Reference attitude pitch */
  float ratyaw;    /*  12    +RATYAW = Reference attitude yaw */
  double epoch;    /*  13-14  ETIME  = Epoch time */
  float edtime;    /*  15    +EDTIME = Delta from epoch time */
  float imcrol;    /*  16    +IMCROL = Image motion compensation roll */
  float imcptc;    /*  17    +IMCPTC = Image motion compensation pitch */
  float imcyaw;    /*  18    +IMCYAW = Image motion compensation yaw */
  float ldr[13];   /*  19-31 +LDR    = Longitude delta from ref parameters */
  float rddr[11];  /*  32-42 +RDDR   = Radial distance delta from ref params */
  float dgl[9];    /*  43-51 +DGL    = Geocentric latitude delta parameters */
  float doy[9];    /*  52-60 +DOY    = Orbit yaw delta parameters */
  float solinc;    /*  62    +EXPTIM = Exponential start time from epoch */
  float exptim;    /*  62    +EXPTIM = Exponential start time from epoch */
  struct ang_wds {
     float expn[3];    /*   1- 3 */
     long nsinus;      /*   4    */
     float sinus[30];  /*   5-34 */
     long nmsin;       /*  35    */
     struct mono_sin {
        long ord;      /*  36    */
        long mord;     /*  37    */
        float sinus[3];/*  38-40 */
        } msin[4];
     } raawds,     /*  63-129 RAAWDS = Roll attitude angle words */
       paawds,     /* 130-184 PAAWDS = Pitch attitude angle words */
       yaawds,     /* 185-257 YAAWDS = Yaw attitude angle words */
       rmawds,     /* 258-312 RMAWDS = Roll misalignment angle words */
       pmawds;     /* 313-367 PMAWDS = Pitch misalignment angle words */
  double imgtim;   /* 368     IMGDAY = Image day value (YYDDD) */
                   /* 369     IMGTM  = Image time value (HHMMSS) */
  long imgsnd;     /* 370     IMGSND = Imager/sounder instrument flag */
  } gv_nav;

/*  ELCOMM include variables */
double bt[3][3];             /* Instrument to ECEF coordinates transformation */
double q3;                   /* Used in function lpoint */
double pitch,roll,yaw;       /* Pitch,roll,yaw angles of instrument (rad) */
float pma,rma;               /* Pitch,roll misalignments of instrument (rad) */

/*  INSTCO include variables */
long inc_max[2];             /* Number of increments per cycle */
float elev_bnds[2];          /* Bounds in elevation (radians) */
float scan_bnds[2];          /* Bounds in scan angle (radians) */
float elev_inc[2];           /* Change in elevation angle per increment (rad) */
float scan_inc[2];           /* Change in scan angle per increment (radians) */
float elev_dln[2];           /* Elevation angle per detector line (radians) */
float scan_pix[2];           /* Scan angle per pixel (radians) */

/*  GVRCOM common variables */
int itype;
int instr;
double sublat;
double sublon;

/*  SAVCOM common variables */
struct pt xs;               /* Normalized S/C position in ECEF coordinates */
double b[3][3];             /* Spacecraft to ECEF coordinates transformation */
double dr;
struct angle phi;
struct angle psi;

/*********************************************************
   NUMYR - Number of days in a year
*********************************************************/
long numyr( year )

long year;

{
if( year < 50 )
   year += 2000;
else
   year += 1900;
if(( year % 4 == 0 && year % 100 != 0 ) || year % 400 == 0 )
   return 366;
else
   return 365;
}

/*********************************************************
   NUMDY - Number of days between 2 dates
*********************************************************/
long numdy(date1,date2)

long date1,date2;

{
long year1,day1,year2,day2,temp,days;
   
year1 = date1/1000;
day1 = date1 % 1000;
year2 = date2/1000;
day2 = date2 % 1000;
temp = 1;
/*
   Switch values if date1 > date2
*/
if(( year1 > year2 ) ||
   ( year1 == year2 && day1 > day2)){
   temp = year2;
   year2 = year1;
   year1 = temp;
   temp = day2;
   day2 = day1;
   day1 = temp;
   temp = -1;
   }
for( days = 0; year1 < year2; year1++ ){
   days += numyr(year1) - day1 + 1;
   day1 = 1;
   }
days += day2 - day1;
days *= temp;
return days;
}

/*********************************************************
   TIMDIF - Number of minutes between 2 dates
*********************************************************/
double timdif( date1,time1,date2,time2 )

long date1,date2;
double time1,time2;

{
return 1440.0*numdy(date1,date2) + 60.0*(time2 - time1);
}

/*********************************************************
   GEOLAT - Geoedtic-geocentric latitude conersion
*********************************************************/
double geolat( type,lat )

int type;
double lat;

{
double fac;
   
fac = (RADE_EQUA - RADE_POLE) / RADE_EQUA;
fac = (1.0 - fac)*(1.0 - fac);
lat = DRC * lat;
/*
   Geodetic to geocentric conversion 
*/
if( type == 1 )
   return atan(tan(lat)*fac)*RDC;
/*
   Geocentric to geodetic conversion 
*/
else
   return atan(tan(lat)/fac)*RDC;
}

/*********************************************************
   MDCON - Conversion between YYMMDD to YYDDD for
   Julian date conversions
*********************************************************/
long mdcon( type,date )

int type;
long date;

{
long year,month,day,leap,julian,tyear;
static int num[]   = { 0,31,59,90,120,151,181,212,243,273,304,334,365 };
static int num_l[] = { 0,31,60,91,121,152,182,213,244,274,305,335,366 };
/*
   YYMMDD to YYDDD conversion
*/
if( type == 1 ){
   year = date/10000;
   date %= 10000;
   month = date/100;
   date %= 100;
   day = date;
   if( month < 1 )
      month = 1;
   if( month > 12 )
      month = 12;
   if( year < 50 )
      tyear = 2000 + year;
   else
      tyear = 1900 + year;
   if(( tyear % 4 == 0 && tyear % 100 ) || tyear % 400 == 0 )
      julian = year * 1000 + num_l[month-1];
   else
      julian = year * 1000 + num[month-1];
   julian += day;
   return julian;
   }
/*
   YYDDD to YYMMDD conversion
*/
else { 
   year = date/1000;
   julian = date % 1000;
   if( year < 50 )
      tyear = 2000 + year;
   else
      tyear = 1900 + year;
   if(( tyear % 4 == 0 && tyear % 100 ) || tyear % 400 == 0 )
      leap = 1;
   else
      leap = 0;
   if( julian < 1 )
      julian = 1;
   if( leap && julian > 366 )
      julian = 366;
   else if( !leap && julian > 365 )
      julian = 365;
   if( leap ){
      for( month = 0; month < 12 && num_l[month] < julian; month++ );
      day = julian - num_l[month-1];
      }
   else {
      for( month = 0; month < 12 && num[month] < julian; month++ );
      day = julian - num[month-1];
      }
   return year*10000 + month*100 + day;
   }
}

/*********************************************************
   INT2DEG - Converts HH.MMSS to decimal degrees
*********************************************************/
double int2deg( ang )

long ang;

{
double deg;
int sign;

if( ang < 0 ){
   ang = -ang;
   sign = -1;
   }
else
   sign = 1;

deg = (double)(ang % 100) / 3600.;
ang /= 100;
deg += (double)(ang % 100) / 60.;
ang /= 100;
deg += ang;
return sign*deg;
}

/*********************************************************
   DEG2INT - Converts decimal degrees to HH.MMSS
*********************************************************/
long deg2int( ang )

double ang;

{
long lang, deg;
int sign;

if( ang < 0 ){
   ang = -ang;
   sign = -1;
   }
else
   sign = 1;

ang += 1.38888E-4;

lang = (long)(ang*3600);
deg = (long)ang;
lang -= deg*3600;
deg = deg * 100 + (lang/60);
lang -= (lang/60)*60;
deg = deg * 100 + lang;
return sign*deg;
}

/*********************************************************
   ROUND - Rounds to the nearest whole number
*********************************************************/
long round_long( x )

double x;

{
if( x < 0 )
   return (long)( x - 0.5 );
else if( x == 0 )
   return 0L;
else
   return (long)( x + 0.5 );
}

/************************************************************
   elev_scan2line_pix - Converts GVAR elevation and scan line
   information to line and element.
************************************************************/
void elev_scan2line_pix(elev,scan,line,elem)

float elev;       /* Elevation angle in radians */
float scan;       /* Scan angle in radians */
float *line;      /* Line number (OUTPUT) */
float *elem;      /* Element number (OUTPUT) */
{
/*
  Compute fractional line number
*/
*line = (elev_bnds[instr] - elev) / elev_dln[instr];
if( instr == 0 )
  *line = *line + 4.5;
else
  *line = *line + 2.5;
/*
  Compute fractional element number
*/
*elem = (scan_bnds[instr] + scan) / scan_pix[instr] + 1;
}

/*********************************************************
   sat_line_elem - Converts image line,element to satellite
   line,element.
*********************************************************/
void sat_line_elem( type, ilin,iele, olin,oele )

int type;
float ilin,iele, *olin,*oele;

{
if( type == 1 ){
   *oele = ecor + iele*eres;
   *olin = lcor + ilin*lres;
   }
else {
   *oele = (iele - ecor)/eres;
   *olin = (ilin - lcor)/lres;
   }
}

/*********************************************************
   SET_SAT_RES - Sets up the satellite resolution for
   line element conversion.
*********************************************************/
void set_sat_res( ilcor,iecor,ilres,ieres )

float ilcor,iecor,ilres,ieres;

{
lres = ilres;
eres = ieres;
lcor = ilcor;
ecor = iecor;
}
/*********************************************************
   SPHERE_CVT - Converts from cartesian to spherical 
   coordinates.
*********************************************************/
void sphere_cvt( type,pnt,sph_pnt )

int type;
struct pt *pnt;
struct sp_pt *sph_pnt;

{
double temp;
/*
   Convert to spherical coord's
*/
if( type == 1 ){
   temp = pnt->x * pnt->x + pnt->y * pnt->y;
   sph_pnt->lat = atan2(pnt->z, sqrt(temp)) * RDC;
   sph_pnt->lon = atan2(pnt->y, pnt->x) * RDC;
   sph_pnt->hgt = sqrt(temp + pnt->z * pnt->z);
   }
}

/************************************************************
   gatt_init - Initializes attitude and misalignment attributes
************************************************************/
void gatt_init( words, data )

struct ang_wds words;
long *data;

{
int i,j,k;

words.expn[0] = data[0]/10000000.;
words.expn[1] = data[1]/100.;
words.expn[2] = data[2]/10000000.;

words.nsinus = data[3];
for( i = 0; i < 30; i++ )
   words.sinus[i] = data[i+4]/10000000.;

words.nmsin = data[34];
for( i = 0, k = 35; i < 4; i++ ){
   words.msin[i].ord = data[k++];
   words.msin[i].mord = data[k++];
   for( j = 0; j < 3; j++ )
      words.msin[i].sinus[j] = data[k++]/10000000.;
   }
}

/************************************************************
   gatt - Calculates attitude and misalignment attributes
************************************************************/
double gatt(parms,wa,te)

struct ang_wds parms;
double wa;            /* Input solar orbit angle in radians */
double te;            /* Input exponential time delay from epoch (minutes) */

{
int i;
double att;

att = parms.expn[2];
/*
  Computes the exponential term
*/
if( te >= 0 ) 
   att += parms.expn[0] * exp(-te / parms.expn[1]);
/*
  Calculation of sinusoids
*/
for( i = 0; i < parms.nsinus; i++ )
   att += parms.sinus[2*i] * cos(wa*i+parms.sinus[2*i+1]);
/*
  Computes monomial sinusoids
*/
for( i = 0; i < parms.nmsin; i++ ){
   att += parms.msin[i].sinus[0] * 
          pow((wa - parms.msin[i].sinus[2]),(double)parms.msin[i].mord) * 
          cos((double)parms.msin[i].ord*wa+parms.msin[i].sinus[1]);
   }
return att;
}

/************************************************************
   inst2e - Calculates instrument to earth coordinate transformation
   matrix
************************************************************/
inst2e(roll,pitch,yaw,a,at)

double roll;      /* Roll angle in radians */
double pitch;     /* Pitch angle in radians */
double yaw;       /* Yaw angle in radians */
double a[3][3];   /* Spacecraft to ECEF coordinates transformation matrix */
double at[3][3];  /* Instrument to ECEF coordinates transformation matrix */

{
double rpy[3][3];
int i,j;
/*
  We compute instrument to body coordinates transformation
  matrix by using a small angle approximation of trigonometric
  functions of the roll, pitch and yaw.
*/
rpy[0][0] = 1 - .5 * (pitch*pitch + yaw*yaw);
rpy[0][1] = -yaw;
rpy[0][2] = pitch;
rpy[1][0] = yaw + pitch * roll;
rpy[1][1] = 1. - 0.5 * (yaw * yaw + roll * roll);
rpy[1][2] = -roll;
rpy[2][0] = -pitch + roll * yaw;
rpy[2][1] = roll + pitch * yaw;
rpy[2][2] = 1. - 0.5 * (pitch * pitch + roll * roll);
/*
  Multiplication of matrices a and rpy
*/
for( i = 0; i < 3; i++ )
   for( j = 0; j < 3; j++ )
       at[i][j] = a[i][0]*rpy[0][j] + a[i][1]*rpy[1][j] + a[i][2]*rpy[2][j];

return(0);
}

/************************************************************
   set_gvar_con - Initializes GVAR constants
************************************************************/
void set_gvar_con(instr, nadnsc, nadnsi, nadewc, nadewi)

long instr, nadnsc, nadnsi, nadewc, nadewi;

{
inc_max[0]   = 6136;
inc_max[1]   = 2805;
elev_inc[0]  = 8.E-6;
elev_inc[1]  = 17.5E-6;
scan_inc[0]  = 16.E-6;
scan_inc[1]  = 35.E-6;
elev_dln[0]  = 28.E-6;
elev_dln[1]  = 280.E-6;
scan_pix[0]  = 16.E-6;
scan_pix[1]  = 280.E-6;
elev_bnds[0] = 0.220896;
elev_bnds[1] = 0.22089375;
scan_bnds[0] = 0.24544;
scan_bnds[1] = 0.2454375;
/*
  New code because of change to elug; nadir position is available
  in the signal, so should compute 4 values above using them
  instead of having them hardwired

  New code from Kathy Kelly for sounder nav - 10/27
*/
if( nadnsc != 0 && nadnsi != 0 && nadewc != 0 && nadewi != 0 ){
   if( gv_nav.imgsnd == 0 )
       elev_bnds[instr] = (nadnsc*inc_max[instr]+nadnsi)*elev_inc[instr];
   else
       elev_bnds[instr] = ((9-nadnsc)*inc_max[instr]-nadnsi)*elev_inc[instr];

   scan_bnds[instr] = (nadewc*inc_max[instr]+nadewi)*scan_inc[instr];
   }
}

/************************************************************
   timex - General time conversion utility to convert year, 
   day, hour, minute and second to minutes from Jan 1, 1950
************************************************************/
double timex( year, day, hour, min, sec )

int year, day, hour, min;
float sec;

{
unsigned long temp;
/*
  Here we convert year and day of year to number of
  days from 0 hour UT, 1950 Jan. 1.0
  This conversion is based on an algorithm by Fliegel and Van
  Flandern, Comm. of ACM, Vol.11, No. 10, Oct. 1968 (P.657)
*/
temp = day + 1461 * (year + 4799) / 4 - 3 * ((year + 4899) / 100) / 4 - 2465022;
/*
  Compute time in minutes from January 1.0, 1950
*/
return (double)temp * 1440 + hour * 60 + min + sec / 60;
}

/************************************************************
   time50 - Converts file based date to epoch time in minutes
   from Jan 1, 1950
************************************************************/
double time50( epoch )

long epoch[2];

{
unsigned long temp;
int year, day, hour, min;
float sec;

temp = epoch[0];

hour = (temp % 16) * 10; temp >>= 4;
day = (temp % 16); temp >>= 4;
day += (temp % 16) * 10; temp >>= 4;
day += (temp % 16) * 100; temp >>= 4;
year = (temp % 16); temp >>= 4;
year += (temp % 16) * 10; temp >>= 4;
year += (temp % 16) * 100; temp >>= 4;
year += (temp % 16) * 1000; temp >>= 4;

temp = epoch[1];

sec = (temp % 16) * .001; temp >>= 4;
sec += (temp % 16) * .01; temp >>= 4;
sec += (temp % 16) * .1; temp >>= 4;
sec += (temp % 16); temp >>= 4;
sec += (temp % 16) * 10; temp >>= 4;
min = (temp % 16); temp >>= 4;
min += (temp % 16) * 10; temp >>= 4;
hour += (temp % 16); temp >>= 4;
/*
  Here we convert year and day of year to number of
  days from 0 hour UT, 1950 Jan. 1.0
  This conversion is based on an algorithm by Fliegel and Van
  Flandern, Comm. of ACM, Vol.11, No. 10, Oct. 1968 (P.657)
*/
temp = day + 1461 * (year + 4799) / 4 - 3 * ((year + 4899) / 100) / 4 - 2465022;
/*
  Compute time in minutes from January 1.0, 1950
*/
return (double)temp * 1440 + hour * 60 + min + sec / 60;
}

/************************************************************
   ELEV_LINE
************************************************************/
double elev_line(instr,line)

int instr;         /* Instrument code (0-imager, 1-sounder) */
double line;       /* Fractional line number */

{
if (instr == 1)
  return elev_bnds[instr] - (line - 4.5) * elev_dln[instr];
else
  return elev_bnds[instr] - (line - 2.5) * elev_dln[instr];
}

/************************************************************
   SCAN_ANGLE
************************************************************/
double scan_angle(instr,pixel)

int instr;         /* Instrument code (0-imager, 1-sounder) */
double pixel;      /* Fractional pixel number */

{
return (pixel - 1.) * scan_pix[instr] - scan_bnds[instr];
}

/************************************************************
   lpoint - Converts GVAR elevation and scan line to lat and lon
************************************************************/
int lpoint(elev,scan,lat,lon)

float elev;       /* Elevation angle (rad) */
float scan;       /* Scan angle (rad) */
float *lat;       /* Latitude in radians (OUTPUT) */
float *lon;       /* Longitude in radians (OUTPUT) */

{
/*
  Output status; 0 - point on the earth found,
                 1 - instrument points off earth
*/
struct angle alpha;
struct angle zeta;
struct pt g,g1,u;
double q1,q2,d,d1,h;
/*
  Computes trigonometric functions of the scan and elevation
  angles corrected for the roll and pitch misalignments
*/
alpha.ang = elev;
alpha.sin = sin( alpha.ang );
alpha.cos = cos( alpha.ang );

zeta.ang = scan;
zeta.sin = sin( zeta.ang );
zeta.cos = cos( zeta.ang );

alpha.ang = alpha.ang - pma*alpha.sin*(1./zeta.cos + tan(zeta.ang)) -
            rma*(1 - alpha.cos/zeta.cos);
zeta.ang = zeta.ang + rma*alpha.sin;
/*
  Corrected scan angle
*/
alpha.sin = sin( alpha.ang );
alpha.cos = cos( alpha.ang );
zeta.sin = sin( zeta.ang );
zeta.cos = cos( zeta.ang );
/*
  Computes pointing vector in instrument coordinates
*/
g.x =  zeta.sin;
g.y = -zeta.cos * alpha.sin;
g.z =  zeta.cos * alpha.cos;
/*
  Transforms the pointing vector to earth fixed coordinates
*/
g1.x = bt[0][0]*g.x + bt[0][1]*g.y + bt[0][2]*g.z;
g1.y = bt[1][0]*g.x + bt[1][1]*g.y + bt[1][2]*g.z;
g1.z = bt[2][0]*g.x + bt[2][1]*g.y + bt[2][2]*g.z;
/*
  Computes coefficients and solves a quadratic equation to
  find the intersect of the pointing vector with the earth
  surface
*/
q1 = g1.x*g1.x + g1.y*g1.y + REQRPL2*g1.z*g1.z;
q2 = xs.x*g1.x + xs.y*g1.y + REQRPL2*xs.z*g1.z;
d  = q2 * q2 - q1 * q3;

if( d < 1e-9 && d > -1e-9 )
   d = 0;
/*
  If the discriminant of the equation, d, is negative, the
  instrument points off the earth
*/
if( d < 0 ){
   *lat = MISS;
   *lon = MISS;
   return 1;
   }
d = sqrt(d);
/*
  Slant distance from the satellite to the earth point
*/
h = -(q2 + d) / q1;
/*
  Cartesian coordinates of the earth point
*/
u.x = xs.x + h*g1.x;
u.y = xs.y + h*g1.y;
u.z = xs.z + h*g1.z;
/*
  Sinus of geocentric latitude
*/
d1 = u.z / sqrt(u.x*u.x + u.y*u.y + u.z*u.z);
/*
  Geographic (geodetic) coordinates of the point
*/
*lat = atan( REQRPL2 * d1 / sqrt(1. - d1 * d1));
*lon = atan2( u.y, u.x );
return 0;
}

/************************************************************
   sat2earth (NVXSAE) - Converts line, elem on GVAR image to lat,lon
************************************************************/
int sat2earth( line, elem, lat, lon)

float line, elem;
float *lat, *lon;

{
float elev, scan;
/*
  If doing sounder nav, have to trick routines into thinking image is
  at res 1, because nav routines take sounder res into account
*/
if( instr == 1 ){
   line = (line+9)/10;
   line = (line+9)/10;
   }
/*
  Compute elevation and scan angles related to input
  line and pixel numbers
*/
elev = elev_line(instr,line);
scan = scan_angle(instr,elem);
/*
  Transform elevation and scan angles to geographic coordinates
*/
if( lpoint(elev,scan,lat,lon) == 0 ){
   *lat = *lat * RDC;
   *lon = *lon * RDC;
   return 0;
   }
return -1;
}

/************************************************************
   gpoint - Converts GVAR lat and lon to elevation and scan angle
************************************************************/
int gpoint(lat,lon,elev,scan)

float lat;     /* Geographic latitude in radians (input) */
float lon;     /* Geographic longitude in radians (input) */
float *elev;   /* Elevation angle in radians (output) */
float *scan;   /* Scan angle in radians (output) */

{
/*
  Output status; 0 - successful completion,
                 1 - point with given lat/lon is invisible
*/
struct pt u,f,ft;
double sing;
double slat;
double w1,w2;
/*
  Computes sinus of geographic (geodetic) latitude
*/
sing = sin(lat);
w1 = REQRPL4 * sing * sing;
/*
  Sinus of the geocentric latitude
*/
slat = ((0.375 * w1 - 0.5) * w1 + 1.) * sing / REQRPL2;
/*
  Computes local earth radius at specified point
*/
w2 = slat * slat;
w1 = REQRPL3 * w2;
w1 = (0.375 * w1 - 0.5) * w1 + 1;
/*
  Computes cartesian coordinates of the point
*/
u.z = slat * w1;
w2  = w1 * sqrt(1 - w2);
u.x = w2 * cos(lon);
u.y = w2 * sin(lon);
/*
  Pointing vector from satellite to the earth point
*/
f.x = u.x - xs.x;
f.y = u.y - xs.y;
f.z = u.z - xs.z;
w2  = u.x*f.x + u.y*f.y + u.z*f.z*REQRPL2;
/*
  Verifies visibility of the point
*/
if( w2 > 0 ){
   *elev  = MISS;
   *scan  = MISS;
   return 1;
   }
/*
  Converts pointing vector to instrument coordinates
*/
ft.x = bt[0][0]*f.x + bt[1][0]*f.y + bt[2][0]*f.z;
ft.y = bt[0][1]*f.x + bt[1][1]*f.y + bt[2][1]*f.z;
ft.z = bt[0][2]*f.x + bt[1][2]*f.y + bt[2][2]*f.z;
/*
  Converts pointing vector to scan and elevation angles and
  corrects for the roll and pitch misalignments
*/
*scan  =  atan( ft.x / sqrt(ft.y*ft.y + ft.z*ft.z));
*elev  = -atan( ft.y / ft.z );
w1    = sin(*elev);
w2    = cos(*scan);

*elev  = *elev + rma * (1 - cos(*elev) / w2) + pma * w1 * (1/w2 + tan(*scan));
*scan  = *scan - rma * w1;
return 0;
}

/************************************************************
  earth2sat (NVXEAS) - Converts lat, lon to line, elem on GVAR image
************************************************************/
void earth2sat( lat,lon,line,elem )

float lat,lon,*line,*elem;

{
float elev, scan;

if( lat > 90 || lat < -90 ) {
   *line=MISS;
   *elem=MISS;
   return;
  }
/*
  Transform lat/lon to elevation and scan angles
*/
if( gpoint(lat*DRC,lon*DRC,&elev,&scan)) {
   *line=MISS;
   *elem=MISS;
   return;
   }
/*
  Convert elevation and scan angles to line/pixel coordinates
*/
elev_scan2line_pix(elev,scan,line,elem);
/*
  If doing sounder nav, change lin & elem return to res 10 values
*/
if( instr == 1 ){
   *line = *line*10-9;
   *elem = *elem*10-9;
   }
}

#define GRACON 0.07436574  /* Gravitation constant */
#define J2 1082.28E-6      /* 2nd harmonic coefficient of earths 
                              aspherical gravitational potential */
#define J4 -2.12E-6        /* 4th harmonic coefficient of earths 
                              aspherical gravitational potential */
#define EPSILON 1.0E-8     /* Convergence criterion used for calculating 
                              eccentric anomaly */
#undef NUM_ITER
#define NUM_ITER 20

/************************************************************************
   SATPOS - Determines the location of the satellite based on orbit
   navigation information. (GOES only)
************************************************************************/
void satpos( sattim, coord, sat )

struct datim *sattim;
int coord;
struct pt *sat;

{
int i;
double temp;
long itemp;
static int initial = 0;
static long key = 0;
int year;
int day;
int iday;
int sign;
/*
   GEOS declarations
*/
static struct datim epoch;      /* Epoch time */
static struct datim peri_focus; /* Peri-focus time */
static struct angle inc;        /* Inclination angle */
static struct angle earth;      /* Earth position angle */
static struct angle per;        /* Orbit perigee position */
static struct angle asn;        /* Orbit ascending mode */
static struct pt p,q;
static struct pt tpt;           /* Temporary point */
static double ecc_anom;
static double ecc_fac;          /* eccentricity factor */
static double orb_sparam;       /* orbital semi-parameter */
static double mean_motion;      /* Mean motion constant */
static double an_mean_motion;   /* anamalistic mean motion constant */
static double mean_anom;        /* Mean anomaly */
static double del_per;
static double del_asn;
static double time_diff;
static double adj_perigee;
static double adj_ascnode;

/*
  Test to see if day or satellite has changed necessitating param update
*/
if( !initial ){
   if( g_orb.sat_type < 0 )
      g_orb.sat_type = -g_orb.sat_type;
/*
   Convert epoch to julian day - time 
*/
   epoch.date = mdcon(1, g_orb.epoch_date);
   epoch.tim = int2deg(g_orb.epoch_time);
/*
   Define mean anomaly

   Explicit relationships between v,e, and m are given by the following:

   cos(v)=(cos(e)-i)/(1-i*cos(e)
   sin(v)=sqrt(1-i*i)*sin(e)/(1-i*cos(e))
   cos(e)=(cos(v)+i)/(1+i*cos(v))
   sin(e)=(sqrt(1-i*i)*sin(v)/(1+i*cos(v))
   m=e-i*sin(e)   
*/
   if( g_orb.anom_type == 0 )
      mean_anom = g_orb.anomaly;
   else {
      temp = cos(DRC * g_orb.anomaly);
      ecc_anom = acos((temp + g_orb.eccent) / (1.0 + g_orb.eccent * temp));
      mean_anom = (ecc_anom - g_orb.eccent * sin(ecc_anom)) * RDC;
      }
   mean_anom = fmod(mean_anom,360.); 
   if( mean_anom < 0. ) mean_anom += 360.0;
/*
   Define eccentricity factor and orbital semi-parameter
*/
   ecc_fac = sqrt(1.0 - (g_orb.eccent * g_orb.eccent));
   orb_sparam = (g_orb.semi_maj / RADE_EQUA) * ecc_fac * ecc_fac;
/*
   Calculate inclination sin and cos terms
*/
   inc.ang = g_orb.inclin*DRC;
   inc.sin = sin(inc.ang);
   inc.cos = cos(inc.ang);
/*
   Mean motion constant - speed of tranversal around earth in rad/min
*/
   mean_motion = GRACON * pow((RADE_EQUA / g_orb.semi_maj), 1.5);
/*
   Calculate orbital period
*/
   g_orb.period = TWO_PI / mean_motion;
/*
   Calculate anomalistic mean motion constant and derivitives based
   on selected order of secular perturbation theory

   Zero order
*/
   if( g_orb.sec_ord == 0 ){
      an_mean_motion = mean_motion;
      del_per = 0.0;
      del_asn = 0.0;
      }
/*
   First order 
*/
   else if( g_orb.sec_ord == 1 ){
      an_mean_motion = mean_motion*(1.0 + (1.5*J2*ecc_fac/
         (orb_sparam*orb_sparam))*(1.0 - 1.5*inc.sin*inc.sin));
      del_per = (1.5*J2*(2.0 - 2.5*inc.sin*inc.sin)/
         (orb_sparam*orb_sparam))*an_mean_motion*RDC;
      del_asn = -(1.5*J2*inc.cos/(orb_sparam*orb_sparam))*an_mean_motion*RDC;
      }
/*
   Second order 

   else if( g_orb.sec_ord == 2 ){
      an_mean_motion = mean_motion*(1.0 + (1.5*J2*ecc_fac/
             (orb_sparam*orb_sparam))*(1.0 - 1.5*inc.sin*inc.sin) +
             (0.0234375*J2*J2*ecc_fac/pow(orb_sparam,4.))*(16.*ecc_fac +
             25.*ecc_fac*ecc_fac - 15. + (30. - 96.*ecc_fac - 
             90.*ecc_fac*ecc_fac)*inc.cos*inc.cos + (105. + 144.*ecc_fac + 
             25.*ecc_fac*ecc_fac)*pow(inc.cos,4.)) -
             (.3515625*J4*ecc_fac*g_orb.eccent*g_orb.eccent/pow(orb_sparam,4.))*
             (3. - 30.*inc.cos*inc.cos + 35.*pow(inc.cos,4.)));
      del_per = ((1.5*J2*an_mean_motion/(orb_sparam*orb_sparam))*(2. - 
             2.5*inc.sin*inc.sin)*(1. + (1.5*J2/(orb_sparam*orb_sparam))*
             (2. + g_orb.eccent*g_orb.eccent/2. - 2.*ecc_fac - (1.791666667 - 
             g_orb.eccent*g_orb.eccent/48. - 3.*ecc_fac)*inc.sin*inc.sin)) - 
             1.25*J2*J2*g_orb.eccent*g_orb.eccent*mean_motion*pow(inc.cos,4.)/
             pow(orb_sparam,4.) - (4.375*J4*mean_motion/pow(orb_sparam,4.))*
             (1.714285714 - 6.642857143*inc.sin*inc.sin + 
             5.25*pow(inc.sin,4.) + g_orb.eccent*g_orb.eccent*(1.928571429 - 
             6.75*inc.sin*inc.sin + 5.0625*pow(inc.sin,4.))))*RDC;
      del_asn = -((1.25*J2*an_mean_motion*inc.cos/(orb_sparam*orb_sparam))*
             (1. + (1.5*J2/(orb_sparam*orb_sparam))*(1.5 + 
             g_orb.eccent*g_orb.eccent/6. - 2.*ecc_fac - (1.666666667 - 
             .208333333*g_orb.eccent*g_orb.eccent - 3.*ecc_fac)*
             inc.sin*inc.sin)) + (4.375*J4*mean_motion/pow(orb_sparam,4.))*
             (1. + 1.5*g_orb.eccent*g_orb.eccent)*(.857142857 - 
             1.5*inc.sin*inc.sin)*inc.cos)*RDC;
      }
*/
/*
   Calculate anomalistic period 
*/
   g_orb.anom_per = TWO_PI / an_mean_motion;
/*
   Determine time of peri-focal passage 
*/
   year = epoch.date / 1000;
   day = epoch.date % 1000;
   peri_focus.tim = epoch.tim - DRC * mean_anom / (60.0 * an_mean_motion);
   if( peri_focus.tim >= 0.0 )
      sign = 1;
   else
      sign = -1;
   iday = sign * (fabs(peri_focus.tim) / 24.0 + 1.0);
   if( iday > 0 ) iday--;
   peri_focus.tim -= iday * 24.0;
   if( iday != 0 ){
      day += iday;
      if( day < 1 ){
         year--;
         day += numyr((long)year);
         }
      else if( day > numyr((long)year)){
         day -= numyr((long)year);
         year++;
         }
      }
   itemp = deg2int(peri_focus.tim);
   peri_focus.tim = int2deg(itemp);
   peri_focus.date = 1000L * year + day;
/*
   Adjust perigee and ascending node to time of peri-focal passage
*/
   time_diff = timdif(epoch.date, epoch.tim, peri_focus.date, peri_focus.tim);
   adj_perigee = g_orb.perigee + del_per * time_diff;
   adj_perigee = fmod(adj_perigee,360.);
   if( adj_perigee < 0. ) adj_perigee += 360.;
   adj_ascnode = g_orb.asc_node + del_asn * time_diff;
   adj_ascnode = fmod(adj_ascnode,360.);
   if( adj_ascnode < 0. ) adj_ascnode += 360.;
   key = 1;
   }
/*
   Calculate delta-time ( from time of peri-focus to specified time )
*/
time_diff = timdif(peri_focus.date, peri_focus.tim, sattim->date, sattim->tim);
if( g_orb.sec_ord != 0 || key != 0 ){
   key = 0;
/*
   Calculate time dependent values of perigee and ascending node
*/
   per.ang = DRC * (adj_perigee + del_per * time_diff);
   asn.ang = DRC * (adj_ascnode + del_asn * time_diff);
/*
   Calculate perigee and ascending node sin and cos terms
*/
   per.sin = sin(per.ang);
   per.cos = cos(per.ang);
   asn.sin = sin(asn.ang);
   asn.cos = cos(asn.ang);
/*
   Calculate the (P,Q,W) orthogonal orientation vectors P
   points toward peri-focus Q is in the orbit plane advanced
   from P by a right angle in the direction of increasing
   true anomaly W completes a right handed coordinate system
*/
   p.x =  per.cos*asn.cos - per.sin*asn.sin*inc.cos;
   p.y =  per.cos*asn.sin + per.sin*asn.cos*inc.cos;
   p.z =  per.sin*inc.sin;
   q.x = -per.sin*asn.cos - per.cos*asn.sin*inc.cos;
   q.y = -per.sin*asn.sin + per.cos*asn.cos*inc.cos;
   q.z =  per.cos*inc.sin;
/*
   The W terms were commented out in the original
   wx =  asn.sin*inc.sin;
   wy = -asn.cos*inc.sin;
   wz =  inc.cos;
*/
   initial = 1;
   }
/*
   define mean anomaly (m) at specified time 
*/
mean_anom = fmod((an_mean_motion*time_diff),TWO_PI);
/*
   Calculate eccentric anomaly (e) at specified time

   The solution is given by a simplified numerical (Newtons) method. An
   explicit relationship involves a bessel function of the first
   kind.
 
   e = m+2*sum(n=1,infinity)(j(n)(n*i)*sin(n*m))
*/
temp = mean_anom;
for( i = 0; i < NUM_ITER; i++ ){
   ecc_anom = mean_anom + g_orb.eccent * sin(temp);
   if( fabs(ecc_anom - temp) < EPSILON ) break;
   temp = ecc_anom;
   }
/*
   Expression for magnitude of satellite radius vector (r)

   r = re*orb_sparam/(1.0+g_orb.eccent*cos(ecc_anom))

   Generate a position vector with respect to the focus and in the
   orbital plane. Note that the Z coordinate is by definition zero.
*/
tpt.x = g_orb.semi_maj * (cos(ecc_anom) - g_orb.eccent);
tpt.y = g_orb.semi_maj * (sin(ecc_anom) * ecc_fac);
/*
   Transformation to a celestial pointing vector by utilization of
   the transpose of the (P,Q,W) orthoganal transfromation matrix.
   Note that the third row containing W is not required because
   zomega is zero.
*/
sat->x = tpt.x * p.x + tpt.y * q.x;
sat->y = tpt.x * p.y + tpt.y * q.y;
sat->z = tpt.x * p.z + tpt.y * q.z;
if( coord == 0 ){
/*
   Determine transformation matrix for rotation to terrestrial coord's
*/
   time_diff = timdif( ref.date, ref.tim, sattim->date, sattim->tim);
   earth.ang = time_diff * (earth_rot - prec_veq);
   earth.ang = fmod(earth.ang,TWO_PI);
   if( earth.ang < 0 ) earth.ang += TWO_PI;
   earth.sin = sin(earth.ang);
   earth.cos = cos(earth.ang);
   tpt = *sat;
/*
   Rotation to terrestrial pointing vector 
*/
   sat->x = earth.cos * tpt.x + earth.sin * tpt.y;
   sat->y = -earth.sin * tpt.x + earth.cos * tpt.y;
   }
return;
}

/************************************************************************
   SAT_TRAN - Performs coordinate transformations between earth and
   satellite coordinate systems based on orbit navigation information
   for the satellite.  This is based on the Wisconsin SSEC/McIDAS area
   navigation information.

   type = transformation type
        = 1 for satellite to earth transformation (specify xlin,xele)
        = 2 for earth to satellite transformation (specify xlat,xlon)
        = 3 for subpoint calculation (specify nothing)
************************************************************************/
#undef NUM_ITER
#define NUM_ITER 5

void sat_tran( type,lat,lon,lin,ele )

int type;
float *lat,*lon,*lin,*ele;

{
double temp;
static int initial = 0;
static double center_elem;       /* Picture center element */
static double rad_per_line;      /* Radians per scan line */
static double rad_per_elem;      /* Radians per element */
static double time_diff;         /* Time difference in minutes */
static double rot_ax[3][2];      /* Rotation axis transformation matrix */
static double rot_sat[3][3];     /* Satellite axis transformation matrix */
static int num_sensor;           /* Number of sensors on satellite */
static struct datim pix;         /* Pixel sample point time */
static struct angle line;        /* */
static struct angle elem;
static struct angle lats;
static struct angle lons;
static struct angle ras;
static struct angle dec;
static struct angle roll;
static struct angle pitch;
static struct angle yaw;
static struct angle prec;        /* Precession direction */
static struct angle ptot;        /* Total precession to sample point time */
static struct angle gamma;       /* Image east-west drift angle */
static struct angle earth;       /* Earth rotation */
static struct pt spin_ax;        /* Spin axis in earth ref frame at t(0) */
static struct pt u_sat;
static struct pt v_sat;
static struct pt pts[3];
static struct pt pt_vec,tmp_vec;
static long iter;
static double angx,angy;
static double x,y;
static double par_line,par_elem; /* Partial line,element counts */
static double prev_scan,new_scan;

if( image_type == GOES ){
   if( !initial ){
/*
   Expanded set of transformation parameters 
*/
      num_sensor = (g_nav.num_lines/100000L)%100;
      if( num_sensor < 1 ) num_sensor = 1;
      g_nav.num_lines = num_sensor*(g_nav.num_lines%100000L); 
      center_elem = (1 + g_nav.num_elem)/2.;
      rad_per_line = g_nav.deg_line*DRC/(g_nav.num_lines - 1.);
      rad_per_elem = g_nav.deg_elem*DRC/(g_nav.num_elem - 1.);

      dec.ang = g_nav.declin*DRC;
      dec.sin = sin(dec.ang);
      dec.cos = cos(dec.ang);

      time_diff = timdif(ref.date,ref.tim,sat_time.date,0.);
      ras.ang = g_nav.right_asc*DRC - time_diff*(earth_rot - prec_veq);
      ras.ang = fmod(ras.ang,TWO_PI);
      if( ras.ang < 0 ) ras.ang += TWO_PI;
      ras.sin = sin(ras.ang);
      ras.cos = cos(ras.ang);

      spin_ax.x = dec.cos*ras.cos;
      spin_ax.y = dec.cos*ras.sin;
      spin_ax.z = dec.sin;

      g_nav.prec_rate = g_nav.prec_rate*DRC/24.;
      prec.ang = g_nav.prec_dir*DRC;
      prec.sin = sin(prec.ang);
      prec.cos = cos(prec.ang);

      pitch.ang = g_nav.pitch*DRC;
      pitch.sin = sin(pitch.ang); 
      pitch.cos = cos(pitch.ang);

      yaw.ang = g_nav.yaw*DRC;
      yaw.sin = sin(yaw.ang);
      yaw.cos = cos(yaw.ang);

      roll.ang = g_nav.roll*DRC;
      roll.sin = sin(roll.ang);
      roll.cos = cos(roll.ang);
/*
   Compute rotational matrix for pinciple axis misalignment 
*/
      rot_ax[0][0] = roll.cos*pitch.cos;
      rot_ax[0][1] = yaw.sin*roll.sin*pitch.cos + yaw.cos*pitch.sin;
      rot_ax[1][0] = -roll.sin;
      rot_ax[1][1] = yaw.sin*roll.cos;
      rot_ax[2][0] = -roll.cos*pitch.sin;
      rot_ax[2][1] = yaw.cos*pitch.cos - yaw.sin*roll.sin*pitch.sin;
      initial = 1;
      }
/*
   Normalize satellite coordinates and convert earth coordinates to radians
   transform latitude to geocentric coordinates check for transform direction 
*/
   switch( type ){
      case 1:
         line.ang = rad_per_line*(*lin - g_nav.center_line);
         elem.ang = rad_per_elem*(*ele - center_elem);
         line.sin = sin(line.ang);
         line.cos = cos(line.ang);
         elem.sin = sin(elem.ang);
         elem.cos = cos(elem.ang);
         iter = NUM_ITER;
         break;
      case 2:
         *lin = g_nav.center_line;
         *ele = center_elem;
         lats.ang = DRC*geolat(1,*lat);
         lons.ang = DRC*(*lon);
         lats.sin = sin(lats.ang);
         lats.cos = cos(lats.ang);
         lons.sin = sin(lons.ang);
         lons.cos = cos(lons.ang);
         iter = 0; 
         break;
      case 3: 
         *lin = g_nav.center_line; 
         *ele = center_elem;
         line.ang = rad_per_line*(*lin - g_nav.center_line);
         elem.ang = rad_per_elem*(*ele - center_elem);
         line.sin = sin(line.ang);
         line.cos = cos(line.ang);
         elem.sin = sin(elem.ang);
         elem.cos = cos(elem.ang);
         iter = 0; 
         break;
      }
   do {
/*
   Compute time dependent parameters 
*/
      par_line = (double)((round_long(*lin) - 1)/num_sensor); 
      par_elem = (*ele - 1.)/(g_nav.num_elem - 1.)*(g_nav.deg_elem/360.);
      pix.date = sat_time.date;
      pix.tim = sat_time.tim + g_nav.spin_rate*(par_line + par_elem);
      ptot.ang = pix.tim*g_nav.prec_rate;
      ptot.sin = sin(ptot.ang);
      ptot.cos = cos(ptot.ang);
      gamma.ang = rad_per_elem*(gamma_init + gamma_dot*pix.tim);
      gamma.sin = sin(gamma.ang);
      gamma.cos = cos(gamma.ang);
      earth.ang = PI*SOLSID/12.*pix.tim;
      earth.sin = sin(earth.ang);
      earth.cos = cos(earth.ang);
/*
   Determine satellite position vector (keplerian model) 
*/
      satpos(&pix, 0, &sat);
      sphere_cvt( 1, &sat, &sp_sat );
/*
   Compute unit pointing vector 
*/
      u_sat.x = sat.x/sp_sat.hgt;
      u_sat.y = sat.y/sp_sat.hgt;
      u_sat.z = sat.z/sp_sat.hgt;
/*
   Repoint spin axis as function of precession 
*/
      pts[0].x = sqrt(1./(1. + (spin_ax.x/spin_ax.z)*(spin_ax.x/spin_ax.z)));
      pts[0].y = 0.;
      pts[0].z = -(spin_ax.x*pts[0].x/spin_ax.z);
      pts[1].x = spin_ax.y*pts[0].z;
      pts[1].y = spin_ax.z*pts[0].x - spin_ax.x*pts[0].z;
      pts[1].z = -spin_ax.y*pts[0].x;
      pts[0].x = prec.cos*pts[0].x + prec.sin*pts[1].x;
      pts[0].y = prec.cos*pts[0].y + prec.sin*pts[1].y;
      pts[0].z = prec.cos*pts[0].z + prec.sin*pts[1].z;
      spin_ax.x = ptot.cos*spin_ax.x + ptot.sin*pts[0].x;
      spin_ax.y = ptot.cos*spin_ax.y + ptot.sin*pts[0].y;
      spin_ax.z = ptot.cos*spin_ax.z + ptot.sin*pts[0].z;
/*
   Compute nominal satellite position rotational matrix 
*/
      rot_sat[2][0] = earth.cos*spin_ax.x + earth.sin*spin_ax.y;
      rot_sat[2][1] = -earth.sin*spin_ax.x + earth.cos*spin_ax.y;
      rot_sat[2][2] = spin_ax.z;
      temp = u_sat.x*rot_sat[2][0] +
             u_sat.y*rot_sat[2][1] +
             u_sat.z*rot_sat[2][2];
      v_sat.x = u_sat.x - temp*rot_sat[2][0];
      v_sat.y = u_sat.y - temp*rot_sat[2][1];
      v_sat.z = u_sat.z - temp*rot_sat[2][2];
/*
   Normalize v_sat
*/
      temp = -1./sqrt(v_sat.x*v_sat.x + v_sat.y*v_sat.y + v_sat.z*v_sat.z);
      rot_sat[0][0] = v_sat.x*temp;
      rot_sat[0][1] = v_sat.y*temp;
      rot_sat[0][2] = v_sat.z*temp;
      rot_sat[1][0] = rot_sat[2][1]*rot_sat[0][2] -
                      rot_sat[2][2]*rot_sat[0][1];
      rot_sat[1][1] = rot_sat[2][2]*rot_sat[0][0] -
                      rot_sat[2][0]*rot_sat[0][2];
      rot_sat[1][2] = rot_sat[2][0]*rot_sat[0][1] -
                      rot_sat[2][1]*rot_sat[0][0];
      rot_sat[0][0] = rot_sat[0][0]*gamma.cos - gamma.sin*rot_sat[1][0];
      rot_sat[1][0] = rot_sat[0][0]*gamma.sin + gamma.cos*rot_sat[1][0];
      rot_sat[0][1] = rot_sat[0][1]*gamma.cos - gamma.sin*rot_sat[1][1];
      rot_sat[1][1] = rot_sat[0][1]*gamma.sin + gamma.cos*rot_sat[1][1];
      rot_sat[0][2] = rot_sat[0][2]*gamma.cos - gamma.sin*rot_sat[1][2];
      rot_sat[1][2] = rot_sat[0][2]*gamma.sin + gamma.cos*rot_sat[1][2];
/*
   Switch on transformation type 
*/
      switch( type ){
/*
   Case 1 - Transform from satellite coordinates to earth coordinates 
*/
         case 1: 
            {
            double a,b,c,rad;
            double equa_sq, pole_sq, ratio, in_ratio;
/*
   Compute pointing vector in satellite coordinate system at element 0 
*/
            pt_vec.x = rot_ax[0][0]*line.cos - rot_ax[0][1]*line.sin;
            pt_vec.y = rot_ax[1][0]*line.cos - rot_ax[1][1]*line.sin;
            pt_vec.z = rot_ax[2][0]*line.cos - rot_ax[2][1]*line.sin;
/*
   Adjust pointing vector for element count 
*/
            tmp_vec = pt_vec;
            pt_vec.x =  elem.cos*tmp_vec.x + elem.sin*tmp_vec.y;
            pt_vec.y = -elem.sin*tmp_vec.x + elem.cos*tmp_vec.y;
            pt_vec.z =  tmp_vec.z;
/*
   Compute pointing vector in earth coordinate system 
*/
            tmp_vec = pt_vec;
            pt_vec.x = rot_sat[0][0]*tmp_vec.x +
                       rot_sat[1][0]*tmp_vec.y +
                       rot_sat[2][0]*tmp_vec.z;
            pt_vec.y = rot_sat[0][1]*tmp_vec.x +
                       rot_sat[1][1]*tmp_vec.y +
                       rot_sat[2][1]*tmp_vec.z;
            pt_vec.z = rot_sat[0][2]*tmp_vec.x +
                       rot_sat[1][2]*tmp_vec.y +
                       rot_sat[2][2]*tmp_vec.z;
/*
   Adjust for oblateness of earth sphere and atmospheric height 
*/
            equa_sq = (RADE_EQUA + atms_hght)*(RADE_EQUA + atms_hght);
            pole_sq = (RADE_POLE + atms_hght)*(RADE_POLE + atms_hght);
            ratio = pole_sq/equa_sq;
            in_ratio = 1. - ratio;
            a = ratio + in_ratio*pt_vec.z*pt_vec.z;
            b = 2.*((pt_vec.x*sat.x + pt_vec.y*sat.y)*ratio + pt_vec.z*sat.z);
            c = (sat.x*sat.x + sat.y*sat.y)*ratio + sat.z*sat.z - pole_sq;
/*
   Compute radical 
*/
            rad = b*b - 4.*a*c;
/*
   Check if point is off earth and if so set rejection values 
*/
            if( rad >= 1. ){
/*
   Find point along pointing vector intersecting earth surface 
*/
               rad = -(b + sqrt(rad))/(2.*a);
               sat.x = sat.x + pt_vec.x*rad;
               sat.y = sat.y + pt_vec.y*rad;
               sat.z = sat.z + pt_vec.z*rad;
/*
   Convert to earth coordinates 
*/
               lats.ang = atan2(sat.z,sqrt(sat.x*sat.x + sat.y*sat.y));
               lons.ang = atan2(sat.y,sat.x);
/*
   Convert from radians to degrees and convert latitude to geodedtic coor 
*/
               *lat = geolat(2,lats.ang*RDC);
               *lon = lons.ang*RDC;
               return;
               }
            *lat = MISS;
            *lon = MISS;
            return;
            }
         case 2:
/*
   Transform from earth coordinates to satellite coordinates 
*/
            iter++;
            if( iter == 1 ){
/*
   Compute earth coordinate vector 
*/
               pt_vec.x = lats.cos*lons.cos;
               pt_vec.y = lats.cos*lons.sin;
               pt_vec.z = lats.sin; 
/*
   Check if point is out of saatellite view and 
   if so set rejection value and return 
*/
               temp = RADE_MEAN/sp_sat.hgt;
               if( pt_vec.x*u_sat.x + pt_vec.y*u_sat.y + pt_vec.z*u_sat.z < temp ){
                  *lin = MISS;
                  *ele = MISS;
                  return;
                  }
/*
   Adjust for oblateness of earth sphere and atmospheric height 
*/
               temp = (lats.sin/lats.cos)*(lats.sin/lats.cos);
               temp = sqrt((1. + temp)/
                      (RADE_POLE*RADE_POLE + RADE_EQUA*RADE_EQUA*temp))*
                      RADE_EQUA*RADE_POLE + atms_hght;
               pt_vec.x = temp*pt_vec.x;
               pt_vec.y = temp*pt_vec.y;
               pt_vec.z = temp*pt_vec.z;
               }
            break;
         case 3:
            iter++;
            if( iter == 1 ){
               pt_vec.x = 0.;
               pt_vec.y = 0.;
               pt_vec.z = 0.;
               }
            break;
         }
/*
   Compute vector from satellite to earth in earth coordinate system 
*/
      pts[0].y = pt_vec.x - sat.x;
      pts[1].y = pt_vec.y - sat.y;
      pts[2].y = pt_vec.z - sat.z;
/*
   Compute normalized pointing vector in earth coordinate system 
*/
      temp = 1./sqrt(pts[0].y*pts[0].y + pts[1].y*pts[1].y + pts[2].y*pts[2].y);
      pts[0].y = pts[0].y*temp;
      pts[1].y = pts[1].y*temp;
      pts[2].y = pts[2].y*temp;
/*
   Compute vector from satellite to earth in earth coordinate system 
*/
      pts[0].x = rot_sat[0][0]*pts[0].y +
                 rot_sat[0][1]*pts[1].y +
                 rot_sat[0][2]*pts[2].y;
      pts[1].x = rot_sat[1][0]*pts[0].y +
                 rot_sat[1][1]*pts[1].y +
                 rot_sat[1][2]*pts[2].y;
      pts[2].x = rot_sat[2][0]*pts[0].y +
                 rot_sat[2][1]*pts[1].y +
                 rot_sat[2][2]*pts[2].y;
/*
   Convert pointing vector to line-element coordinate 
*/
      temp = sqrt(rot_ax[2][0]*rot_ax[2][0] + rot_ax[2][1]*rot_ax[2][1] -
                  pts[2].x*pts[2].x);
      angx = atan2(pts[2].x,temp) - atan2(rot_ax[2][0],rot_ax[2][1]);
      x = rot_ax[0][0]*cos(angx) + rot_ax[0][1]*sin(angx);
      y = rot_ax[1][0]*cos(angx) + rot_ax[1][1]*sin(angx);
      angy = atan2(pts[1].x,pts[0].x) - atan2(y,x);
      prev_scan = (round_long(*lin) - 1)/num_sensor;
      *lin = g_nav.center_line - angx/rad_per_line;
      *ele = center_elem - angy/rad_per_elem;
      new_scan = (round_long(*lin) - 1)/num_sensor;
      }
   while( iter < NUM_ITER && prev_scan != new_scan );
   switch( type ){
      case 2:
/*
   Check if point is off frame and if so set rejection values 
*/
         if( *lin < 1 || *lin > g_nav.num_lines ||
             *ele < 1 || *ele > g_nav.num_elem ){
            *lin = MISS;
            *ele = MISS;
            }
         return;
      case 3:
/*
   Compute sub-satellite point from satellite position vector 
*/
         *lat = sp_sat.lat;
         *lon = sp_sat.lon;
         return;
      }
   }
/************************************************************
   GVAR Code
************************************************************/
else if( image_type == GVAR ){
/*
   Switch on transformation type 
*/
   switch( type ){
/*
   Case 1 - Transform from satellite coordinates to earth coordinates 
*/
      case 1: 
         sat2earth( *lin, *ele, lat, lon);
         break;
/*
   Transform from earth coordinates to satellite coordinates
*/
      case 2:
         earth2sat( *lat, *lon, lin, ele );
         break;
/*
   Compute sub-satellite point from satellite position vector 
*/
      case 3:
         *lat = sublat*RDC;
         *lon = sublon*RDC;
         return;
      }
   }
}
/************************************************************************
   SAT_INIT - Initializes the satellite parameters.
************************************************************************/
int sat_init( area )

struct mc_area *area;

{
int i;

if( !strncmp( area->type,"GOES",4 )){
/*
   Initialize the gv_nav structure from the data read in from image file
*/
   printf(" Initializing GOES data navigation\n");
   image_type = GOES;
   sat_image       = area;
   sat_time.date   = area->navg->iddate%100000;
   sat_time.tim    = int2deg((long)area->navg->itime );
   g_orb.sat_type    = 1;
   g_orb.sec_ord     = 0;
   g_orb.anom_type   = 0;
   g_orb.epoch_date  = area->navg->edate;
   g_orb.epoch_time  = area->navg->etime;
   g_orb.semi_maj    = area->navg->semima / 100.;
   g_orb.eccent      = area->navg->eccen / 1E6;
   g_orb.inclin      = area->navg->orbinc / 1000.;
   g_orb.anomaly     = area->navg->meana / 1000.;
   g_orb.perigee     = area->navg->perigee / 1000.;
   g_orb.asc_node    = area->navg->asnode / 1000.;
   g_nav.declin      = int2deg((long)area->navg->declin);
   g_nav.right_asc   = int2deg((long)area->navg->rascen);
   g_nav.center_line = area->navg->piclin;
   g_nav.spin_rate   = area->navg->spinp / 3.6E9;
   g_nav.deg_line    = int2deg((long)area->navg->deglin);
   g_nav.num_lines   = area->navg->lintot;
   g_nav.deg_elem    = int2deg((long)area->navg->degele);
   g_nav.num_elem    = area->navg->eletot;
   g_nav.pitch       = int2deg((long)area->navg->pitch);
   g_nav.yaw         = int2deg((long)area->navg->yaw);
   g_nav.roll        = int2deg((long)area->navg->roll);
   g_nav.prec_rate   = 0;
   g_nav.prec_dir    = 0;
   ecor = sat_image->dir->ecor;
   lcor = sat_image->dir->lcor;
   eres = sat_image->dir->eres;
   lres = sat_image->dir->lres;
   gamma_init      = area->navg->gamma / 100.;
   gamma_dot       = area->navg->gamdot / 100.;
   atms_hght       = 0;
   ref.date = REF_DATE;
   ref.tim = int2deg((long)REF_TIME);
/*
   Rotation rate of the vernal equinox in terms of sidereal time 
*/
   prec_veq = TWO_PI*SOLSID/(PREC_VER_EQ*SOLAR_YR*1440.);
   prec_veq = 0;
/*
   Terrestrial rotation rate in terms of sidereal time 
*/ 
   earth_rot = TWO_PI*SOLSID/1440.;

   printf( "date            = %ld\n", sat_time.date );
   printf( "pict_time       = %f\n", sat_time.tim );
   printf( "orb.sat_type    = %d\n", g_orb.sat_type );
   printf( "orb.sec_ord     = %d\n", g_orb.sec_ord );
   printf( "orb.anom_type   = %d\n", g_orb.anom_type );
   printf( "orb.epoch_date  = %ld\n", g_orb.epoch_date );
   printf( "orb.epoch_time  = %ld\n", g_orb.epoch_time );
   printf( "orb.semi_maj    = %f\n", g_orb.semi_maj );
   printf( "orb.eccent      = %f\n", g_orb.eccent );
   printf( "orb.inclin      = %f\n", g_orb.inclin );
   printf( "orb.anomaly     = %f\n", g_orb.anomaly );
   printf( "orb.perigee     = %f\n", g_orb.perigee );
   printf( "orb.asc_node    = %f\n", g_orb.asc_node );
   printf( "nav.declin      = %f\n", g_nav.declin );
   printf( "nav.right_asc   = %f\n", g_nav.right_asc );
   printf( "nav.center_line = %ld\n", g_nav.center_line );
   printf( "nav.spin_rate   = %f\n", g_nav.spin_rate );
   printf( "nav.deg_line    = %f\n", g_nav.deg_line );
   printf( "nav.num_lines   = %ld\n", g_nav.num_lines );
   printf( "nav.deg_elem    = %f\n", g_nav.deg_elem );
   printf( "nav.num_elem    = %ld\n", g_nav.num_elem );
   printf( "nav.pitch       = %f\n", g_nav.pitch );
   printf( "nav.yaw         = %f\n", g_nav.yaw );
   printf( "nav.roll        = %f\n", g_nav.roll );
   printf( "nav.prec_rate   = %f\n", g_nav.prec_rate );
   printf( "nav.prec_dir    = %f\n", g_nav.prec_dir );
   printf( "gamma_init      = %f\n", gamma_init );
   printf( "gamma_dot       = %f\n", gamma_dot );
   printf( "atms_hght       = %f\n", atms_hght );
   return 0;
   }
/************************************************************
   GVAR Code
************************************************************/
else if( !strncmp( area->type,"GVAR",4 )){
   int imc;
   double lam, dlat, dyaw;
   double r;
   double te, ts, wa;
   struct angle oi, u, asc;
/*
   Initialize the gv_nav structure from the data read in from image file
*/
   printf(" Initializing GVAR data navigation\n");
   image_type = GVAR;
   ecor = area->dir->ecor;
   lcor = area->dir->lcor;
   eres = area->dir->eres;
   lres = area->dir->lres;
   strcpy( area->navgv->sttype, gv_nav.sttype );
   gv_nav.idntfr = area->navgv->idntfr;
   gv_nav.imcact = area->navgv->imcact;
   gv_nav.reflon = area->navgv->reflon/10000000.;
   gv_nav.refdis = area->navgv->refdis/10000000.;
   gv_nav.reflat = area->navgv->reflat/10000000.;
   gv_nav.refyaw = area->navgv->refyaw/10000000.;
   gv_nav.ratrol = area->navgv->ratrol/10000000.;
   gv_nav.ratptc = area->navgv->ratptc/10000000.;
   gv_nav.ratyaw = area->navgv->ratyaw/10000000.;
   gv_nav.epoch = time50( area->navgv->etime );
   gv_nav.edtime = area->navgv->edtime/100.;
   gv_nav.imcrol = area->navgv->imcrol/10000000.;
   gv_nav.imcptc = area->navgv->imcptc/10000000.;
   gv_nav.imcyaw = area->navgv->imcyaw/10000000.;
   for( i = 0; i < 13; i++ )
      gv_nav.ldr[i] = area->navgv->ldr[i]/10000000.;
   for( i = 0; i < 11; i++ )
      gv_nav.rddr[i] = area->navgv->rddr[i]/10000000.;
   for( i = 0; i < 9; i++ )
      gv_nav.dgl[i] = area->navgv->dgl[i]/10000000.;
   for( i = 0; i < 9; i++ )
      gv_nav.doy[i] = area->navgv->doy[i]/10000000.;
   gv_nav.exptim = area->navgv->exptim/100.;
   gv_nav.solinc = area->navgv->solinc/10000000.;

   gatt_init( gv_nav.raawds, area->navgv->raawds );
   gatt_init( gv_nav.paawds, area->navgv->paawds );
   gatt_init( gv_nav.yaawds, area->navgv->yaawds );
   gatt_init( gv_nav.rmawds, area->navgv->rmawds );
   gatt_init( gv_nav.pmawds, area->navgv->pmawds );

   {
   int year, day, hour, min;
   float sec;
   
   year = area->navgv->imgday / 1000 + 1900;
   day = area->navgv->imgday % 1000;

   hour = area->navgv->imgtm / 10000000;
   min = area->navgv->imgtm % 10000000 / 100000;
   sec = area->navgv->imgtm % 100000 / 1000.;
   gv_nav.imgtim = timex( year, day, hour, min, sec );
   }

   instr = gv_nav.imgsnd = area->navgv->imgsnd-1;
/*
   Initialize constants 
*/
   set_gvar_con( gv_nav.imgsnd, area->navgv->iofnc, 
      area->navgv->iofni, area->navgv->iofec, area->navgv->iofei );
   if ( area->navgv->imcact & (1<<7))
      imc = 0;
   else
      imc = 1;
/*
  Assign reference values to the subsatellite longitude and
  latitude, the radial distance and the orbit yaw.
*/
   ts = 0;
   lam = gv_nav.reflon;
   dr  = gv_nav.refdis;
   phi.ang = gv_nav.reflat;
   psi.ang = gv_nav.refyaw;
/*
  Assign reference values to the attitudes and misalignments
*/
   roll  = gv_nav.ratrol;
   pitch = gv_nav.ratptc;
   yaw   = gv_nav.ratyaw;
   rma   = 0;
   pma   = 0;
/*
  If imc is off, compute changes in the satellite orbit
*/
   if( imc ){
      struct angle orb,orb1,orb2,orb3;
/*
  Set reference radial distance, latitude and orbit yaw to zero
*/
      dr  = 0;
      phi.ang = 0;
      psi.ang = 0;
/*
  Compute time since epoch (in minutes)
*/
      ts = gv_nav.imgtim - gv_nav.epoch;
/*
  Computes orbit angle and the related trigonometric functions.
  earth rotational rate=.729115e-4 (rad/s)
*/
      orb.ang   = (double)0.729115 - 240 * ts;
      orb.sin = sin( orb.ang );
      orb.cos = cos( orb.ang );
      orb1.ang = .927*orb.ang;
      orb1.sin = sin( orb1.ang );
      orb1.cos = cos( orb1.ang );
      orb2.ang = 2*orb.ang;
      orb2.sin = sin( orb2.ang );
      orb2.cos = cos( orb2.ang );
      orb3.ang = 1.9268*orb.ang;
      orb3.sin = sin( orb3.ang );
      orb3.cos = cos( orb3.ang );
/*
  Computes change in the imc longitude from the reference
*/
      lam = lam + gv_nav.ldr[0] + 
            (gv_nav.ldr[1] + gv_nav.ldr[2]*orb.ang)*orb.ang + 
            (gv_nav.ldr[9]*orb1.sin + gv_nav.ldr[10]*orb1.cos + 
            gv_nav.ldr[3]*orb.sin + gv_nav.ldr[4]*orb.cos + 
            gv_nav.ldr[5]*orb2.sin + gv_nav.ldr[6]*orb2.cos + 
            gv_nav.ldr[7]*orb3.sin + gv_nav.ldr[8]*orb3.cos + 
            orb.ang*(gv_nav.ldr[11]*orb.sin + gv_nav.ldr[12]*orb.cos))*2;
/*
  Computes change in radial distance from the reference (km)
*/
      dr = dr + gv_nav.rddr[0] + gv_nav.rddr[1]*orb.cos + 
           gv_nav.rddr[2]*orb.sin + gv_nav.rddr[3]*orb2.cos + 
           gv_nav.rddr[4]*orb2.sin + gv_nav.rddr[5]*orb3.cos +
           gv_nav.rddr[6]*orb3.sin + gv_nav.rddr[7]*orb1.cos + 
           gv_nav.rddr[8]*orb1.sin + 
           orb.ang*(gv_nav.rddr[9]*orb.cos + gv_nav.rddr[10]*orb.sin);
/*
  Computes the sine of the change in the geocentric latitude
*/
      dlat = gv_nav.dgl[0] + gv_nav.dgl[1]*orb.cos + gv_nav.dgl[2]*orb.sin + 
             gv_nav.dgl[3]*orb2.cos + gv_nav.dgl[4]*orb2.sin + 
             orb.ang*(gv_nav.dgl[5]*orb.cos + gv_nav.dgl[6]*orb.sin) + 
             gv_nav.dgl[7]*orb1.cos + gv_nav.dgl[8]*orb1.sin;
/*
  Computes geocentric latitude by using an expansion for arcsine
*/
      phi.ang = phi.ang + dlat * (1. + dlat * dlat / 6.);
/*
  Computes sine of the change in the orbit yaw
*/
      dyaw = gv_nav.doy[0] + gv_nav.doy[1]*orb.sin + gv_nav.doy[2]*orb.cos + 
             gv_nav.doy[3]*orb2.sin + gv_nav.doy[4]*orb2.cos + 
             orb.ang*(gv_nav.doy[5]*orb.sin + gv_nav.doy[6]*orb.cos) + 
             gv_nav.doy[7]*orb1.sin + gv_nav.doy[8]*orb1.cos;
/*
  Computes the orbit yaw by using an expansion for arcsine
*/
      psi.ang = psi.ang + dyaw * (1. + dyaw * dyaw / 6.);
      }
/*
  Conversion of the imc longitude and orbit yaw to the subsatellite
  longitude and the orbit inclination (Ref: GOES-PCC-TM-2473, inputs
  required for earth location and gridding by SPS, June 6, 1988)
*/
   phi.sin  = sin(phi.ang);
   psi.sin  = sin(psi.ang);
   oi.ang = phi.sin*phi.sin + psi.sin*psi.sin;
   oi.cos = sqrt(1-oi.ang);
   oi.sin = sqrt(oi.ang);

   if( phi.sin == 0 && psi.sin == 0 )
      u.ang = 0;
   else
      u.ang = atan2(phi.sin,psi.sin);

   u.sin  = sin(u.ang);
   u.cos  = cos(u.ang);
/*
  Computes longitude of the ascending node
*/
   asc.ang = lam-u.ang;
   asc.sin = sin(asc.ang);
   asc.cos = cos(asc.ang);
/*
  Computes the subsatellite geographic latitude
*/
   sublat = atan(REQRPL2*tan(phi.ang));
/*
  Computes the subsatellite longitude
*/
   sublon = asc.ang+atan2(oi.cos*u.sin,u.cos);
/*
  Computes the spacecraft to earth fixed coordinates transformation
  matrix:
      (vector in ecef coordinates) = b * (vector in s/c coordinates)
*/
   b[0][1] = -asc.sin*oi.sin;
   b[1][1] =  asc.cos*oi.sin;
   b[2][1] = -oi.cos;
   b[0][2] = -asc.cos*u.cos+asc.sin*u.sin*oi.cos;
   b[1][2] = -asc.sin*u.cos-asc.cos*u.sin*oi.cos;
   b[2][2] = -phi.sin;
   b[0][0] = -asc.cos*u.sin-asc.sin*u.cos*oi.cos;
   b[1][0] = -asc.sin*u.sin+asc.cos*u.cos*oi.cos;
   b[2][0] =  u.cos*oi.sin;
/*
  Computes the normalized spacecraft position vector in earth fixed
  coordinates - xs.
*/
   r     = (NOMORB+dr)/RADE_EQUA;
   xs.x = -b[0][2]*r;
   xs.y = -b[1][2]*r;
   xs.z = -b[2][2]*r;
/*
  Precomputes q3 (used in lpoint)
*/
   q3 = xs.x*xs.x + xs.y*xs.y + REQRPL2 * xs.z*xs.z - 1.0;
/*
  Computes the attitudes and misalignments if imc is off
*/
   if( imc ){
/*
  Computes the solar orbit angle
*/
      wa = gv_nav.solinc*ts;
/*
  Computes the difference between current time, ts, and the
  exponential time, iparms(62). Note that both times are since epoch.
*/
      te = ts - gv_nav.exptim;
/*
  Computes roll + roll misalignment
*/
      roll = roll + gatt(gv_nav.raawds,wa,te);
/*
  Computes pitch + pitch misalignment
*/
      pitch = pitch + gatt(gv_nav.paawds,wa,te);
/*
  Computes yaw
*/
      yaw = yaw + gatt(gv_nav.yaawds,wa,te);
/*
  Computes roll misalignment
*/
      rma = gatt(gv_nav.rmawds,wa,te);
/*
  Computes pitch misalignment
*/
      pma = gatt(gv_nav.pmawds,wa,te);
/*
  Apply the earth sensor compensation if needed
*/
      roll   = roll + gv_nav.imcrol;
      pitch  = pitch + gv_nav.imcptc;
      yaw    = yaw + gv_nav.imcyaw;
      }
/*
  Computes the instrument to earth fixed coordinates transformation
  matrix - bt
*/
   inst2e(roll,pitch,yaw,b,bt);
   }
return 0;
}