Exemplo n.º 1
0
 def __init__(self, SCP, ARPPos, ARPVel):
     self.SCP = SCP
     self.ARP = ARPPos
     self.ARP_vel = ARPVel
     self.LOS = (self.SCP - self.ARP)
     # unit vector versions
     self.uSCP = self._make_unit(self.SCP)
     self.uARP = self._make_unit(self.ARP)
     self.uARP_vel = self._make_unit(self.ARP_vel)
     self.uLOS = self._make_unit(self.LOS)
     self.left = numpy.cross(self.uARP, self.uARP_vel)
     self.look = numpy.sign(self.left.dot(self.uLOS))
     # Earth Tangent Plane (ETP) at the SCP is the plane tangent to the surface of constant height
     # above the WGS 84 ellipsoid (HAE) that contains the SCP. The ETP is an approximation to the
     # ground plane at the SCP.
     self.ETP = geocoords.wgs_84_norm(SCP)
     # slant plane unit normal
     self.uSPZ = self._make_unit(self.look *
                                 numpy.cross(self.ARP_vel, self.uLOS))
     # perpendicular component of range vector wrt the ground plane
     self.uGPX = self._make_unit(-self.uLOS +
                                 numpy.dot(self.ETP, self.uLOS) * self.ETP)
     self.uGPY = numpy.cross(self.ETP, self.uGPX)  # already unit vector
     # perpendicular component of north wrt the ground plane
     self.uNORTH = self._make_unit(
         numpy.array([0, 0, 1]) - self.ETP[2] * self.ETP)
     self.uEAST = numpy.cross(self.uNORTH, self.ETP)  # already unit vector
Exemplo n.º 2
0
    def __init__(self, geometry_calculator, row_vector, col_vector):
        """

        Parameters
        ----------
        geometry_calculator : GeometryCalculator
        row_vector : numpy.ndarray
        col_vector : numpy.ndarray
        """

        # extract sicd based parameters
        self.geometry_calculator = geometry_calculator
        self.ARPPos = geometry_calculator.ARP
        self.ARPVel = geometry_calculator.ARP_vel
        self.SCP = geometry_calculator.SCP
        self.slant_x = self._make_unit(self.ARPPos - self.SCP)
        self.slant_z = self._make_unit(numpy.cross(self.slant_x, self.ARPVel))
        if self.slant_z.dot(self.ARPPos) < 0:
            self.slant_z *= -1
        self.slant_y = self._make_unit(numpy.cross(self.slant_z, self.slant_x))
        self.ETP = wgs_84_norm(self.SCP)

        # store SIDD grid parameters
        self.row_vector = self._make_unit(row_vector)
        self.col_vector = self._make_unit(col_vector)
        self.normal_vector = self._make_unit(
            numpy.cross(self.row_vector, self.col_vector))
Exemplo n.º 3
0
    def __init__(self, sicd, row_vector, col_vector):
        """

        Parameters
        ----------
        sicd : SICDType
        row_vector : numpy.ndarray
        col_vector : numpy.ndarray
        """

        # extract sicd based parameters
        self.sicd = sicd
        self.ARPPos = sicd.SCPCOA.ARPPos.get_array()
        self.ARPVel = sicd.SCPCOA.ARPVel.get_array()
        self.SCP = sicd.GeoData.SCP.ECF.get_array()
        self.slant_x = self._make_unit(self.ARPPos - self.SCP)
        self.slant_z = self._make_unit(numpy.cross(self.slant_x, self.ARPVel))
        if self.slant_z.dot(self.ARPPos) < 0:
            self.slant_z *= -1
        self.slant_y = self._make_unit(numpy.cross(self.slant_z, self.slant_x))
        self.ETP = wgs_84_norm(self.SCP)

        # store SIDD grid parameters
        self.row_vector = self._make_unit(row_vector)
        self.col_vector = self._make_unit(col_vector)
        self.normal_vector = self._make_unit(
            numpy.cross(self.row_vector, self.col_vector))
