from math import floor, pi, sin, cos, tan, asin, acos, atan, atan2, sqrt
#from orbit_base import OrbitBase
from orbitsim.orbit_base import OrbitBase
import numpy as np
import os
from sys import exit
import time as tm
from orbitsim.tools import mjd2utc
from netCDF4 import default_fillvals, Dataset

# Constants.
fillvalue = default_fillvals["f8"]
fillvalue_int = default_fillvals["i2"]
degrees = pi / 180.0
GmE = 398600.4418 # [km^3 / s^s]. Universal graviational constant times mass of Earth. Divide this by the distance squared and yet the acceleration.
r_earth = 6371.0 # [km] Radius of the Earth.
seconds_day = 86400.0

class Orbit_sim(OrbitBase):
    """Orbit_sim is child class of OrbitBase """

    def __init__(self, name, inclination=None, t_node=None, date=None,
            trev=None, altitude=None, longitude_eq=None, phase_start=None,
            phase_end=None, tsamp=None, nrow=None, polyterms_alpha=None,
            polyterms_beta=None
        ):
        self.inclination = inclination
        self.t_node = t_node
        self.date = date
        self.trev = trev
        self.altitude = altitude
        self.longitude_eq = longitude_eq
        self.phase_start = phase_start
        self.phase_end = phase_end
        self.tsamp = tsamp
        self.nnrow = nrow
        self.polyterms_alpha = polyterms_alpha
        self.polyterms_beta = polyterms_beta

        super(self.__class__, self).__init__(name)


    @classmethod
    def from_file(cls, filename, name=None):
        if name is None:
            name = os.path.splitext(os.path.basename(filename))[0]

        self = cls(name)
        self.read_file(filename)
        return self


    def read_file(self,filename):

        inputfile = open(filename,'r')
        # This is a dirty text file with header lines. We have to improve on this interface.

        inputfile.readline()
        self.inclination = float(inputfile.readline()) * degrees # Inclination angle converted from degrees to to radians.
        inputfile.readline()
        self.t_node = float(inputfile.readline()) # Local solar time in hours when the satellite is at the equator and flies northwards.
        inputfile.readline()
        self.date = [int(v) for v in inputfile.readline().split()] # Space-separated integers for year, month and day.
        inputfile.readline()
        gravityflag = int(inputfile.readline()) # Switch between providing revolution time and calculating altitude or providing altitude and calculating revolution time.

        inputfile.readline()
        if gravityflag == 0:
            self.trev = float(inputfile.readline()) / 24.0 # Time of one revolution converted from hours to days.
            # Low of gravity on way around.
            self.altitude = GmE**(1.0/3.0) * (seconds_day*self.trev / (2.0*pi))**(2.0/3.0) # Altitude with respect to the Earth center.
        elif gravityflag == 1:
            self.altitude = float(inputfile.readline()) + r_earth # Satellite altitude converted to distance from the Earth center.
            # Low of gravity the other way around.
            self.trev = 2.0*pi * sqrt(self.altitude**3.0 / GmE) / seconds_day # Altitude with respect to the Earth center.
        else:
            print ("Error: Revolution time or altitude flag should be 0 or 1.")
            exit()
        inputfile.readline()
        self.longitude_eq = float(inputfile.readline()) * degrees # Longitude at daytime equator pass converted to radians.
        inputfile.readline()
        self.phase_start = float(inputfile.readline()) # Starting phase: 0.0: last night-time equator pass. 0.25: Closest to first pole. 0.5: Daytime equator pass. 0.75: Closest to second other. 1.0 Next night-time equator pass.
        inputfile.readline()
        self.phase_end = float(inputfile.readline()) # Ending phase: 0.0: last night-time equator pass. 0.25: Closest to first pole. 0.5: Daytime equator pass. 0.75: Closest to second other. 1.0 Next night-time equator pass.
        inputfile.readline()
        self.tsamp = float(inputfile.readline()) # Time for each sample in seconds.
        inputfile.readline()
        self.nrow = int(inputfile.readline()) # Number of detector rows. That is the number of observations in one swath.
        inputfile.readline()
        self.polyterms_alpha = [float(s)*degrees for s in inputfile.readline().split()] # Polynomial expression of viewing angle from satellite point of view perpendicular to flight direction. Converted from degrees to radians.
        inputfile.readline()
        self.polyterms_beta = [float(s)*degrees for s in inputfile.readline().split()] # Polynomial expression of viewing angle from satellite point of view parallel to flight direction. Converted from degrees to radians.
        # The polynomial expressions can have any number of numbers. It starts with the zeroth order
        # term and it extends as far as input is given. The input parameter (x) of the polynomial
        # goes from -1 to 1 through the swath. The polynomial basis set is x^n.
        inputfile.close()



    def calculate_revolution(self):
        inclination = self.inclination
        t_node = self.t_node
        date = self.date
        trev = self.trev
        altitude = self.altitude
        longitude_eq = self.longitude_eq
        phase_start = self.phase_start
        phase_end = self.phase_end
        tsamp = self.tsamp
        nrow = self.nrow
        polyterms_alpha = self.polyterms_alpha
        polyterms_beta = self.polyterms_beta


        # Check legal content.
        assert phase_end > phase_start,"Ending phase must be larger than staring phase. This looks like a stationary orbit or an orbit in reverse. We will implement single-swath approach later on."
        # TODO: Implement scanning mode, with times dependent on row.
        # If the local solar time at ascending node is in the night. We let it cross the same
        # position in southward direction with twelve hours offset. So the equator crossing point
        # is always in daytime.
        if ((t_node-6.0) % 24.0) / 12.0 > 1.0:
            t_node = (t_node + 12.0) % 24.0
            inclination *= -1.0

        # Calculate number of scanlines.
        nscanline = int((phase_end-phase_start)*seconds_day*trev/tsamp) + 1
        # Now perform all calculations from the technote, not understanding the meaning of
        # all the different intermediate results.

        # Satellite height from center of the Earth in units of Earth radii.
        h = altitude / r_earth

        # UTC time of equator pass. The time is in hours, and one hour corresponds to 15 degrees.
        t_equator = t_node - longitude_eq/(15.0*degrees)

        # Construct arrays of sub-satellite points equidistant in UTC time.
        longitudes_scanlines = np.zeros(nscanline,dtype=np.float64)
        latitudes_scanlines = np.zeros(nscanline,dtype=np.float64)
        times_scanlines = np.zeros(nscanline,dtype=np.float64)
        thetas_scanlines = np.zeros(nscanline,dtype=np.float64)
        # We define the orbit phase from -pi to pi as the angle that the satellite has rotated
        # around the Earth with respect to the equator-crossing point as defined in the input.
        for iscanline in range(nscanline):


            # The phase starts at phase_start and adds tsamp / trev of one full rotation, but
            # tsamp is in seconds and trev is in days (converted from hours input).
            # Meanwhile, we convert the domain [0..1] to [-180 degrees ... +180 degrees]
            phase = (360.0*(phase_start + iscanline*tsamp / (trev*seconds_day)) - 180.0) * degrees

            # We start at the equator crossing UTC time t_equator and we add the time that the
            # satellite takes to reach the phase. If the phase is 360 higher, the time is trev
            # longer. But trev is in days and the desired unit is hours, so we have to add
            # 24*trev to the time is the phase is 360 degrees.
            times_scanlines[iscanline] = t_equator + phase*trev/(15.0*degrees)

            # The rotation is at inclination i and we rotate an angle (between -pi and pi).
            # Our reference point is at the equator-crossing point at zero latitude, but we will
            # also take zero longitude, because we can just shift the longitudes at the end.
            # The Z is to the north, The Y is to the east and X is towards us, so
            # position = [1,0,0].
            # We define lowercase c and s and cosine and sine of the half the phase angle
            # and uppercase C and S as the cosine and sine of the inclination.
            # The rotation quaternion is [c,0,-sS,sC].
            # with conjugate [c,0,sS,-sC].
            # p = [0,1,0,0]
            # q' = [c,0,sS,-sC]
            # pq' = [0,c,sC,sS]
            # Next calculation:
            # q =   [c,0,-sS,sC]
            # pq' = [0,c,sC,sS]
            # qpq' = [0,c^2-s^2,2scC,2scS]
            # Now use phase angle p (phase) and inclination angle i and write out the full sines.
            # using sin(p) = 2sc and cos(p) = c^2-s^2.
            # Result = [cos(p),sin(p)*cos(i),sin(p)*sin(i)]
            # The sine of the latitude is the last component, so we have
            # sin(lat) = sin(p)*sin(i), so
            # sin(p) = sin(lat) / sin(i).
            latitudes_scanlines[iscanline] = asin(sin(phase)*sin(inclination))

            # The longitude is the arctangent of Y divided by X. Afterwards, it must be corrected
            # down with the amount of rotation that the Earth has done in the time to get to this
            # point (phase times the revolution time in days.), and the shift of the equator longitude has to be applied.
            longitudes_scanlines[iscanline] = atan2(sin(phase)*cos(inclination),cos(phase)) - phase*trev + longitude_eq

            # Now, we want to calculate the angle of the direction in which the satellite is
            # pointing with respect to the northward vector from the sub-satellite point. The
            # direction in which the satellite is pointing is the derivative of its position
            # with respect to the phase.
            # [-sin(p),cos(p)*cos(i),cos(p)*sin(i)]
            # And the local north is the derivative of the sub-satellite point with respect to
            # the latitude (with constant longitude).
            # The point is [cos(lon)*cos(lat),sin(lon)*cos(lat),sin(lat)]
            # And the derivative with latitude is [-cos(lon)*sin(lat),-sin(lon)*cos(lat),cos(lat)]
            # Now, we take the derivative divided by point itself, so we can multiply it again
            # with the point in the old perspective.
            # north/point = [-sin(lat)/cos(lat),-sin(lat)/cos(lat),cos(lat)/sin(lat)]
            # We will split off 1/cos(lat), therefore, we did not use tan(lat).
            # north/point = 1/cos(lat) * [-sin(lat),-sin(lat),cos^2(lat)/sin(lat)]
            # And multiply with the point, using whichever perspective is desired.
            # north = 1/cos(lat) * [-sin(lat)*cos(p),-sin(lat)*sin(p)*cos(i),cos^2(lat)]
            # For the Z, we kept the sin(lat) for the point. Now, we set cos^2(lat) equal
            # to 1-sin^2(lat) and replace sin(lat) with sin(p)*sin(i), but keep the 1/cos(lat)
            # in front as it is.
            # North = 1/cos(lat) * [-sin(p)*sin(i)*cos(p),-sin^2(p)*sin(i)*cos(i),1-sin^2(p)*sin^2(i)]
            # We will temporarily drop 1/cos(lat), replace the sine and cosine of p (phase) with
            # the lowercase s and c and replace the sine and cosine of the inclination with the
            # uppercase S and C. And we no longer use squares, but just concatenate same sines
            # or cosines.
            # Direction = [-s,cC,cS]
            # North = [-sSc,-ssSC,1-ssSS]
            # We calculate the cross product of these to get the sine of the angle. That is
            # [cC-cCssSS+cSssSC,
            #  -cSsSc+s-sssSS,
            #  sssSC+cCsSc]
            # =
            # [cC,sCC,sSC]
            # And this must be in line with the zenith, because both the pointing direction
            # and the north are perpendicular to the zenith, so their inner product must on on
            # the zenith. More precisely, it is C times the zenith, so the inner product is C.
            # So, we can return the 1/cos(lat), so we know that
            # sin(theta) = cos(i) / cos(lat)
            # The only thing is, that the angle may be moved to another quadrant. We calculated
            # the sine with a cross product. If the inner product is negative, the angle must
            # be 180 degrees minus the angle.
            # So, calculate the inner product, that is the cosine.
            # Direction = [-s,cC,cS]
            # North = [-sSc,-ssSC,1-ssSS]
            # Result = ssSc-cCssSC+cS-cSssSS = ssSc-cssS+cS = cS
            # which we will have to divide by the cosine of the latitude for the cosine of theta.
            # Now, we have the sine and the cosine, we will use the two-argument arctan,
            # so we are protected from rounding issues. And we can ignore the cosine of
            # the latitude again.
            thetas_scanlines[iscanline] = atan2(cos(inclination),cos(phase)*sin(inclination))
            # The technote adds a term -trev * cos^2(lat) in the numerator. Then, we take
            # the line of sub-satellite points and not the satellite's nose. We think we need
            # the satellite's nose.

        # Repeat joke for the half scanlines, needed for the corners, but we can drop the
        # the times.
        longitudes_scanlines_corners = np.zeros(nscanline+1,dtype=np.float64)
        latitudes_scanlines_corners = np.zeros(nscanline+1,dtype=np.float64)
        thetas_scanlines_corners = np.zeros(nscanline+1,dtype=np.float64)
        for iscanline in range(nscanline+1):
            phase = (360.0*(phase_start + (iscanline-0.5)*tsamp / (trev*seconds_day)) - 180.0) * degrees
            latitudes_scanlines_corners[iscanline] = asin(sin(phase)*sin(inclination))
            longitudes_scanlines_corners[iscanline] = atan2(sin(phase)*cos(inclination),cos(phase)) - phase*trev + longitude_eq
            thetas_scanlines_corners[iscanline] = atan2(cos(inclination),cos(phase)*sin(inclination))

        # For each scanline, calculate the viewed latitudes and longitudes per row.
        # We reverse the loops, because we can recycle row-specific things that are independent
        # of the scanline.
        longitudes_pixel = np.zeros((nscanline,nrow),dtype=np.float64)
        latitudes_pixel = np.zeros((nscanline,nrow),dtype=np.float64)
        vza_pixel = np.zeros(nrow,dtype=np.float64) # Does not depend on scanline for perfectly spherical Earth.
        vaa_pixel = np.zeros((nscanline,nrow),dtype=np.float64)
        sza_pixel = np.zeros((nscanline,nrow),dtype=np.float64)
        saa_pixel = np.zeros((nscanline,nrow),dtype=np.float64)
        dead_rows = np.zeros(nrow,np.bool)
        for irow in range(nrow):
            if nrow == 1: q = 0.0
            else: q = 2.0 * float(irow) / float(nrow-1) - 1.0
            alpha = 0.0
            for order in range(len(polyterms_alpha)):
                alpha += polyterms_alpha[order] * q**order
            beta = 0.0
            for order in range(len(polyterms_beta)):
                beta += polyterms_beta[order] * q**order
            # Note that alpha and beta are the angles of the projected viewing line with
            # the actual viewing line, not the projected viewing lines with the zeniths,
            # This means that alpha and beta together can never exceed ninety degrees.

            # Section 3.1.2.

            # Assert that the angles are well defined. Because of the definition of the
            # angles, they cannot add up to more than ninety degrees. On the other hands,
            # the alpha and beta are independently defined as a polynomial. If they add up
            # to ninety or more degrees, it will result into a floating point exception,
            # so we have to catch it and write a dead row.
            if cos(beta)**2.0 - sin(alpha)**2.0 <= 0.0: dead_rows[irow] = True
            if dead_rows[irow]: continue # And get away from the floating-point danger.

            # Points on the projection plane. This is a plane originating at the sub-satellite
            # point and extending perpendicular to the line from this point to the satellite.
            # The xi is perpendicular to the flight direction, the eta is along the flight
            # direction.
            xi = (h-1.0) * sin(alpha) / sqrt(cos(beta)**2.0 - sin(alpha)**2.0)
            eta = (h-1.0) * sin(beta) / sqrt(cos(beta)**2.0 - sin(alpha)**2.0)
            dist = sqrt(xi**2.0+eta**2.0) # Distance between sub-satellite point and projection point.
            # 1.  Draw the Earth from center (C) with radius 1 and put satellite (S) at distnace
            #     h from (C) with h>1.
            # 2.  Put critical viewed point (V) on the Earth surface such that angle CVS is
            #     ninety degrees.
            # 3.  Put the sub-satellite point on line CS on the Earth (O).
            # 4.  Draw line from O perpendicular to CS until it crosses SV at projection point (R).
            # 5.  From triagle SCV, is can be deduced that cos(SCV) = 1/h.
            # 6.  Thus sin(SCV) = sqrt(1-1/h^2) and tan(SCV) = sqrt(h^2-1).
            # 7.  Consider triangle SOR, its ORS angle is the same as SCV.
            # 8.  The distance SO = h-1.
            # 9.  The dist is the distance RO and that is (h-1) / tan(ORS) = (h-1) / sqrt(h^2-1).
            # 10. This reduces to sqrt((h-1)/(h+1)).
            dead_rows[irow] = dist > sqrt((h-1.0)/(h+1.0))
            if dead_rows[irow]: continue
            if dist == 0.0: # Exactly nadir, protection from zero division.
                vza_pixel[irow] = 0.0 # This is nadir.
                for iscanline in range(nscanline):
                    latitudes_pixel[iscanline,irow] = latitudes_scanlines[iscanline]
                    longitudes_pixel[iscanline,irow] = longitudes_scanlines[iscanline]
                    vaa_pixel[iscanline,irow] = 0.0 # Undefined for nadir.
            else:
                # Now, we are not in the critical position. So we have our view point (V)
                # generally on a position somewhere between the critical (V) and (O).
                # The following formula can be proven with the following protocol.
                #  1. Create the same drawing, but put point (V) on the Earth so that
                #     angle CVS is larger than ninety degrees.
                #  2. Put the sub-satellite point on line CS on the Earth (O).
                #  3. Draw line from O perpendicular to CS until it crosses SV at projection point (R).
                #  4. Draw from (V) a line perpendicular to CS until it crosses that line inside
                #     the Earth at (E).
                #  5. Draw a line from (V) perpendicular to SV until it crosses CS at point (X).
                #  6. Consider angle ORS. It has a right angle at (O) and OR = dist and SO = h-1.
                #     Therefore, we have sin(OSR) = dist / sqrt(dist^2 + (h-1)^2)
                #     and cos(OSR) = (h-1) / sqrt(dist^2 + (h-1)^2)
                #     and tan(OSR) = dist / (h-1)
                #  7. We want angle SCV, its sine is equal to distance EV, because CV = 1.
                #  8. Because angle EVX is equal to OSR,
                #     we have SX = sin(SCV) * (1/tan(OSR) + tan(OSR))
                #  9. And CX = h - SX
                # 10. Now go to the big triangle SCV. The sine rule says that
                #     sin(CVS) / sin(CSV) = h, because SC = h and CV = 1.
                # 11. And CSV = OSR, thus sin(CVS) = h*dist / sqrt(dist^2 + (h-1)^2)
                # 12. Now go to triangle VXC. Angle XVC is ninety degrees smaller than
                #     angle CVS, so cos(XVC) = sin(CVS) = h*dist / sqrt(dist^2 + (h-1)^2)
                # 13. And angle VXC is known, namely ninety degrees larger than OSR.
                # 14. Our required angle is equal to XCV and can be calculated by subtracting
                #     angles VXC and XVC from 180 degrees. That works. But using distance CX
                #     saves reduces the number of sine functions to one asin.
                # 15. sin(VXC) = cos(OSR), because they differ ninety degrees.
                # 15. Construct sin(XVC) = sqrt(1-cos(XVC))
                # 16. Use sine rule: CX = sin(XVC) / sin(VXC)
                # 17. Return to formulas:
                #     sin(XVC) = sqrt(1 - (h^2*dist^2) / (dist^2 + (h-1)^2))
                #              = sqrt((dist^2 + (h-1)^2 - h^2*dist^2) / (dist^2 + (h-1)^2))
                #     sin(VXC) = (h-1) / sqrt(dist^2 + (h-1)^2)
                #     CX = sin(XVC) / sin(VXC) = sqrt(dist^2 + (h-1)^2 - h^2*dist^2) / (h-1)
                #     , removing the common denominator.
                #     Put the new denominator into the root sign.
                #     CX = sqrt((dist^2 + (h-1)^2 - h^2*dist^2) / (h-1)^2)
                #        = sqrt(1 - dist^2*(h^2-1) / (h-1)^2)
                #        = sqrt(1 - dist^2*(h+1) / (h-1))
                # 18. Now reverse the first deduction of CS, from step 9 back.
                #     SX = h - CX
                #     sin(SCV) = SX / (1/tan(OSR) + tan(OSR))
                #     sin(SCV) = (h - sqrt(1 - dist^2*(h+1) / (h-1))) / ((h-1)/d + d/(h-1))
                #     SCV = asin((h - sqrt(1 - dist^2*(h+1) / (h-1))) / ((h-1)/d + d/(h-1)))
                SCV = asin((h - sqrt(1.0 - dist**2.0*(h+1.0)/(h-1.0))) / ((h-1.0)/dist + dist/(h-1.0)))
                # Viewing zenith angle. This is the intermediate results CVS. Though
                # CVS is larger than ninety degrees and the viewing zenith angle is less
                # than ninety degrees, they share the sine.
                vza_pixel[irow] = asin(h*dist / sqrt(dist**2.0 + (h-1.0)**2.0))
                # This corresponds to formula 47 from the technote.
                # The viewing zenith angle is not dependent on the scanline, only on the row.
                # This is because our Earth is a sphere, not an egg.
                for iscanline in range(nscanline):
                    # Turn projection plane, and define x to the east and y to the north.
                    x = xi*cos(thetas_scanlines[iscanline]) + eta*sin(thetas_scanlines[iscanline])
                    y = -xi*sin(thetas_scanlines[iscanline]) + eta*cos(thetas_scanlines[iscanline])
                    # We take point at a latitude and longitude 0 as sub-satellite point,
                    # coordinate [C,0,S], where S is the sine of the latitude and C is the
                    # cosine of the latitude. Now, we define lowercase s and c as sine and cosine
                    # of half the angle SCV. The rotation axis of X-rotation is [-S,0,C] and
                    # on y-rotation is [0,-1,0], so we get the following quaternion rotation.
                    # where x/dist is taken as x and y/dist is taken as y, so x^2+y^2 = 1.
                    # q  = [c,-xsS,-ys,xsC]
                    # p  = [0,C,0,S]
                    # q' = [c,xsS,ys,-xsC]
                    # pq' = [-xsSC+xsSC,Cc-ysS,xCCs+xSSs,cS+ysC]
                    # pq' = [0,Cc-ysS,xs,cS+ysC]

                    # q   = [c,-xsS  ,-ys,xsC]
                    # pq' = [0,Cc-ysS,xs,cS+ysC]

                    # qpq' = [xscSC-xyssSS+xyss-xscSC-xyssCC,
                    #         ccC-yscS-yscS-yyssC-xxssC,
                    #         xsc+xscCC-xyssSC+xscSS+xyssSC,
                    #         ccS+yscC-xxssS+yscC-yyssS]
                    #      = [0,
                    #         (cc-ss)*C-2sc*yS,
                    #         2sc*x,
                    #         (cc-ss)*S+2sc*yC]
                    # cos(SCV) = cc - ss and sin(SCV) = 2*sc
                    #      = [0,
                    #         C*cos(SCV)-yS*sin(SCV),
                    #         x*sin(SCV),
                    #         S*cos(SCV) + yC*sin(SCV)]
                    # And that is now [0,cos(lat)*cos(lon),sin(lon)*cos(lat),sin(lat)]
                    # sin(lat) = S*cos(SCV) + yC*sin(SCV)
                    # tan(lon) = x*sin(SCV) / (C*cos(SCV) - yS*sin(SCV))
                    # And that is exactly formula 22 and 23 from the technote.
                    # Plus an eventual offset longitude.
                    latitudes_pixel[iscanline,irow] = asin(cos(SCV)*sin(latitudes_scanlines[iscanline]) + (y*sin(SCV)*cos(latitudes_scanlines[iscanline]))/dist)
                    longitudes_pixel[iscanline,irow] = longitudes_scanlines[iscanline] + atan2(x*sin(SCV),dist*cos(latitudes_scanlines[iscanline])*cos(SCV) - y*sin(latitudes_scanlines[iscanline])*sin(SCV))

                    # For the azimuth, we have to let go the protocol, because it is no longer
                    # in the right plane. The x and y were defined according to the perspective
                    # of the sub-satellite point and we need the perspective of the viewed point.
                    # The azimuth angle is not reciprocal. Considere someone sitting at the equator
                    # and the other one metre left of the north pole. That one is north
                    # of the first person, but the other way around, it is east.

                    # If we put the longitude of the viewed poisiton at zero.
                    # Define Sv = sin(lat_viewed), Cv = cos(lat_viewed), Ss = sin(lat_subsat),
                    # Cs = cos(lat_subsat), Sl = sin(lon_ss-lon_v), Cl = cos(lon_s-lon_v)
                    # The viewed location at [Cv,0,Sv]
                    # The subsatellite locaiton is at [Cl*Cs,Sl*Cs,Ss]
                    # North is at direction [-Sv,0,Cv] (azimuth = 0)
                    # East is at direction [0,1,0] (azimuth = 90)
                    # The azimuth is defined as a two-argument arctan. The numerator must be
                    # East and the denominator must be north. And it is about subsatellite minus
                    # viewed.
                    # Projection: subsatellite East of viewed location: Sl*Cs
                    # Projection: subsatellite north of viewed location: -Cl*Cs*Sv+Sv*Cv+Ss*Cv-Sv*Cv
                    # And that is Ss*Cv-Cl*Cs*Sv.
                    # And we have the Arctan from formula 48.
                    vaa_pixel[iscanline,irow] = atan2(sin(longitudes_scanlines[iscanline]-longitudes_pixel[iscanline,irow])*cos(latitudes_scanlines[iscanline]) , sin(latitudes_scanlines[iscanline])*cos(latitudes_pixel[iscanline,irow]) - cos(longitudes_scanlines[iscanline]-longitudes_pixel[iscanline,irow])*cos(latitudes_scanlines[iscanline])*sin(latitudes_pixel[iscanline,irow]))

        # And calculate the longitudes and the latitudes of the pixels from row -0.5 to nrow-0.5.
        dead_halfrows = np.zeros(nrow+1,dtype=np.bool)
        longitudes_pixel_corners = np.zeros((nscanline+1,nrow+1),dtype=np.float64)
        latitudes_pixel_corners = np.zeros((nscanline+1,nrow+1),dtype=np.float64)
        for irow in range(nrow+1):
            if nrow == 1: q = irow - 0.5
            else: q = 2.0 * float(irow-0.5) / float(nrow-1) - 1.0
            alpha = 0.0
            for order in range(len(polyterms_alpha)):
                alpha += polyterms_alpha[order] * q**order
            beta = 0.0
            for order in range(len(polyterms_beta)):
                beta += polyterms_beta[order] * q**order
            # We will shorten the comments. We perform a subset of calculations from the main
            # rows and scanlines. Now, we just want the longitudes and latitudes. And we check
            # if a half-row is dead. Any living adjacent row will be killed then, because we
            # want well-defined corners, even if the pixel center was well defined.
            if cos(beta)**2.0 - sin(alpha)**2.0 <= 0.0: dead_halfrows[irow] = True
            if dead_halfrows[irow]: continue
            xi = (h-1.0) * sin(alpha) / sqrt(cos(beta)**2.0 - sin(alpha)**2.0)
            eta = (h-1.0) * sin(beta) / sqrt(cos(beta)**2.0 - sin(alpha)**2.0)
            dist = sqrt(xi**2.0+eta**2.0)
            dead_halfrows[irow] = dist > sqrt((h-1.0)/(h+1.0))
            if dead_halfrows[irow]: continue
            if dist == 0.0: # Exactly nadir, protection from zero division.
                for iscanline in range(nscanline+1):
                    latitudes_pixel_corners[iscanline,irow] = latitudes_scanlines_corners[iscanline]
                    longitudes_pixel_corners[iscanline,irow] = longitudes_scanlines_corners[iscanline]
            else:
                SCV = asin((h - sqrt(1.0 - (dist**2.0*(h+1.0))/(h-1.0))) / ((h-1.0)/dist + dist/(h-1.0)))
                for iscanline in range(nscanline+1):
                    x = xi*cos(thetas_scanlines_corners[iscanline]) + eta*sin(thetas_scanlines_corners[iscanline])
                    y = -xi*sin(thetas_scanlines_corners[iscanline]) + eta*cos(thetas_scanlines_corners[iscanline])
                    latitudes_pixel_corners[iscanline,irow] = asin(cos(SCV)*sin(latitudes_scanlines_corners[iscanline]) + (y*sin(SCV)*cos(latitudes_scanlines_corners[iscanline]))/dist)
                    longitudes_pixel_corners[iscanline,irow] = longitudes_scanlines_corners[iscanline] + atan2(x*sin(SCV),dist*cos(latitudes_scanlines_corners[iscanline])*cos(SCV) - y*sin(latitudes_scanlines_corners[iscanline])*sin(SCV))

        # Now, kill rows adjacent to a dead half-row, if not already dead.
        for irow in range(nrow):
            if dead_halfrows[irow] or dead_halfrows[irow+1] and not dead_rows[irow]: dead_rows[irow] = True
            # We will save ourselves the effort to remove all calculated values. The Nones were only
            # appended to make sure that the indices remain correct.
            # A living half-row will not provide corners for an adjacent dead row.

        if any(dead_rows):
            print("Warning: Some rows are dead")
        # Solar angles. This needs the time, which is only dependent on scanline, not on row.
        # So we reverse loops.
        x = 1 if date[1] <= 2 else 0
        # Julian day.
        J_day = 1721089 + date[2] + floor(-0.75*floor((date[0]-x)/100.0)) + floor(365.25*(date[0]-x)) + floor(367.0*(x+(date[1]-2.0)/12.0))
        for iscanline in range(nscanline):
            # Solar model, including several long-term perturbations on the diurnal cycle.
            # Therefore, it contains a lot of numbers.
            J = J_day + (times_scanlines[iscanline] - 12.0) / 24.0
            T = (J-2451545.0)/36525.0
            eps = (84381.448 - 46.815*T - 0.00059*T**2.0+0.001813*T**3.0) * degrees / 3600.0
            L = (280.46645 + 36000.7693*T + 0.0003032*T**2.0) * degrees
            M = (357.5291 + 35999.0503*T - 0.0001559*T**2.0 - 4.8e-7*T**3.0) * degrees
            C = ((1.9146 - 0.004817*T - 0.000014*T**2.0)*sin(M) + (0.019993 - 0.000101*T)*sin(2.0*M) + 0.00029 * sin(3.0*M)) * degrees
            lon_s = L + C
            alpha = atan2(cos(eps)*sin(lon_s),cos(lon_s))
            delta = asin(sin(eps)*sin(lon_s)) # Latitude of the sun.
            tgm = (280.46061837 + 360.98564736629*(J-2451545.0)+0.000387933*T**2.0 - T**3.0/38710000.0) * degrees
            for irow in range(nrow):
                if dead_rows[irow]: continue
                H = alpha - tgm - longitudes_pixel[iscanline,irow] # Relative longitude of the sun.
                # From now on, it is exact geometry.
                sza_pixel[iscanline,irow] = acos(sin(delta)*sin(latitudes_pixel[iscanline,irow]) + cos(delta)*cos(H)*cos(latitudes_pixel[iscanline,irow]))
                saa_pixel[iscanline,irow] = atan2(sin(H)*cos(delta),sin(delta)*cos(latitudes_pixel[iscanline,irow]) - cos(H)*cos(delta)*sin(latitudes_pixel[iscanline,irow]))

        # Put the data in member variables. This includes writing fill values for
        # dead pixels and converting units.
        nrow_living = np.sum(np.logical_not(dead_rows))
        self.nobs = nscanline*nrow_living
        rowmap = np.zeros(nrow,dtype=np.int32)
        irow_living = 0
        for irow in range(nrow):
          if dead_rows[irow]:
            rowmap[irow] = self.nobs # Will cause an error when used.
          else:
            rowmap[irow] = irow_living
            irow_living += 1
        self.ncorner = 4
        self.utc = np.zeros((self.nobs,7),dtype=np.int16)
        self.mjd = np.zeros((self.nobs),dtype=np.float64)
        self.lat_corner = np.zeros((self.nobs,self.ncorner),dtype=np.float64)
        self.lat_center = np.zeros(self.nobs,dtype=np.float64)
        self.lon_corner = np.zeros((self.nobs,self.ncorner),dtype=np.float64)
        self.lon_center = np.zeros(self.nobs,dtype=np.float64)
        self.sza = np.zeros(self.nobs,dtype=np.float64)
        self.vza = np.zeros(self.nobs,dtype=np.float64)
        self.saz = np.zeros(self.nobs,dtype=np.float64)
        self.vaz = np.zeros(self.nobs,dtype=np.float64)

        for iscanline in range(nscanline):
            mjd = (tm.mktime((date[0],date[1],date[2],0,0,0,0,0,0)) - tm.mktime((2000, 1, 1, 0, 0, 0, 0, 0, 0)))/seconds_day + times_scanlines[iscanline] / 24.0 # The times_scanlines are hours.
            self.mjd[iscanline*nrow:(iscanline+1)*nrow] = mjd
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 0] = mjd2utc(mjd)['year'] 
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 1] = mjd2utc(mjd)['month'] 
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 2] = mjd2utc(mjd)['day'] 
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 3] = mjd2utc(mjd)['hour'] 
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 4] = mjd2utc(mjd)['minute'] 
            sec = mjd2utc(mjd)['second']
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 5] = np.floor(sec)
            self.utc[iscanline*nrow:(iscanline+1)*nrow, 6] = round(1000.0*(sec%1.0)) # Millisecons.
            for irow in range(nrow):
                # MIPrep obervation index, zero-based.
                if dead_rows[irow]: continue
                iobs = iscanline*nrow_living+rowmap[irow]
                self.lat_corner[iobs,0] = latitudes_pixel_corners[iscanline,irow] / degrees
                self.lat_corner[iobs,1] = latitudes_pixel_corners[iscanline,irow+1] / degrees
                self.lat_corner[iobs,2] = latitudes_pixel_corners[iscanline+1,irow+1] / degrees
                self.lat_corner[iobs,3] = latitudes_pixel_corners[iscanline+1,irow] / degrees
                self.lat_center[iobs] = latitudes_pixel[iscanline,irow] / degrees
                self.lon_corner[iobs,0] = (longitudes_pixel_corners[iscanline,irow] / degrees + 180.0) % 360.0 - 180.0
                self.lon_corner[iobs,1] = (longitudes_pixel_corners[iscanline,irow+1] / degrees + 180.0) % 360.0 - 180.0
                self.lon_corner[iobs,2] = (longitudes_pixel_corners[iscanline+1,irow+1] / degrees + 180.0) % 360.0 - 180.0
                self.lon_corner[iobs,3] = (longitudes_pixel_corners[iscanline+1,irow] / degrees + 180.0) % 360.0 - 180.0
                self.lon_center[iobs] = (longitudes_pixel[iscanline,irow] / degrees + 180.0) % 360.0 - 180.0
                self.sza[iobs] = sza_pixel[iscanline,irow] / degrees
                self.vza[iobs] = vza_pixel[irow] / degrees
                self.saz[iobs] = saa_pixel[iscanline,irow] / degrees
                self.vaz[iobs] = vaa_pixel[iscanline,irow] / degrees

    def write_output(self,outfile):

        # Let the base orbit write the output as it should be done.
        super(Orbit_sim,self).write_output(outfile)

        # Re-open the dataset.
        rootgrp = Dataset(outfile, 'a', format='NETCDF4')

        # An additional global attribute.
        rootgrp.source = "MIPrep orbit simulator"

        # Close the dataset again.
        rootgrp.close()




