//
// MSGTnav.java
//
/*
This source file is part of the edu.wisc.ssec.mcidas package and is
Copyright (C) 1998 - 2017 by Tom Whittaker, Tommy Jasmin, Tom Rink,
Don Murray, James Kelly, Bill Hibbard, Dave Glowacki, Curtis Rueden
and others.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
*/
package edu.wisc.ssec.mcidas;
/**
* Navigation class for MSG type nav. This code was modified
* from the original FORTRAN code (nvxmsgt.dlm) on the McIDAS system. It
* only supports latitude/longitude to line/element transformations (LL)
* and vice/versa. Transform to 'XYZ' not implemented.
* @see <A HREF="http://www.ssec.wisc.edu/mcidas/doc/prog_man.html">
* McIDAS Programmer's Manual</A>
*
* @author Tom Whittaker
*/
public final class MSGTnav extends AREAnav
{
private boolean isEastPositive = true;
final double NOMORB=42164.; // nominal radial distance of satellite (km)
final double EARTH_RADIUS=6378.155; // earth equatorial radius (km)
int itype;
double h;
double a;
double rp;
double cdr;
double crd;
double rs, yk;
double deltax;
double deltay;
int LOFF, COFF, LFAC, CFAC;
int[] ioff = new int[3];
boolean first = true;
int count = 0;
double sublon = 0.0;
/**
* Set up for the real math work. Must pass in the int array
* of the MSGT nav 'codicil'.
*
* @param iparms the nav block from the image file
* @throws IllegalArgumentException
* if the nav block is not a MSGT type.
*/
public MSGTnav (int[] iparms) throws IllegalArgumentException {
if (iparms[0] != MSGT )
throw new IllegalArgumentException("Invalid navigation type" +
iparms[0]);
itype = 2;
LOFF = iparms[1];
COFF = iparms[2];
LFAC = iparms[3];
CFAC = iparms[4];
sublon = iparms[5]/10000.;
if (isEastPositive) sublon = -sublon;
h = NOMORB - EARTH_RADIUS;
rs = EARTH_RADIUS + h;
yk = rs/EARTH_RADIUS;
a = 1./297.;
rp = EARTH_RADIUS / (1. + a);
crd = 180. / Math.PI;
cdr = Math.PI / 180.;
deltax = 1.0/(CFAC/1000000.);
deltay = 1.0/(LFAC/1000000.);
}
/** converts from satellite coordinates to latitude/longitude
*
* @param linele array of line/element pairs. Where
* linele[indexLine][] is a 'line' and
* linele[indexEle][] is an element. These are in
* 'file' coordinates (not "image" coordinates.)
*
* @return latlon[][] array of lat/long pairs. Output array is
* latlon[indexLat][] of latitudes and
* latlon[indexLon][] of longitudes.
*
*/
public double[][] toLatLon(double[][] linele) {
int number = linele[0].length;
// Convert array to Image coordinates for computations
double[][] imglinele = areaCoordToImageCoord(linele);
double[][] latlon = imglinele;
double xlin, xele, xr, yr, tanx, tany, v1, v2;
double vmu, xt, yt, zt, teta, xlat, xlon;
for (int point=0; point < number; point++)
{
if (Double.isNaN(imglinele[indexLine][point]) ||
Double.isNaN(imglinele[indexEle][point])) {
continue;
}
xlin = 11136.0 - imglinele[indexLine][point] + 1.0;
xele = 11136.0 - imglinele[indexEle][point] + 1.0;
xlin = (xlin + 2.0) / 3.0;
xele = (xele + 2.0) / 3.0;
xr = xele - (COFF/10.);
yr = xlin - (LOFF/10.);
xr = xr*deltax*cdr;
yr = yr*deltay*cdr;
tanx = Math.tan(xr);
tany = Math.tan(yr);
v1 = 1. + tanx*tanx;
v2 = 1. + (tany*tany)*((1.+a)*(1.+a));
if (v1*v2 > ((yk*yk)/(yk*yk-1))) {
xlat = Double.NaN;
xlon = Double.NaN;
} else {
vmu = (rs - EARTH_RADIUS*Math.sqrt(yk*yk-(yk*yk-1)*v1*v2))/(v1*v2);
xt = rs - vmu;
yt = - vmu*tanx;
zt = vmu * tany/Math.cos(xr);
teta = Math.asin(zt/rp);
xlat = Math.atan(Math.tan(teta)*EARTH_RADIUS/rp) * crd;
xlon = Math.atan(yt/xt) * crd;
}
// put longitude into East Positive (form)
xlon = xlon + sublon;
if (!isEastPositive) xlon = -xlon;
latlon[indexLat][point] = xlat;
latlon[indexLon][point] = xlon;
} // end point for loop
return latlon;
}
/**
* toLinEle converts lat/long to satellite line/element
*
* @param latlon array of lat/long pairs. Where latlon[indexLat][]
* are latitudes and latlon[indexLon][] are longitudes.
*
* @return linele[][] array of line/element pairs. Where
* linele[indexLine][] is a line and linele[indexEle][]
* is an element. These are in 'file' coordinates
* (not "image" coordinates);
*/
public double[][] toLinEle(double[][] latlon) {
int number = latlon[0].length;
double[][] linele = new double[2][number];
double xfi, xla, rom, y, r1, r2, teta, xt, yt, zt;
double px, py, xr, yr, xele, xlin;
double xlat, xlon;
if (first) {
//System.out.println("#### first time...");
first = false;
}
for (int point=0; point < number; point++)
{
if (Double.isNaN(latlon[indexLat][point]) ||
Double.isNaN(latlon[indexLon][point])) {
linele[indexLine][point] = Double.NaN;
linele[indexEle][point] = Double.NaN;
continue;
}
xlat = latlon[indexLat][point];
// expects positive East Longitude.
xlon = isEastPositive
? latlon[indexLon][point]
: -latlon[indexLon][point];
xlon = xlon - sublon;
xfi = xlat*cdr;
xla = xlon*cdr;
rom = EARTH_RADIUS*rp/Math.sqrt(rp*rp*Math.cos(xfi) *
Math.cos(xfi)+EARTH_RADIUS*EARTH_RADIUS *
Math.sin(xfi)*Math.sin(xfi));
y = Math.sqrt(h*h + rom*rom - 2.*h*rom*Math.cos(xfi)*Math.cos(xla));
r1 = y*y + rom*rom;
r2 = h*h;
if (r1 > r2) {
xlin = Double.NaN;
xele = Double.NaN;
linele[indexLine][point] = Double.NaN;
linele[indexEle][point] = Double.NaN;
} else {
teta = Math.atan((rp/EARTH_RADIUS) * Math.tan(xfi));
xt = EARTH_RADIUS * Math.cos(teta) * Math.cos(xla);
yt = EARTH_RADIUS * Math.cos(teta) * Math.sin(xla);
zt = rp * Math.sin(teta);
px = Math.atan(yt/(xt-rs));
py = Math.atan(-zt/(xt-rs)*Math.cos(px));
px = px*crd;
py = py*crd;
xr = px/deltax;
yr = py/deltay;
xele = (COFF/10.) + xr;
xlin = (LOFF/10.) + yr;
xlin = xlin * 3.0 - 2.0;
xele = xele * 3.0 - 2.0;
linele[indexLine][point] = 11136.0 - xlin + 1.0;
linele[indexEle][point] = 11136.0 - xele + 1.0;
} // end calculations
} // end point loop
// Return in 'File' coordinates
return imageCoordToAreaCoord(linele, linele);
}
/** converts from satellite coordinates to latitude/longitude
*
* @param linele array of line/element pairs. Where
* linele[indexLine][] is a 'line' and
* linele[indexEle][] is an element. These are in
* 'file' coordinates (not "image" coordinates.)
*
* @return latlon[][] array of lat/long pairs. Output array is
* latlon[indexLat][] of latitudes and
* latlon[indexLon][] of longitudes.
*
*/
public float[][] toLatLon(float[][] linele) {
int number = linele[0].length;
// Convert array to Image coordinates for computations
float[][] imglinele = areaCoordToImageCoord(linele);
float[][] latlon = imglinele;
double xlin, xele, xr, yr, tanx, tany, v1, v2;
double vmu, xt, yt, zt, teta, xlat, xlon;
for (int point=0; point < number; point++)
{
if (Float.isNaN(imglinele[indexLine][point]) ||
Float.isNaN(imglinele[indexEle][point])) {
continue;
}
xlin = 11136.0 - imglinele[indexLine][point] + 1.0;
xele = 11136.0 - imglinele[indexEle][point] + 1.0;
xlin = (xlin + 2.0) / 3.0;
xele = (xele + 2.0) / 3.0;
xr = xele - (COFF/10.);
yr = xlin - (LOFF/10.);
xr = xr*deltax*cdr;
yr = yr*deltay*cdr;
tanx = Math.tan(xr);
tany = Math.tan(yr);
v1 = 1. + tanx*tanx;
v2 = 1. + (tany*tany)*((1.+a)*(1.+a));
if (v1*v2 > ((yk*yk)/(yk*yk-1))) {
xlat = Float.NaN;
xlon = Float.NaN;
} else {
vmu = (rs - EARTH_RADIUS*Math.sqrt(yk*yk-(yk*yk-1)*v1*v2))/(v1*v2);
xt = rs - vmu;
yt = - vmu*tanx;
zt = vmu * tany/Math.cos(xr);
teta = Math.asin(zt/rp);
xlat = Math.atan(Math.tan(teta)*EARTH_RADIUS/rp) * crd;
xlon = Math.atan(yt/xt) * crd;
}
// put longitude into East Positive (form)
xlon = xlon + sublon;
if (!isEastPositive) xlon = -xlon;
latlon[indexLat][point] = (float) xlat;
latlon[indexLon][point] = (float) xlon;
} // end point for loop
return latlon;
}
/**
* toLinEle converts lat/long to satellite line/element
*
* @param latlon array of lat/long pairs. Where latlon[indexLat][]
* are latitudes and latlon[indexLon][] are longitudes.
*
* @return linele[][] array of line/element pairs. Where
* linele[indexLine][] is a line and linele[indexEle][]
* is an element. These are in 'file' coordinates
* (not "image" coordinates);
*/
public float[][] toLinEle(float[][] latlon) {
int number = latlon[0].length;
float[][] linele = new float[2][number];
double xfi, xla, rom, y, r1, r2, teta, xt, yt, zt;
double px, py, xr, yr, xele, xlin;
double xlat, xlon;
if (first) {
//System.out.println("#### first time...");
first = false;
}
for (int point=0; point < number; point++)
{
if (Float.isNaN(latlon[indexLat][point]) ||
Float.isNaN(latlon[indexLon][point])) {
linele[indexLine][point] = Float.NaN;
linele[indexEle][point] = Float.NaN;
continue;
}
xlat = latlon[indexLat][point];
// expects positive East Longitude.
xlon = isEastPositive
? latlon[indexLon][point]
: -latlon[indexLon][point];
xlon = xlon - sublon;
xfi = xlat*cdr;
xla = xlon*cdr;
rom = EARTH_RADIUS*rp/Math.sqrt(rp*rp*Math.cos(xfi) *
Math.cos(xfi)+EARTH_RADIUS*EARTH_RADIUS *
Math.sin(xfi)*Math.sin(xfi));
y = Math.sqrt(h*h + rom*rom - 2.*h*rom*Math.cos(xfi)*Math.cos(xla));
r1 = y*y + rom*rom;
r2 = h*h;
if (r1 > r2) {
xlin = Float.NaN;
xele = Float.NaN;
linele[indexLine][point] = Float.NaN;
linele[indexEle][point] = Float.NaN;
} else {
teta = Math.atan((rp/EARTH_RADIUS) * Math.tan(xfi));
xt = EARTH_RADIUS * Math.cos(teta) * Math.cos(xla);
yt = EARTH_RADIUS * Math.cos(teta) * Math.sin(xla);
zt = rp * Math.sin(teta);
px = Math.atan(yt/(xt-rs));
py = Math.atan(-zt/(xt-rs)*Math.cos(px));
px = px*crd;
py = py*crd;
xr = px/deltax;
yr = py/deltay;
xele = (COFF/10.) + xr;
xlin = (LOFF/10.) + yr;
xlin = xlin * 3.0 - 2.0;
xele = xele * 3.0 - 2.0;
linele[indexLine][point] = (float)(11136.0 - xlin + 1.0);
linele[indexEle][point] = (float)(11136.0 - xele + 1.0);
} // end calculations
} // end point loop
// Return in 'File' coordinates
return imageCoordToAreaCoord(linele, linele);
}
}