Exemplo n.º 4
0
    def _derive_parameters(self, Grid, SCPCOA, GeoData):
        """
        Expected to be called from SICD parent.

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.GridType
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType
        GeoData : sarpy.io.complex.sicd_elements.GeoData.GeoDataType

        Returns
        -------
        None
        """

        if self.PolarAngRefTime is None and SCPCOA.SCPTime is not None:
            self.PolarAngRefTime = SCPCOA.SCPTime

        if GeoData is not None and GeoData.SCP is not None and GeoData.SCP.ECF is not None and \
                SCPCOA.ARPPos is not None and SCPCOA.ARPVel is not None:
            SCP = GeoData.SCP.ECF.get_array()
            ETP = geocoords.wgs_84_norm(SCP)

            ARP = SCPCOA.ARPPos.get_array()
            LOS = (SCP - ARP)
            uLOS = LOS / norm(LOS)

            look = SCPCOA.look
            ARP_vel = SCPCOA.ARPVel.get_array()
            uSPZ = look * numpy.cross(ARP_vel, uLOS)
            uSPZ /= norm(uSPZ)
            if Grid is not None and Grid.ImagePlane is not None:
                if self.IPN is None:
                    if Grid.ImagePlane == 'SLANT':
                        self.IPN = XYZType.from_array(uSPZ)
                    elif Grid.ImagePlane == 'GROUND':
                        self.IPN = XYZType.from_array(ETP)
            elif self.IPN is None:
                self.IPN = XYZType.from_array(
                    uSPZ)  # assuming slant -> most common

            if self.FPN is None:
                self.FPN = XYZType.from_array(ETP)
Exemplo n.º 5
0
    def _derive_geometry_parameters(self, GeoData):
        """
        Expected to be called by SICD parent.

        Parameters
        ----------
        GeoData : sarpy.io.complex.sicd_elements.GeoData.GeoDataType

        Returns
        -------
        None
        """

        if GeoData is None or GeoData.SCP is None or GeoData.SCP.ECF is None or \
                self.ARPPos is None or self.ARPVel is None:
            return  # nothing can be derived

        SCP = GeoData.SCP.ECF.get_array()
        ARP = self.ARPPos.get_array()
        ARP_vel = self.ARPVel.get_array()
        LOS = (SCP - ARP)
        # unit vector versions
        uSCP = SCP/norm(SCP)
        uARP = ARP/norm(ARP)
        uARP_vel = ARP_vel/norm(ARP_vel)
        uLOS = LOS/norm(LOS)
        # cross product junk
        left = numpy.cross(uARP, uARP_vel)
        look = numpy.sign(numpy.dot(left, uLOS))

        sot = 'R' if look < 0 else 'L'
        if self.SideOfTrack is None:
            self.SideOfTrack = sot
        elif self.SideOfTrack != sot:
            logging.error(
                'In SCPCOAType, the derived value for SideOfTrack is {} and the set '
                'value is {}'.format(sot, self.SideOfTrack))

        slant_range = numpy.linalg.norm(LOS)
        if self.SlantRange is None:
            self.SlantRange = slant_range
        elif abs(self.SlantRange - slant_range) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for SlantRange is {} and the set '
                          'value is {}'.format(slant_range, self.SlantRange))

        ground_range = norm(SCP)*numpy.arccos(numpy.dot(uSCP, uARP))
        if self.GroundRange is None:
            self.GroundRange = ground_range
        elif abs(self.GroundRange - ground_range) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for GroundRange is {} and the set '
                          'value is {}'.format(ground_range, self.GroundRange))

        doppler_cone = numpy.rad2deg(numpy.arccos(numpy.dot(uARP_vel, uLOS)))
        if self.DopplerConeAng is None:
            self.DopplerConeAng = doppler_cone
        elif abs(self.DopplerConeAng - doppler_cone) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for DopplerConeAng is {} and the set '
                          'value is {}'.format(doppler_cone, self.DopplerConeAng))

        # Earth Tangent Plane (ETP) at the SCP is the plane tangent to the surface of constant height
        # above the WGS 84 ellipsoid (HAE) that contains the SCP. The ETP is an approximation to the
        # ground plane at the SCP.
        ETP = geocoords.wgs_84_norm(SCP)
        graze_ang = numpy.rad2deg(numpy.arcsin(numpy.dot(ETP, -uLOS)))
        if self.GrazeAng is None:
            self.GrazeAng = graze_ang
        elif abs(self.GrazeAng - graze_ang) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for GrazeAng is {} and the set '
                          'value is {}'.format(graze_ang, self.GrazeAng))

        if self.IncidenceAng is None:
            self.IncidenceAng = 90 - self.GrazeAng

        # slant plane unit normal
        uSPZ = look*numpy.cross(ARP_vel, uLOS)
        uSPZ /= norm(uSPZ)
        # perpendicular component of range vector wrt the ground plane
        uGPX = -uLOS + numpy.dot(ETP, uLOS)*ETP
        uGPX /= norm(uGPX)
        uGPY = numpy.cross(ETP, uGPX)  # already unit vector
        twist_ang = -numpy.rad2deg(numpy.arcsin(numpy.dot(uGPY, uSPZ)))
        if self.TwistAng is None:
            self.TwistAng = twist_ang
        elif abs(self.TwistAng - twist_ang) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for TwistAng is {} and the set '
                          'value is {}'.format(twist_ang, self.TwistAng))

        slope_ang = numpy.rad2deg(numpy.arccos(numpy.dot(ETP, uSPZ)))
        if self.SlopeAng is None:
            self.SlopeAng = slope_ang
        elif abs(self.SlopeAng - slope_ang) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for SlopeAng is {} and the set '
                          'value is {}'.format(slope_ang, self.SlopeAng))

        # perpendicular component of north wrt the ground plane
        NORTH = numpy.array([0, 0, 1]) - ETP[2]*ETP
        uNORTH = NORTH/norm(NORTH)
        uEAST = numpy.cross(uNORTH, ETP)  # already unit vector
        azim_ang = numpy.rad2deg(numpy.arctan2(numpy.dot(uGPX, uEAST), numpy.dot(uGPX, uNORTH)))
        azim_ang = azim_ang if azim_ang > 0 else azim_ang + 360
        if self.AzimAng is None:
            self.AzimAng = azim_ang
        elif abs(self.AzimAng - azim_ang) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for AzimAng is {} and the set '
                          'value is {}'.format(azim_ang, self.AzimAng))

        # perpendicular component of ground plane wrt slant plane
        layover_ground = ETP - numpy.dot(ETP, uSPZ)*uSPZ  # TODO: reciprocal in sicd.py line 1605?
        layover_ang = numpy.rad2deg(numpy.arctan2(numpy.dot(layover_ground, uEAST), numpy.dot(layover_ground, uNORTH)))
        layover_ang = layover_ang if layover_ang > 0 else layover_ang + 360
        if self.LayoverAng is None:
            self.LayoverAng = layover_ang
        elif abs(self.LayoverAng - layover_ang) > 1e-5:  # sensible tolerance?
            logging.error('In SCPCOAType, the derived value for LayoverAng is {} and the set '
                          'value is {}'.format(layover_ang, self.LayoverAng))
Exemplo n.º 6
0
    def set_plane_frame(self,
                        normal_vector=None,
                        row_vector=None,
                        col_vector=None):
        """
        Set the plane unit normal, and the row and column vectors, in ECF coordinates.
        Note that the perpendicular component of col_vector with respect to the
        row_vector will be used.

        If `normal_vector`, `row_vector`, and `col_vector` are all `None`, then
        the normal to the Earth tangent plane at the reference point is used for
        `normal_vector`. The `row_vector` will be defined as the perpendicular
        component of `sicd.Grid.Row.UVectECF` to `normal_vector`. The `colummn_vector`
        will be defined as the component of `sicd.Grid.Col.UVectECF` perpendicular
        to both `normal_vector` and `row_vector`.

        If only `normal_vector` is supplied, then the `row_vector` and `column_vector`
        will be defined similarly as the perpendicular components of
        `sicd.Grid.Row.UVectECF` and `sicd.Grid.Col.UVectECF`.

        Otherwise, all vectors supplied will be normalized, but are required to be
        mutually perpendicular. If only two vectors are supplied, then the third
        will be determined.

        Parameters
        ----------
        normal_vector : None|numpy.ndarray
            The vector defining the outward unit normal in ECF coordinates.
        row_vector : None|numpy.ndarray
            The vector defining increasing column direction.
        col_vector : None|numpy.ndarray
            The vector defining increasing column direction.

        Returns
        -------
        None
        """
        def normalize(vec, name, perp=None):
            if not isinstance(vec, numpy.ndarray):
                vec = numpy.array(vec, dtype=numpy.float64)
            if not (isinstance(vec, numpy.ndarray) and vec.ndim == 1
                    and vec.size == 3):
                raise ValueError(
                    '{} vector must be a numpy.ndarray of dimension 1 and size 3.'
                    .format(name))
            vec = numpy.copy(vec)
            if perp is None:
                pass
            elif isinstance(perp, numpy.ndarray):
                vec = vec - perp * (perp.dot(vec))
            else:
                for entry in perp:
                    vec = vec - entry * (entry.dot(vec))

            norm = numpy.linalg.norm(vec)
            if norm == 0:
                raise ValueError(
                    '{} vector cannot be the zero vector.'.format(name))
            elif norm != 1:
                vec = vec / norm  # avoid modifying row_vector def exterior to this class
            return vec

        def check_perp(vec1, vec2, name1, name2, tolerance=1e-6):
            if abs(vec1.dot(vec2)) > tolerance:
                raise ValueError(
                    '{} vector and {} vector are not perpendicular'.format(
                        name1, name2))

        if self._reference_point is None:
            raise ValueError(
                'This requires that reference point is previously set.')

        if normal_vector is None and row_vector is None and col_vector is None:
            if self.sicd.RadarCollection.Area is None:
                self._normal_vector = wgs_84_norm(self.reference_point)
                self._row_vector = normalize(
                    self.sicd.Grid.Row.UVectECF.get_array(),
                    'row',
                    perp=self.normal_vector)
                self._col_vector = normalize(
                    self.sicd.Grid.Col.UVectECF.get_array(),
                    'column',
                    perp=(self.normal_vector, self.row_vector))
            else:
                self._row_vector = self.sicd.RadarCollection.Area.Plane.XDir.UVectECF.get_array(
                )
                self._col_vector = normalize(self.sicd.RadarCollection.Area.
                                             Plane.YDir.UVectECF.get_array(),
                                             'col',
                                             perp=self._row_vector)
                self._normal_vector = numpy.cross(self._row_vector,
                                                  self._col_vector)
        elif normal_vector is not None and row_vector is None and col_vector is None:
            self._normal_vector = normalize(normal_vector, 'normal')
            self._row_vector = normalize(
                self.sicd.Grid.Row.UVectECF.get_array(),
                'row',
                perp=self.normal_vector)
            self._col_vector = normalize(
                self.sicd.Grid.Col.UVectECF.get_array(),
                'column',
                perp=(self.normal_vector, self.row_vector))
        elif normal_vector is None:
            if row_vector is None or col_vector is None:
                raise ValueError(
                    'normal_vector is not defined, so both row_vector and col_vector must be.'
                )
            row_vector = normalize(row_vector, 'row')
            col_vector = normalize(col_vector, 'col')
            check_perp(row_vector, col_vector, 'row', 'col')
            self._row_vector = row_vector
            self._col_vector = col_vector
            self._normal_vector = numpy.cross(row_vector, col_vector)
        elif col_vector is None:
            if row_vector is None:
                raise ValueError(
                    'col_vector is not defined, so both normal_vector and row_vector must be.'
                )
            normal_vector = normalize(normal_vector, 'normal')
            row_vector = normalize(row_vector, 'row')
            check_perp(normal_vector, row_vector, 'normal', 'row')
            self._normal_vector = normal_vector
            self._row_vector = row_vector
            self._col_vector = numpy.cross(self.normal_vector, self.row_vector)
        elif row_vector is None:
            normal_vector = normalize(normal_vector, 'normal')
            col_vector = normalize(col_vector, 'col')
            check_perp(normal_vector, col_vector, 'normal', 'col')
            self._normal_vector = normal_vector
            self._col_vector = col_vector
            self._row_vector = numpy.cross(self.col_vector, self.normal_vector)
        else:
            normal_vector = normalize(normal_vector, 'normal')
            row_vector = normalize(row_vector, 'row')
            col_vector = normalize(col_vector, 'col')
            check_perp(normal_vector, row_vector, 'normal', 'row')
            check_perp(normal_vector, col_vector, 'normal', 'col')
            check_perp(row_vector, col_vector, 'row', 'col')
            self._normal_vector = normal_vector
            self._row_vector = row_vector
            self._col_vector = col_vector
        # check for outward unit norm
        if numpy.dot(self.normal_vector, self.reference_point) < 0:
            logger.warning(
                'The normal vector appears to be outward pointing, so reversing.'
            )
            self._normal_vector *= -1
Exemplo n.º 7
0
    def _derive_parameters(self, Grid, SCPCOA, GeoData, Position, Timeline):
        """
        Expected to be called from SICD parent.

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.Grid.GridType
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType
        GeoData : sarpy.io.complex.sicd_elements.GeoData.GeoDataType
        Position : sarpy.io.complex.sicd_elements.Position.PositionType
        Timeline : sarpy.io.complex.sicd_elements.Timeline.TimelineType

        Returns
        -------
        None
        """

        if self.PolarAngRefTime is None and SCPCOA.SCPTime is not None:
            self.PolarAngRefTime = SCPCOA.SCPTime

        if GeoData is None or GeoData.SCP is None or GeoData.SCP.ECF is None:
            return

        scp = GeoData.SCP.ECF.get_array()

        if SCPCOA.ARPPos is not None and SCPCOA.ARPVel is not None:
            scp = GeoData.SCP.ECF.get_array()
            etp = geocoords.wgs_84_norm(scp)

            arp = SCPCOA.ARPPos.get_array()
            los = (scp - arp)
            ulos = los / norm(los)

            look = SCPCOA.look
            arp_vel = SCPCOA.ARPVel.get_array()
            uspz = look * numpy.cross(arp_vel, ulos)
            uspz /= norm(uspz)
            if Grid is not None and Grid.ImagePlane is not None:
                if self.IPN is None:
                    if Grid.ImagePlane == 'SLANT':
                        self.IPN = XYZType.from_array(uspz)
                    elif Grid.ImagePlane == 'GROUND':
                        self.IPN = XYZType.from_array(etp)
            elif self.IPN is None:
                self.IPN = XYZType.from_array(
                    uspz)  # assuming slant -> most common

            if self.FPN is None:
                self.FPN = XYZType.from_array(etp)

        if Position is not None and \
                Timeline is not None and Timeline.CollectDuration is not None and \
                (self.PolarAngPoly is None or self.SpatialFreqSFPoly is None):
            pol_ref_pos = Position.ARPPoly(self.PolarAngRefTime)
            # fit the PFA polynomials
            times = numpy.linspace(0, Timeline.CollectDuration, 15)
            k_a, k_sf = self.pfa_polar_coords(Position, scp, times)

            self.PolarAngPoly = Poly1DType(
                Coefs=polynomial.polyfit(times, k_a, 5, full=False))
            self.SpatialFreqSFPoly = Poly1DType(
                Coefs=polynomial.polyfit(k_a, k_sf, 5, full=False))

        if Grid is not None and Grid.Row is not None and \
                Grid.Row.KCtr is not None and Grid.Row.ImpRespBW is not None:
            if self.Krg1 is None:
                self.Krg1 = Grid.Row.KCtr - 0.5 * Grid.Row.ImpRespBW
            if self.Krg2 is None:
                self.Krg2 = Grid.Row.KCtr + 0.5 * Grid.Row.ImpRespBW
        if Grid is not None and Grid.Col is not None and \
                Grid.Col.KCtr is not None and Grid.Col.ImpRespBW is not None:
            if self.Kaz1 is None:
                self.Kaz1 = Grid.Col.KCtr - 0.5 * Grid.Col.ImpRespBW
            if self.Kaz2 is None:
                self.Kaz2 = Grid.Col.KCtr + 0.5 * Grid.Col.ImpRespBW
Exemplo n.º 8
0
    def _check_image_plane(self, Grid, SCPCOA, SCP):
        """

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.Grid.GridType
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType
        SCP : numpy.ndarray

        Returns
        -------
        bool
        """

        if self.IPN is None or self.FPN is None:
            return True

        cond = True
        ipn = self.IPN.get_array(dtype='float64')
        fpn = self.FPN.get_array(dtype='float64')
        ETP = geocoords.wgs_84_norm(SCP)

        if Grid.ImagePlane == 'SLANT':
            try:
                ARP_Pos = SCPCOA.ARPPos.get_array(dtype='float64')
                ARP_Vel = SCPCOA.ARPVel.get_array(dtype='float64')
                uRG = SCP - ARP_Pos
                uRG /= numpy.linalg.norm(uRG)
                left = numpy.cross(ARP_Pos / numpy.linalg.norm(ARP_Pos),
                                   ARP_Vel / numpy.linalg.norm(ARP_Vel))
                look = numpy.sign(numpy.dot(left, uRG))
                Spn = -look * numpy.cross(uRG, ARP_Vel)
                uSpn = Spn / numpy.linalg.norm(Spn)

                if numpy.arccos(ipn.dot(uSpn)) > numpy.deg2rad(1):
                    logging.error(
                        'The image formation algorithm is PFA, the Grid.ImagePlane is "SLANT", '
                        'but COA slant plane and provided ipn are not within one degree of each other.'
                    )
                    cond = False
            except (AttributeError, ValueError, TypeError):
                pass
        elif Grid.ImagePlane == 'GROUND':
            if numpy.arccos(ipn.dot(ETP)) > numpy.deg2rad(3):
                logging.error(
                    'The image formation algorithm is PFA, the Grid.ImagePlane is "Ground", '
                    'but the Earth Tangent Plane at SCP and provided ipn are not within three '
                    'degrees of each other.')
                cond = False

        # verify that fpn points outwards
        if fpn.dot(SCP) < 0:
            logging.error(
                'The image formation algorithm is PFA, and the focus plane unit normal does not '
                'appear to be outward pointing')
            cond = False
        # check agreement between focus plane and ground plane
        if numpy.arccos(fpn.dot(ETP)) > numpy.deg2rad(3):
            logging.warning(
                'The image formation algorithm is PFA, and the focus plane unit normal is not within '
                'three degrees of the earth Tangent Plane.')
        return cond