Exemple #1
0
class RcvParametersType(Serializable):
    """
    The receive parameters geometry implementation.
    """

    _fields = ('RcvTime', 'RcvPos', 'RcvVel', 'SideOfTrack', 'SlantRange',
               'GroundRange', 'DopplerConeAngle', 'GrazeAngle',
               'IncidenceAngle', 'AzimuthAngle')
    _required = _fields
    _numeric_format = {
        'SlantRange': FLOAT_FORMAT,
        'GroundRange': FLOAT_FORMAT,
        'DopplerConeAngle': FLOAT_FORMAT,
        'GrazeAngle': FLOAT_FORMAT,
        'IncidenceAngle': FLOAT_FORMAT,
        'AzimuthAngle': FLOAT_FORMAT
    }
    # descriptors
    RcvTime = FloatDescriptor(
        'RcvTime',
        _required,
        strict=DEFAULT_STRICT,
        docstring='Receive time for the first sample for the reference vector.'
    )  # type: float
    RcvPos = SerializableDescriptor(
        'RcvPos',
        XYZType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='APC position in ECF coordinates.')  # type: XYZType
    RcvVel = SerializableDescriptor(
        'RcvVel',
        XYZType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='APC velocity in ECF coordinates.')  # type: XYZType
    SideOfTrack = StringEnumDescriptor(
        'SideOfTrack', ('L', 'R'),
        _required,
        strict=DEFAULT_STRICT,
        docstring='Side of Track parameter for the collection.')  # type: str
    SlantRange = FloatDescriptor(
        'SlantRange',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, None),
        docstring='Slant range from the APC to the CRP.')  # type: float
    GroundRange = FloatDescriptor(
        'GroundRange',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, None),
        docstring='Ground range from the APC nadir to the CRP.')  # type: float
    DopplerConeAngle = FloatDescriptor(
        'DopplerConeAngle',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, 180),
        docstring='Doppler Cone Angle between APC velocity and deg CRP Line of '
        'Sight (LOS).')  # type: float
    GrazeAngle = FloatDescriptor(
        'GrazeAngle',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, 90),
        docstring='Grazing angle for the APC to CRP LOS and the Earth Tangent '
        'Plane (ETP) at the CRP.')  # type: float
    IncidenceAngle = FloatDescriptor(
        'IncidenceAngle',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, 90),
        docstring='Incidence angle for the APC to CRP LOS and the Earth Tangent '
        'Plane (ETP) at the CRP.')  # type: float
    AzimuthAngle = FloatDescriptor(
        'AzimuthAngle',
        _required,
        strict=DEFAULT_STRICT,
        bounds=(0, 360),
        docstring='Angle from north to the line from the CRP to the APC ETP '
        'Nadir (i.e. North to +GPX). Measured clockwise from North '
        'toward East.')  # type: float

    def __init__(self,
                 RcvTime=None,
                 RcvPos=None,
                 RcvVel=None,
                 SideOfTrack=None,
                 SlantRange=None,
                 GroundRange=None,
                 DopplerConeAngle=None,
                 GrazeAngle=None,
                 IncidenceAngle=None,
                 AzimuthAngle=None,
                 **kwargs):
        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.RcvTime = RcvTime
        self.RcvPos = RcvPos
        self.RcvVel = RcvVel
        self.SideOfTrack = SideOfTrack
        self.SlantRange = SlantRange
        self.GroundRange = GroundRange
        self.DopplerConeAngle = DopplerConeAngle
        self.GrazeAngle = GrazeAngle
        self.IncidenceAngle = IncidenceAngle
        self.AzimuthAngle = AzimuthAngle
        super(RcvParametersType, self).__init__(**kwargs)

    @property
    def look(self):
        """
        int: An integer version of `SideOfTrack`:

            * None if `SideOfTrack` is not defined

            * -1 if SideOfTrack == 'R'

            * 1 if SideOftrack == 'L'
        """

        if self.SideOfTrack is None:
            return None
        return -1 if self.SideOfTrack == 'R' else 1
Exemple #2
0
class ResearchType(Serializable):
    _fields = ('MetadataVersion', 'DetailCollectionInfo',
               'DetailSubCollectionInfo', 'DetailImageInfo',
               'DetailSensorInfo', 'DetailFiducialInfo', 'DetailObjectInfo')
    _required = ('MetadataVersion', )
    # descriptors
    MetadataVersion = StringDescriptor(
        'MetadataVersion', _required,
        docstring='The version number')  # type: str
    DetailCollectionInfo = SerializableDescriptor(
        'DetailCollectionInfo',
        CollectionInfoType,
        _required,
        docstring='High level information about the data collection'
    )  # type: Optional[CollectionInfoType]
    DetailSubCollectionInfo = SerializableDescriptor(
        'DetailSubCollectionInfo',
        SubCollectionInfoType,
        _required,
        docstring='Information about sub-division of the overall data collection'
    )  # type: Optional[SubCollectionInfoType]
    DetailImageInfo = SerializableDescriptor(
        'DetailImageInfo',
        ImageInfoType,
        _required,
        docstring='Information about the referenced image'
    )  # type: Optional[ImageInfoType]
    DetailSensorInfo = SerializableDescriptor(
        'DetailSensorInfo',
        SensorInfoType,
        _required,
        docstring='Information about the sensor'
    )  # type: Optional[SensorInfoType]
    DetailFiducialInfo = SerializableDescriptor(
        'DetailFiducialInfo',
        FiducialInfoType,
        _required,
        docstring='Information about the ground-truthed fiducials'
    )  # type: Optional[FiducialInfoType]
    DetailObjectInfo = SerializableDescriptor(
        'DetailObjectInfo',
        ObjectInfoType,
        _required,
        docstring='Information about the ground-truthed objects'
    )  # type: Optional[ObjectInfoType]

    def __init__(self,
                 MetadataVersion='Unknown',
                 DetailCollectionInfo=None,
                 DetailSubCollectionInfo=None,
                 DetailImageInfo=None,
                 DetailSensorInfo=None,
                 DetailFiducialInfo=None,
                 DetailObjectInfo=None,
                 **kwargs):
        """
        Parameters
        ----------
        MetadataVersion : str
        DetailCollectionInfo : CollectionInfoType
        DetailSubCollectionInfo : SubCollectionInfoType
        DetailImageInfo : ImageInfoType
        DetailSensorInfo : SensorInfo
        DetailFiducialInfo : FiducialInfoType
        DetailObjectInfo : ObjectInfoType
        kwargs
            Other keyword arguments
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.MetadataVersion = MetadataVersion
        self.DetailCollectionInfo = DetailCollectionInfo
        self.DetailSubCollectionInfo = DetailSubCollectionInfo
        self.DetailImageInfo = DetailImageInfo
        self.DetailSensorInfo = DetailSensorInfo
        self.DetailFiducialInfo = DetailFiducialInfo
        self.DetailObjectInfo = DetailObjectInfo
        super(ResearchType, self).__init__(**kwargs)

    def to_xml_bytes(self,
                     urn=None,
                     tag='RESEARCH',
                     check_validity=False,
                     strict=DEFAULT_STRICT):
        if urn is None:
            urn = _AFRL_SPECIFICATION_NAMESPACE
        return super(ResearchType,
                     self).to_xml_bytes(urn=urn,
                                        tag=tag,
                                        check_validity=check_validity,
                                        strict=strict)

    def to_xml_string(self,
                      urn=None,
                      tag='RESEARCH',
                      check_validity=False,
                      strict=DEFAULT_STRICT):
        return self.to_xml_bytes(urn=urn,
                                 tag=tag,
                                 check_validity=check_validity,
                                 strict=strict).decode('utf-8')

    def apply_sicd(self,
                   sicd,
                   base_file_name,
                   populate_in_periphery=False,
                   include_out_of_range=False,
                   padding_fraction=0.05,
                   minimum_pad=0):
        """
        Apply the given sicd to define all the relevant derived data, assuming
        that the starting point is physical ground truth populated, and image
        details and locations will be inferred. This modifies the structure in
        place.

        Parameters
        ----------
        sicd : SICDType
        base_file_name : str
        populate_in_periphery : bool
        include_out_of_range : bool
        padding_fraction : None|float
        minimum_pad : int|float
        """

        # assume that collection info and subcollection info are previously defined

        # define the image info
        if self.DetailImageInfo is not None:
            raise ValueError('Image Info is already defined')
        self.DetailImageInfo = ImageInfoType.from_sicd(sicd, base_file_name)

        # define sensor info
        if self.DetailSensorInfo is not None:
            raise ValueError('Sensor Info is already defined')
        self.DetailSensorInfo = SensorInfoType.from_sicd(sicd)

        if self.DetailFiducialInfo is None:
            self.DetailFiducialInfo = FiducialInfoType(
                NumberOfFiducialsInImage=0, NumberOfFiducialsInScene=0)
        else:
            self.DetailFiducialInfo.set_image_location_from_sicd(
                sicd,
                populate_in_periphery=populate_in_periphery,
                include_out_of_range=include_out_of_range)

        if self.DetailObjectInfo is None:
            self.DetailObjectInfo = ObjectInfoType(NumberOfObjectsInImage=0,
                                                   NumberOfObjectsInScene=0)
        else:
            self.DetailObjectInfo.set_image_location_from_sicd(
                sicd,
                layover_shift=True,
                populate_in_periphery=populate_in_periphery,
                include_out_of_range=include_out_of_range,
                padding_fraction=padding_fraction,
                minimum_pad=minimum_pad)

    def apply_sicd_reader(self,
                          sicd_reader,
                          populate_in_periphery=False,
                          include_out_of_range=False,
                          padding_fraction=0.05,
                          minimum_pad=0):
        """
        Apply the given sicd to define all the relevant derived data, assuming
        that the starting point is physical ground truth populated, and image
        details and locations will be inferred. This modifies the structure in
        place.

        Parameters
        ----------
        sicd_reader : SICDReader
        populate_in_periphery : bool
        include_out_of_range : bool
        padding_fraction : None|float
        minimum_pad : int|float
        """

        base_file = os.path.split(sicd_reader.file_name)[1]
        self.apply_sicd(sicd_reader.sicd_meta,
                        base_file,
                        populate_in_periphery=populate_in_periphery,
                        include_out_of_range=include_out_of_range,
                        padding_fraction=padding_fraction,
                        minimum_pad=minimum_pad)
Exemple #3
0
class CPHDType(Serializable):
    """
    """

    _fields = ('CollectionInfo', 'Data', 'Global', 'Channel', 'SRP',
               'RadarCollection', 'Antenna', 'VectorParameters')
    _required = ('CollectionInfo', 'Data', 'Global', 'Channel', 'SRP',
                 'VectorParameters')
    # descriptors
    CollectionInfo = SerializableDescriptor(
        'CollectionInfo',
        CollectionInfoType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='General information about the collection.'
    )  # type: CollectionInfoType
    Data = SerializableDescriptor(
        'Data',
        DataType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe binary data components contained in the '
        'product.')  # type: DataType
    Global = SerializableDescriptor(
        'Global',
        GlobalType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Global parameters that apply to metadata components and CPHD '
        'signal arrays.')  # type: GlobalType
    Channel = SerializableDescriptor(
        'Channel',
        ChannelType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Channel specific parameters for CPHD channels.'
    )  # type: ChannelType
    SRP = SerializableDescriptor(
        'SRP',
        SRPTyp,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The Stabilization Reference Point (SRP) parameters.'
    )  # type: SRPTyp
    RadarCollection = SerializableDescriptor(
        'RadarCollection',
        RadarCollectionType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, RadarCollectionType]
    Antenna = SerializableDescriptor(
        'Antenna',
        AntennaType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Antenna parameters that describe antenna orientation, mainlobe '
        'steering and gain patterns vs. '
        'time.')  # type: Union[None, AntennaType]
    VectorParameters = SerializableDescriptor(
        'VectorParameters',
        VectorParametersType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Structure specifying the Vector parameters provided for '
        'each channel of a given product.')  # type: VectorParametersType

    def __init__(self,
                 CollectionInfo=None,
                 Data=None,
                 Global=None,
                 Channel=None,
                 SRP=None,
                 RadarCollection=None,
                 Antenna=None,
                 VectorParameters=None,
                 **kwargs):
        """

        Parameters
        ----------
        CollectionInfo : CollectionInfoType
        Data : DataType
        Global : GlobalType
        Channel : ChannelType
        SRP : SRPTyp
        RadarCollection : None|RadarCollectionType
        Antenna : None|AntennaType
        VectorParameters : VectorParametersType
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.CollectionInfo = CollectionInfo
        self.Data = Data
        self.Global = Global
        self.Channel = Channel
        self.SRP = SRP
        self.RadarCollection = RadarCollection
        self.Antenna = Antenna
        self.VectorParameters = VectorParameters
        super(CPHDType, self).__init__(**kwargs)

    def to_xml_bytes(self,
                     urn=None,
                     tag='CPHD',
                     check_validity=False,
                     strict=DEFAULT_STRICT):
        if urn is None:
            urn = _CPHD_SPECIFICATION_NAMESPACE
        return super(CPHDType,
                     self).to_xml_bytes(urn=urn,
                                        tag=tag,
                                        check_validity=check_validity,
                                        strict=strict)

    def to_xml_string(self,
                      urn=None,
                      tag='CPHD',
                      check_validity=False,
                      strict=DEFAULT_STRICT):
        return self.to_xml_bytes(urn=urn,
                                 tag=tag,
                                 check_validity=check_validity,
                                 strict=strict).decode('utf-8')

    def get_pvp_dtype(self):
        """
        Gets the dtype for the corresponding PVP structured array. Note that they
        must all have homogeneous dtype.

        Returns
        -------
        numpy.dtype
            This will be a compound dtype for a structured array.
        """

        if self.VectorParameters is None:
            raise ValueError('No VectorParameters defined.')
        return self.VectorParameters.get_vector_dtype()

    @classmethod
    def from_xml_file(cls, file_path):
        """
        Construct the cphd object from a stand-alone xml file path.

        Parameters
        ----------
        file_path : str

        Returns
        -------
        CPHDType
        """

        root_node, xml_ns = parse_xml_from_file(file_path)
        ns_key = 'default' if 'default' in xml_ns else None
        return cls.from_node(root_node, xml_ns=xml_ns, ns_key=ns_key)

    @classmethod
    def from_xml_string(cls, xml_string):
        """
        Construct the cphd object from an xml string.

        Parameters
        ----------
        xml_string : str|bytes

        Returns
        -------
        CPHDType
        """

        root_node, xml_ns = parse_xml_from_string(xml_string)
        ns_key = 'default' if 'default' in xml_ns else None
        return cls.from_node(root_node, xml_ns=xml_ns, ns_key=ns_key)
Exemple #4
0
class EBType(Serializable):
    """
    Electrical boresight (EB) steering directions for an electronically steered array.
    """

    _fields = ('DCXPoly', 'DCYPoly')
    _required = _fields
    # descriptors
    DCXPoly = SerializableDescriptor(
        'DCXPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Electrical boresight steering *X-axis direction cosine (DCX)* as a function of '
        'slow time ``(variable 1)``.')  # type: Poly1DType
    DCYPoly = SerializableDescriptor(
        'DCYPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Electrical boresight steering *Y-axis direction cosine (DCY)* as a function of '
        'slow time ``(variable 1)``.')  # type: Poly1DType

    def __init__(self, DCXPoly=None, DCYPoly=None, **kwargs):
        """
        Parameters
        ----------
        DCXPoly : Poly1DType|numpy.ndarray|list|tuple
        DCYPoly : Poly1DType|numpy.ndarray|list|tuple
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.DCXPoly = DCXPoly
        self.DCYPoly = DCYPoly
        super(EBType, self).__init__(**kwargs)

    def __call__(self, t):
        """
        Evaluate the polynomial at points `t`. This passes `t` straight through
        to :func:`polyval` of `numpy.polynomial.polynomial` for each of
        `DCXPoly,DCYPoly` components. If any of `DCXPoly,DCYPoly` is not populated,
        then `None` is returned.

        Parameters
        ----------
        t : float|int|numpy.ndarray
            The point(s) at which to evaluate.

        Returns
        -------
        None|numpy.ndarray
        """

        if self.DCXPoly is None or self.DCYPoly is None:
            return None
        return numpy.array([self.DCXPoly(t), self.DCYPoly(t)])
Exemple #5
0
class AntParamType(Serializable):
    """
    The antenna parameters container.
    """

    _fields = ('XAxisPoly', 'YAxisPoly', 'FreqZero', 'EB', 'Array', 'Elem',
               'GainBSPoly', 'EBFreqShift', 'MLFreqDilation')
    _required = ('XAxisPoly', 'YAxisPoly', 'FreqZero', 'Array')
    _numeric_format = {'FreqZero': '0.16G'}
    # descriptors
    XAxisPoly = SerializableDescriptor(
        'XAxisPoly',
        XYZPolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Antenna X-Axis unit vector in ECF coordinates as a function of time ``(variable 1)``.'
    )  # type: XYZPolyType
    YAxisPoly = SerializableDescriptor(
        'YAxisPoly',
        XYZPolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Antenna Y-Axis unit vector in ECF coordinates as a function of time ``(variable 1)``.'
    )  # type: XYZPolyType
    FreqZero = FloatDescriptor(
        'FreqZero',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'RF frequency *(f0)* used to specify the array pattern and electrical boresite *(EB)* '
        'steering direction cosines.')  # type: float
    EB = SerializableDescriptor(
        'EB',
        EBType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Electrical boresight *(EB)* steering directions for an electronically steered array.'
    )  # type: EBType
    Array = SerializableDescriptor(
        'Array',
        GainPhasePolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Array pattern polynomials that define the shape of the main-lobe.'
    )  # type: GainPhasePolyType
    Elem = SerializableDescriptor(
        'Elem',
        GainPhasePolyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Element array pattern polynomials for electronically steered arrays.'
    )  # type: GainPhasePolyType
    GainBSPoly = SerializableDescriptor(
        'GainBSPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Gain polynomial *(in dB)* as a function of frequency for boresight *(BS)* at :math:`DCX=0, DCY=0`. '
        'Frequency ratio :math:`(f-f0)/f0` is the input variable ``(variable 1)``, and the constant '
        'coefficient is always `0.0`.')  # type: Poly1DType
    EBFreqShift = BooleanDescriptor('EBFreqShift',
                                    _required,
                                    strict=DEFAULT_STRICT,
                                    docstring="""
        Parameter indicating whether the electronic boresite shifts with frequency for an electronically steered array.
        
        * `False` - No shift with frequency.
        
        * `True` - Shift with frequency per ideal array theory.
        
        """)  # type: bool
    MLFreqDilation = BooleanDescriptor('MLFreqDilation',
                                       _required,
                                       strict=DEFAULT_STRICT,
                                       docstring="""
        Parameter indicating the mainlobe (ML) width changes with frequency.
        
        * `False` - No change with frequency.
        
        * `True` - Change with frequency per ideal array theory.
        
        """)  # type: bool

    def __init__(self,
                 XAxisPoly=None,
                 YAxisPoly=None,
                 FreqZero=None,
                 EB=None,
                 Array=None,
                 Elem=None,
                 GainBSPoly=None,
                 EBFreqShift=None,
                 MLFreqDilation=None,
                 **kwargs):
        """
        Parameters
        ----------
        XAxisPoly : XYZPolyType
        YAxisPoly : XYZPolyType
        FreqZero : float
        EB : EBType
        Array : GainPhasePolyType
        Elem : GainPhasePolyType
        GainBSPoly : Poly1DType|numpy.ndarray|list|tuple
        EBFreqShift : bool
        MLFreqDilation : bool
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.XAxisPoly, self.YAxisPoly = XAxisPoly, YAxisPoly
        self.FreqZero = FreqZero
        self.EB = EB
        self.Array, self.Elem = Array, Elem
        self.GainBSPoly = GainBSPoly
        self.EBFreqShift, self.MLFreqDilation = EBFreqShift, MLFreqDilation
        super(AntParamType, self).__init__(**kwargs)

    def _apply_reference_frequency(self, reference_frequency):
        if self.FreqZero is not None:
            self.FreqZero += reference_frequency
class ExploitationFeaturesCollectionInformationType(Serializable):
    """
    General collection information.
    """

    _fields = ('SensorName', 'RadarMode', 'CollectionDateTime',
               'LocalDateTime', 'CollectionDuration', 'Resolution', 'InputROI',
               'Polarizations')
    _required = ('SensorName', 'RadarMode', 'CollectionDateTime',
                 'CollectionDuration')
    _collections_tags = {
        'Polarizations': {
            'array': False,
            'child_tag': 'Polarization'
        }
    }
    _numeric_format = {'CollectionDuration': '0.16G'}
    # Descriptor
    SensorName = StringDescriptor('SensorName',
                                  _required,
                                  strict=DEFAULT_STRICT,
                                  docstring='The name of the sensor.')  # str
    RadarMode = SerializableDescriptor(
        'RadarMode',
        RadarModeType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Radar collection mode.')  # type: RadarModeType
    CollectionDateTime = DateTimeDescriptor(
        'CollectionDateTime',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Collection date and time defined in Coordinated Universal Time (UTC). The seconds '
        'should be followed by a Z to indicate UTC.')  # type: numpy.datetime64
    CollectionDuration = FloatDescriptor(
        'CollectionDuration',
        _required,
        strict=DEFAULT_STRICT,
        docstring='The duration of the collection (units = seconds).'
    )  # type: float
    Resolution = SerializableDescriptor(
        'Resolution',
        RangeAzimuthType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Uniformly-weighted resolution (range and azimuth) processed in '
        'the slant plane.')  # type: Union[None, RangeAzimuthType]
    InputROI = SerializableDescriptor(
        'InputROI',
        InputROIType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='ROI representing portion of input data used to make '
        'this product.')  # type: Union[None, InputROIType]
    Polarizations = SerializableListDescriptor(
        'Polarizations',
        TxRcvPolarizationType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Transmit and receive polarization(s).'
    )  # type: Union[None, List[TxRcvPolarizationType]]

    def __init__(self,
                 SensorName=None,
                 RadarMode=None,
                 CollectionDateTime=None,
                 LocalDateTime=None,
                 CollectionDuration=None,
                 Resolution=None,
                 Polarizations=None,
                 **kwargs):
        """

        Parameters
        ----------
        SensorName : str
        RadarMode : RadarModeType
        CollectionDateTime : numpy.datetime64|datetime.datetime|datetime.date|str
        LocalDateTime : None|str|datetime.datetime
        CollectionDuration : float
        Resolution : None|RangeAzimuthType|numpy.ndarray|list|tuple
        Polarizations : None|List[TxRcvPolarizationType]
        kwargs
        """

        self._local_date_time = None
        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.SensorName = SensorName
        self.RadarMode = RadarMode
        self.CollectionDateTime = CollectionDateTime
        self.CollectionDuration = CollectionDuration
        self.LocalDateTime = LocalDateTime
        self.Resolution = Resolution
        self.Polarizations = Polarizations
        super(ExploitationFeaturesCollectionInformationType,
              self).__init__(**kwargs)

    @property
    def LocalDateTime(self):
        """None|str:  The local date/time string of the collection. *Optional.*"""
        return self._local_date_time

    @LocalDateTime.setter
    def LocalDateTime(
            self,
            value):  # type: (Union[None, str, datetime.datetime]) -> None
        if value is None:
            self._local_date_time = None
            return
        elif isinstance(value, datetime.datetime):
            value = value.isoformat('T')

        if isinstance(value, string_types):
            self._local_date_time = value
        else:
            logger.error(
                'Attribute LocalDateTime of class ExploitationFeaturesCollectionInformationType\n\t'
                'requires a datetime.datetime or string. Got unsupported type {}.\n\t'
                'Setting value to None.'.format(type(value)))
            self._local_date_time = None

    @classmethod
    def from_sicd(cls, sicd):
        """
        Construct from a sicd element.

        Parameters
        ----------
        sicd : SICDType

        Returns
        -------
        ExploitationFeaturesCollectionInformationType
        """

        if not isinstance(sicd, SICDType):
            raise TypeError('Requires SICDType instance, got type {}'.format(
                type(sicd)))

        polarizations = [
            TxRcvPolarizationType.from_sicd_value(entry.TxRcvPolarization)
            for entry in sicd.RadarCollection.RcvChannels
        ]

        return cls(
            SensorName=sicd.CollectionInfo.CollectorName,
            RadarMode=RadarModeType(**sicd.CollectionInfo.RadarMode.to_dict()),
            CollectionDateTime=sicd.Timeline.CollectStart,
            CollectionDuration=sicd.Timeline.CollectDuration,
            Resolution=(sicd.Grid.Row.SS, sicd.Grid.Col.SS),
            Polarizations=polarizations)
class CollectionType(Serializable):
    """
    Metadata regarding one of the input collections.
    """
    _fields = ('Information', 'Geometry', 'Phenomenology', 'identifier')
    _required = ('Information', 'identifier')
    _set_as_attribute = ('identifier', )
    # Descriptor
    Information = SerializableDescriptor(
        'Information',
        ExploitationFeaturesCollectionInformationType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='General collection information.'
    )  # type: ExploitationFeaturesCollectionInformationType
    Geometry = SerializableDescriptor(
        'Geometry',
        ExploitationFeaturesCollectionGeometryType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Key geometry parameters independent of product '
        'processing.'
    )  # type: Union[None, ExploitationFeaturesCollectionGeometryType]
    Phenomenology = SerializableDescriptor(
        'Phenomenology',
        ExploitationFeaturesCollectionPhenomenologyType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Phenomenology related to both the geometry and the final '
        'product processing.'
    )  # type: Union[None, ExploitationFeaturesCollectionPhenomenologyType]
    identifier = StringDescriptor(
        'identifier',
        _required,
        strict=DEFAULT_STRICT,
        docstring='The exploitation feature identifier.')  # type: str

    def __init__(self,
                 Information=None,
                 Geometry=None,
                 Phenomenology=None,
                 identifier=None,
                 **kwargs):
        """

        Parameters
        ----------
        Information : ExploitationFeaturesCollectionInformationType
        Geometry : None|ExploitationFeaturesCollectionGeometryType
        Phenomenology : None|ExploitationFeaturesCollectionPhenomenologyType
        identifier : str
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Information = Information
        self.Geometry = Geometry
        self.Phenomenology = Phenomenology
        self.identifier = identifier
        super(CollectionType, self).__init__(**kwargs)

    @classmethod
    def from_calculator(cls, calculator, sicd):
        """
        Create from an ExploitationCalculator object.

        Parameters
        ----------
        calculator : ExploitationCalculator
        sicd : SICDType

        Returns
        -------
        CollectionType
        """

        if not isinstance(calculator, ExploitationCalculator):
            raise TypeError(
                'Requires input which is an instance of ExploitationCalculator, got type {}'
                .format(type(calculator)))

        return cls(
            identifier=sicd.CollectionInfo.CoreName,
            Information=ExploitationFeaturesCollectionInformationType.
            from_sicd(sicd),
            Geometry=ExploitationFeaturesCollectionGeometryType.
            from_calculator(calculator),
            Phenomenology=ExploitationFeaturesCollectionPhenomenologyType.
            from_calculator(calculator))
Exemple #8
0
class SCPCOAType(Serializable):
    """
    Center of Aperture (COA) for the Scene Center Point (SCP).
    """

    _fields = (
        'SCPTime', 'ARPPos', 'ARPVel', 'ARPAcc', 'SideOfTrack', 'SlantRange', 'GroundRange', 'DopplerConeAng',
        'GrazeAng', 'IncidenceAng', 'TwistAng', 'SlopeAng', 'AzimAng', 'LayoverAng')
    _required = _fields
    _numeric_format = {
        'SCPTime': '0.16G', 'SlantRange': '0.16G', 'GroundRange': '0.16G', 'DopplerConeAng': '0.16G',
        'GrazeAng': '0.16G', 'IncidenceAng': '0.16G', 'TwistAng': '0.16G', 'SlopeAng': '0.16G',
        'AzimAng': '0.16G', 'LayoverAng': '0.16G'}
    # class variables
    _SIDE_OF_TRACK_VALUES = ('L', 'R')
    # descriptors
    SCPTime = FloatDescriptor(
        'SCPTime', _required, strict=DEFAULT_STRICT,
        docstring='*Center Of Aperture time for the SCP (t_COA_SCP)*, relative to collection '
                  'start in seconds.')  # type: float
    ARPPos = SerializableDescriptor(
        'ARPPos', XYZType, _required, strict=DEFAULT_STRICT,
        docstring='Aperture position at *t_COA_SCP* in ECF coordinates.')  # type: XYZType
    ARPVel = SerializableDescriptor(
        'ARPVel', XYZType, _required, strict=DEFAULT_STRICT,
        docstring='ARP Velocity at *t_COA_SCP* in ECF coordinates.')  # type: XYZType
    ARPAcc = SerializableDescriptor(
        'ARPAcc', XYZType, _required, strict=DEFAULT_STRICT,
        docstring='ARP Acceleration at *t_COA_SCP* in ECF coordinates.')  # type: XYZType
    SideOfTrack = StringEnumDescriptor(
        'SideOfTrack', _SIDE_OF_TRACK_VALUES, _required, strict=DEFAULT_STRICT,
        docstring='Side of track.')  # type: str
    SlantRange = FloatDescriptor(
        'SlantRange', _required, strict=DEFAULT_STRICT,
        docstring='Slant range from the aperture to the *SCP* in meters.')  # type: float
    GroundRange = FloatDescriptor(
        'GroundRange', _required, strict=DEFAULT_STRICT,
        docstring='Ground Range from the aperture nadir to the *SCP*. Distance measured along spherical earth model '
                  'passing through the *SCP* in meters.')  # type: float
    DopplerConeAng = FloatDescriptor(
        'DopplerConeAng', _required, strict=DEFAULT_STRICT,
        docstring='The Doppler Cone Angle to SCP at *t_COA_SCP* in degrees.')  # type: float
    GrazeAng = FloatDescriptor(
        'GrazeAng', _required, strict=DEFAULT_STRICT, bounds=(0., 90.),
        docstring='Grazing Angle between the SCP *Line of Sight (LOS)* and *Earth Tangent Plane (ETP)*.')  # type: float
    IncidenceAng = FloatDescriptor(
        'IncidenceAng', _required, strict=DEFAULT_STRICT, bounds=(0., 90.),
        docstring='Incidence Angle between the *LOS* and *ETP* normal.')  # type: float
    TwistAng = FloatDescriptor(
        'TwistAng', _required, strict=DEFAULT_STRICT, bounds=(-90., 90.),
        docstring='Angle between cross range in the *ETP* and cross range in the slant plane.')  # type: float
    SlopeAng = FloatDescriptor(
        'SlopeAng', _required, strict=DEFAULT_STRICT, bounds=(0., 90.),
        docstring='Slope Angle from the *ETP* to the slant plane at *t_COA_SCP*.')  # type: float
    AzimAng = FloatDescriptor(
        'AzimAng', _required, strict=DEFAULT_STRICT, bounds=(0., 360.),
        docstring='Angle from north to the line from the *SCP* to the aperture nadir at *COA*. Measured '
                  'clockwise in the *ETP*.')  # type: float
    LayoverAng = FloatDescriptor(
        'LayoverAng', _required, strict=DEFAULT_STRICT, bounds=(0., 360.),
        docstring='Angle from north to the layover direction in the *ETP* at *COA*. Measured '
                  'clockwise in the *ETP*.')  # type: float

    def __init__(self, SCPTime=None, ARPPos=None, ARPVel=None, ARPAcc=None, SideOfTrack=None,
                 SlantRange=None, GroundRange=None, DopplerConeAng=None, GrazeAng=None, IncidenceAng=None,
                 TwistAng=None, SlopeAng=None, AzimAng=None, LayoverAng=None, **kwargs):
        """

        Parameters
        ----------
        SCPTime : float
        ARPPos : XYZType|numpy.ndarray|list|tuple
        ARPVel : XYZType|numpy.ndarray|list|tuple
        ARPAcc : XYZType|numpy.ndarray|list|tuple
        SideOfTrack : str
        SlantRange : float
        GroundRange : float
        DopplerConeAng : float
        GrazeAng : float
        IncidenceAng : float
        TwistAng : float
        SlopeAng : float
        AzimAng : float
        LayoverAng : float
        kwargs : dict
        """

        self._ROV = None
        self._squint = None
        self._shadow = None
        self._shadow_magnitude = None
        self._layover_magnitude = None

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.SCPTime = SCPTime
        self.ARPPos, self.ARPVel, self.ARPAcc = ARPPos, ARPVel, ARPAcc
        self.SideOfTrack = SideOfTrack
        self.SlantRange, self.GroundRange = SlantRange, GroundRange
        self.DopplerConeAng, self.GrazeAng, self.IncidenceAng = DopplerConeAng, GrazeAng, IncidenceAng
        self.TwistAng, self.SlopeAng, self.AzimAng, self.LayoverAng = TwistAng, SlopeAng, AzimAng, LayoverAng
        super(SCPCOAType, self).__init__(**kwargs)

    @property
    def look(self):
        """
        int: An integer version of `SideOfTrack`:

            * None if `SideOfTrack` is not defined

            * -1 if SideOfTrack == 'R'

            * 1 if SideOftrack == 'L'
        """

        if self.SideOfTrack is None:
            return None
        else:
            return -1 if self.SideOfTrack == 'R' else 1

    @property
    def ROV(self):
        """
        float: The Ratio of Range to Velocity at Center of Aperture time.
        """

        return self._ROV

    @property
    def ThetaDot(self):
        """
        float: Derivative of Theta as a function of time at Center of Aperture time.
        """

        if self.DopplerConeAng is None or self.ROV is None:
            return None
        return float(numpy.sin(numpy.deg2rad(self.DopplerConeAng))/self.ROV)

    @property
    def MultipathGround(self):
        """
        float: The anticipated angle of multipath features on the ground in degrees.
        """
        if self.GrazeAng is None or self.TwistAng is None:
            return None
        return numpy.rad2deg(
            -numpy.arctan(numpy.tan(numpy.deg2rad(self.TwistAng))*numpy.sin(numpy.deg2rad(self.GrazeAng))))

    @property
    def Multipath(self):
        """
        float: The anticipated angle of multipath features in degrees.
        """
        if self.AzimAng is None or self.MultipathGround is None:
            return None
        return numpy.mod(self.AzimAng - 180 + self.MultipathGround, 360)

    @property
    def Shadow(self):
        """
        float: The anticipated angle of shadow features in degrees.
        """

        return self._shadow

    @property
    def ShadowMagnitude(self):
        """
        float: The anticipated relative magnitude of shadow features.
        """

        return self._shadow_magnitude

    @property
    def Squint(self):
        """
        float: The squint angle, in degrees.
        """

        return self._squint

    @property
    def LayoverMagnitude(self):
        """
        float: The anticipated relative magnitude of layover features.
        """

        return self._layover_magnitude

    def _derive_scp_time(self, Grid, overwrite=False):
        """
        Expected to be called by SICD parent.

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.GridType
        overwrite : bool

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

        if Grid is None or Grid.TimeCOAPoly is None:
            return  # nothing can be done
        if not overwrite and self.SCPTime is not None:
            return  # nothing should be done

        scp_time = Grid.TimeCOAPoly.Coefs[0, 0]
        self.SCPTime = scp_time

    def _derive_position(self, Position, overwrite=False):
        """
        Derive aperture position parameters, if necessary. Expected to be called by SICD parent.

        Parameters
        ----------
        Position : sarpy.io.complex.sicd_elements.Position.PositionType
        overwrite : bool

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

        if Position is None or Position.ARPPoly is None or self.SCPTime is None:
            return  # nothing can be derived

        # set aperture position, velocity, and acceleration at scptime from position
        # polynomial, if necessary
        poly = Position.ARPPoly
        scptime = self.SCPTime

        if self.ARPPos is None or overwrite:
            self.ARPPos = XYZType.from_array(poly(scptime))
            self.ARPVel = XYZType.from_array(poly.derivative_eval(scptime, 1))
            self.ARPAcc = XYZType.from_array(poly.derivative_eval(scptime, 2))

    def _derive_geometry_parameters(self, GeoData, overwrite=False):
        """
        Expected to be called by SICD parent.

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

        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

        # construct our calculator
        calculator = GeometryCalculator(
            GeoData.SCP.ECF.get_array(), self.ARPPos.get_array(), self.ARPVel.get_array())
        # set all the values
        self._ROV = calculator.ROV
        if self.SideOfTrack is None or overwrite:
            self.SideOfTrack = calculator.SideOfTrack
        if self.SlantRange is None or overwrite:
            self.SlantRange = calculator.SlantRange
        if self.GroundRange is None or overwrite:
            self.GroundRange = calculator.GroundRange
        if self.DopplerConeAng is None or overwrite:
            self.DopplerConeAng = calculator.DopplerConeAng
        graz, inc = calculator.get_graze_and_incidence()
        if self.GrazeAng is None or overwrite:
            self.GrazeAng = graz
        if self.IncidenceAng is None or overwrite:
            self.IncidenceAng = inc
        if self.TwistAng is None or overwrite:
            self.TwistAng = calculator.TwistAng
        self._squint = calculator.SquintAngle
        if self.SlopeAng is None or overwrite:
            self.SlopeAng = calculator.SlopeAng
        if self.AzimAng is None or overwrite:
            self.AzimAng = calculator.AzimAng
        layover, self._layover_magnitude = calculator.get_layover()
        if self.LayoverAng is None or overwrite:
            self.LayoverAng = layover
        self._shadow, self._shadow_magnitude = calculator.get_shadow()

    def rederive(self, Grid, Position, GeoData):
        """
        Rederive all derived quantities.

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.GridType
        Position : sarpy.io.complex.sicd_elements.Position.PositionType
        GeoData : sarpy.io.complex.sicd_elements.GeoData.GeoDataType

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

        self._derive_scp_time(Grid, overwrite=True)
        self._derive_position(Position, overwrite=True)
        self._derive_geometry_parameters(GeoData, overwrite=True)

    def check_values(self, GeoData):
        """
        Check derived values for validity.

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

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

        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 True  # nothing can be derived

        # construct our calculator
        calculator = GeometryCalculator(
            GeoData.SCP.ECF.get_array(), self.ARPPos.get_array(), self.ARPVel.get_array())

        cond = True
        if calculator.SideOfTrack != self.SideOfTrack:
            self.log_validity_error(
                'SideOfTrack is expected to be {}, and is populated as {}'.format(
                    calculator.SideOfTrack, self.SideOfTrack))
            cond = False

        for attribute in ['SlantRange', 'GroundRange']:
            val1 = getattr(self, attribute)
            val2 = getattr(calculator, attribute)
            if abs(val1/val2 - 1) > 1e-6:
                self.log_validity_error(
                    'attribute {} is expected to have value {}, but is populated as {}'.format(attribute, val1, val2))
                cond = False

        for attribute in [
                'DopplerConeAng', 'GrazeAng', 'IncidenceAng', 'TwistAng', 'SlopeAng', 'AzimAng', 'LayoverAng']:
            val1 = getattr(self, attribute)
            val2 = getattr(calculator, attribute)
            if abs(val1 - val2) > 1e-3:
                self.log_validity_error(
                    'attribute {} is expected to have value {}, but is populated as {}'.format(attribute, val1, val2))
                cond = False
        return cond
Exemple #9
0
class SensorInfoType(Serializable):
    _fields = (
        'Name', 'SensorMfg', 'OperatingAgency', 'Type', 'Mode', 'Band',
        'Bandwidth', 'CenterFrequency', 'NearRange', 'SlantRangeSwathWidth',
        'Polarization', 'Range', 'DepressionAngle', 'LinearDynamicRange',
        'BeamWidth', 'Aimpoint', 'AircraftHeading', 'AircraftTrackAngle', 'Look', 'SquintAngle',
        'AircraftLocation', 'AircraftVelocity', 'FlightNumber', 'PassNumber')
    _required = (
        'Type', 'Range', 'DepressionAngle', 'Aimpoint', 'Look', 'SquintAngle',
        'AircraftLocation', 'AircraftVelocity')
    _numeric_format = {
        'Bandwidth': '0.17G', 'CenterFrequency': '0.17G', 'NearRange': '0.17G',
        'SlantRangeSwathWidth': '0.17G', 'Range': '0.17G', 'DepressionAngle': '0.17G',
        'LinearDynamicRange': '0.17G', 'AircraftHeading': '0.17G', 'AircraftTrackAngle': '0.17G',}
    # descriptors
    Name = StringDescriptor(
        'Name', _required,
        docstring='The name of the sensor')  # type: Optional[str]
    SensorMfg = StringDescriptor(
        'SensorMfg', _required,
        docstring='The manufacturer of the sensor')  # type: Optional[str]
    OperatingAgency = StringDescriptor(
        'OperatingAgency', _required,
        docstring='The agency or company that operates the sensor')  # type: Optional[str]
    Type = StringDescriptor(
        'Type', _required,
        docstring='The type of sensor (i.e SAR or EO)')  # type: str
    Mode = StringDescriptor(
        'Mode', _required,
        docstring='Sensor operating mode')  # type: Optional[str]
    Band = StringDescriptor(
        'Band', _required,
        docstring='designation of the sensor frequency band')  # type: Optional[str]
    Bandwidth = FloatDescriptor(
        'Bandwidth', _required,
        docstring='Radio Frequency bandwidth of the sensor system in GHz')  # type: Optional[float]
    CenterFrequency = FloatDescriptor(
        'CenterFrequency', _required,
        docstring='Center operating frequency of the sensor system in GHz')  # type: Optional[float]
    NearRange = FloatDescriptor(
        'NearRange', _required,
        docstring='The slant range distance measured from the sensor to the '
                  'near range of the image')  # type: Optional[float]
    SlantRangeSwathWidth = FloatDescriptor(
        'SlantRangeSwathWidth', _required,
        docstring='The width of the image as measured in the slant range'
    )  # type: Optional[float]
    Polarization = StringDescriptor(
        'Polarization', _required,
        docstring='The polarization of the transmitted/received signals')  # type: Optional[str]
    Range = FloatDescriptor(
        'Range', _required,
        docstring='Measured slant range between the sensor aperture '
                  'and the scene center')  # type: float
    DepressionAngle = FloatDescriptor(
        'DepressionAngle', _required,
        docstring='Measured depression angle between the sensor line-of-sight '
                  'and the local horizontal reference plane')  # type: float
    LinearDynamicRange = FloatDescriptor(
        'LinearDynamicRange', _required,
        docstring="The span of the signal amplitudes (or power levels) over "
                  "which the system's response is linear. Typically the ratio "
                  "of the largest input signal that causes a 1 db compression "
                  "in receiver dynamic gain and the minimum signal defined by "
                  "receiver noise.")  # type: Optional[float]
    BeamWidth = SerializableDescriptor(
        'BeamWidth', BeamWidthType, _required,
        docstring='The width of the radar beam at its half power'
    )  # type: Optional[BeamWidthType]
    Aimpoint = SerializableDescriptor(
        'Aimpoint', LatLonEleType, _required,
        docstring='The sensor aim point')  # type: LatLonEleType
    AircraftHeading = FloatDescriptor(
        'AircraftHeading', _required,
        docstring='Aircraft heading relative to True North, in degrees'
    )  # type: Optional[float]
    AircraftTrackAngle = FloatDescriptor(
        'AircraftTrackAngle', _required,
        docstring='The bearing from the aircraft position at the first pulse '
                  'to the aircraft position at the last')  # type: Optional[float]
    Look = StringEnumDescriptor(
        'Look', {'Left', 'Right', 'Nadir'}, _required,
        docstring='Direction of the sensor look angle relative to aircraft '
                  'motion')  # type: str
    SquintAngle = SerializableDescriptor(
        'SquintAngle', SquintAngleType, _required,
        docstring='Measured angle between the sensor line-of-sight and the '
                  'lateral axis of the aircraft')  # type: SquintAngleType
    AircraftLocation = SerializableDescriptor(
        'AircraftLocation', AircraftLocationType, _required,
        docstring='The aircraft location (at scene center COA time?)')  # type: AircraftLocationType
    AircraftVelocity = SerializableDescriptor(
        'AircraftVelocity', XYZType, _required,
        docstring='Aircraft velocity in ECEF coordinates (at scene center COA time?)')  # type: XYZType
    FlightNumber = IntegerDescriptor(
        'FlightNumber', _required,
        docstring='The aircraft flight number')  # type: Optional[int]
    PassNumber = IntegerDescriptor(
        'PassNumber', _required,
        docstring='The aircraft pass number')  # type: Optional[int]

    def __init__(self, Name=None, SensorMfg=None, OperatingAgency=None,
                 Type=None, Mode=None, Band=None, Bandwidth=None,
                 CenterFrequency=None, NearRange=None, SlantRangeSwathWidth=None,
                 Polarization=None, Range=None, DepressionAngle=None,
                 LinearDynamicRange=None, BeamWidth=None, Aimpoint=None,
                 AircraftHeading=None, AircraftTrackAngle=None,
                 Look=None, SquintAngle=None,
                 AircraftLocation=None, AircraftVelocity=None,
                 FlightNumber=None, PassNumber=None, **kwargs):
        """
        Parameters
        ----------
        Name : None|str
        SensorMfg : None|str
        OperatingAgency : None|str
        Type : str
        Mode : None|str
        Band : None|str
        Bandwidth : None|float
        CenterFrequency : None|float
        NearRange : None|float
        SlantRangeSwathWidth : None|float
        Polarization : None|str
        Range : float
        DepressionAngle : float
        LinearDynamicRange : None|float
        BeamWidth : BeamWidthType
        Aimpoint : LatLonEleType|numpy.ndarray|list|tuple
        AircraftHeading : None|float
        AircraftTrackAngle : None|float
        Look : str
        SquintAngle : SquintAngleType
        AircraftLocation : AircraftLocationType|numpy.ndarray|list|tuple
        AircraftVelocity : XYZType|numpy.ndarray|list|tuple
        FlightNumber : None|int
        PassNumber : None|int

        kwargs
            Other keyword arguments
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Name = Name
        self.SensorMfg = SensorMfg
        self.OperatingAgency = OperatingAgency
        self.Type = Type
        self.Mode = Mode
        self.Band = Band
        self.Bandwidth = Bandwidth
        self.CenterFrequency = CenterFrequency
        self.NearRange = NearRange
        self.SlantRangeSwathWidth = SlantRangeSwathWidth
        self.Polarization = Polarization
        self.Range = Range
        self.DepressionAngle = DepressionAngle
        self.LinearDynamicRange = LinearDynamicRange
        self.BeamWidth = BeamWidth
        self.Aimpoint = Aimpoint
        self.AircraftHeading = AircraftHeading
        self.AircraftTrackAngle = AircraftTrackAngle
        self.Look = Look
        self.SquintAngle = SquintAngle
        self.AircraftLocation = AircraftLocation
        self.AircraftVelocity = AircraftVelocity
        self.FlightNumber = FlightNumber
        self.PassNumber = PassNumber
        super(SensorInfoType, self).__init__(**kwargs)

    @classmethod
    def from_sicd(cls, sicd):
        """
        Construct the sensor info from a sicd structure

        Parameters
        ----------
        sicd : SICDType

        Returns
        -------
        SensorInfoType
        """

        transmit_freq_proc = sicd.ImageFormation.TxFrequencyProc
        center_freq = 0.5*(transmit_freq_proc.MinProc + transmit_freq_proc.MaxProc)*1e-9
        polarization = sicd.ImageFormation.get_polarization().replace(':', '')
        look = 'Left' if sicd.SCPCOA.SideOfTrack == 'L' else 'Right'
        slant_squint = 90 - sicd.SCPCOA.DopplerConeAng
        ground_squint = 90 - numpy.rad2deg(
            numpy.arccos(
                numpy.cos(numpy.deg2rad(sicd.SCPCOA.DopplerConeAng)) /
                numpy.cos(numpy.deg2rad(sicd.SCPCOA.GrazeAng))
            )
        )
        arp_pos_llh = ecf_to_geodetic(sicd.SCPCOA.ARPPos.get_array())
        return SensorInfoType(
            Name=sicd.CollectionInfo.CollectorName,
            Type='SAR',
            Mode=sicd.CollectionInfo.RadarMode.ModeType,
            Band=sicd.ImageFormation.get_transmit_band_name(),
            CenterFrequency=center_freq,
            Polarization=polarization,
            Range=sicd.SCPCOA.SlantRange,
            DepressionAngle=sicd.SCPCOA.GrazeAng,
            Aimpoint=sicd.GeoData.SCP.LLH.get_array(),
            Look=look,
            SquintAngle=SquintAngleType(SlantPlane=slant_squint, GroundPlane=ground_squint),
            AircraftLocation=arp_pos_llh,
            AircraftVelocity=sicd.SCPCOA.ARPVel.get_array())
Exemple #10
0
class GlobalType(Serializable):
    """
    The Global type definition.
    """

    _fields = ('DomainType', 'SGN', 'Timeline', 'FxBand', 'TOASwath',
               'TropoParameters', 'IonoParameters')
    _required = ('DomainType', 'SGN', 'Timeline', 'FxBand', 'TOASwath')
    # descriptors
    DomainType = StringEnumDescriptor(
        'DomainType', ('FX', 'TOA'),
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Indicates the domain represented by the sample dimension of the '
        'CPHD signal array(s), where "FX" denotes Transmit Frequency, and '
        '"TOA" denotes Difference in Time of Arrival')  # type: str
    SGN = IntegerEnumDescriptor(
        'SGN', (-1, 1),
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Phase SGN applied to compute target signal phase as a function of '
        r'target :math:`\Delta TOA^{TGT}`. Target phase in cycles. '
        r'For simple phase model :math:`Phase(fx) = SGN \times fx \times \Delta TOA^{TGT}` '
        r'In TOA domain, phase of the mainlobe peak '
        r':math:`Phase(\Delta TOA^{TGT}) = SGN \times fx_C \times \Delta TOA^{TGT}`'
        '.')  # type: int
    Timeline = SerializableDescriptor(
        'Timeline',
        TimelineType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the collection times for the data contained '
        'in the product')  # type: TimelineType
    FxBand = SerializableDescriptor(
        'FxBand',
        FxBandType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the FX frequency limits for the signal array(s) '
        'contained in the product.')  # type: FxBandType
    TOASwath = SerializableDescriptor(
        'TOASwath',
        TOASwathType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the time-of-arrival (TOA) swath limits for the '
        'signal array(s) contained in the product.')  # type: TOASwathType
    TropoParameters = SerializableDescriptor(
        'TropoParameters',
        TropoParametersType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters used to compute the propagation delay due to the '
        'troposphere.')  # type: Union[None, TropoParametersType]
    IonoParameters = SerializableDescriptor(
        'IonoParameters',
        IonoParametersType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters used to compute propagation effects due to the '
        'ionosphere.')  # type: Union[None, IonoParametersType]

    def __init__(self,
                 DomainType=None,
                 SGN=None,
                 Timeline=None,
                 FxBand=None,
                 TOASwath=None,
                 TropoParameters=None,
                 IonoParameters=None,
                 **kwargs):
        """

        Parameters
        ----------
        DomainType : str
        SGN : int
        Timeline : TimelineType
        FxBand : FxBandType|numpy.ndarray|list|tuple
        TOASwath : TOASwathType|numpy.ndarray|list|tuple
        TropoParameters : None|TropoParametersType
        IonoParameters : None|IonoParametersType
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.DomainType = DomainType
        self.SGN = SGN
        self.Timeline = Timeline
        self.FxBand = FxBand
        self.TOASwath = TOASwath
        self.TropoParameters = TropoParameters
        self.IonoParameters = IonoParameters
        super(GlobalType, self).__init__(**kwargs)
Exemple #11
0
class PFAType(Serializable):
    """Parameters for the Polar Formation Algorithm."""
    _fields = ('FPN', 'IPN', 'PolarAngRefTime', 'PolarAngPoly',
               'SpatialFreqSFPoly', 'Krg1', 'Krg2', 'Kaz1', 'Kaz2', 'STDeskew')
    _required = ('FPN', 'IPN', 'PolarAngRefTime', 'PolarAngPoly',
                 'SpatialFreqSFPoly', 'Krg1', 'Krg2', 'Kaz1', 'Kaz2')
    _numeric_format = {
        'PolarAngRefTime': '0.16G',
        'Krg1': '0.16G',
        'Krg2': '0.16G',
        'Kaz1': '0.16G',
        'Kaz2': '0.16G'
    }
    # descriptors
    FPN = UnitVectorDescriptor(
        'FPN',
        XYZType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Focus Plane unit normal in ECF coordinates. Unit vector FPN points away from the center of '
        'the Earth.')  # type: XYZType
    IPN = UnitVectorDescriptor(
        'IPN',
        XYZType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Image Formation Plane unit normal in ECF coordinates. Unit vector IPN points away from the '
        'center of the Earth.')  # type: XYZType
    PolarAngRefTime = FloatDescriptor(
        'PolarAngRefTime',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Polar image formation reference time *(in seconds)*. Polar Angle = 0 at the reference time. '
        'Measured relative to collection start. *Note: Reference time is typically set equal to the SCP '
        'COA time but may be different.*')  # type: float
    PolarAngPoly = SerializableDescriptor(
        'PolarAngPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Polynomial function that yields Polar Angle *(in radians)* as function of time '
        'relative to Collection Start.')  # type: Poly1DType
    SpatialFreqSFPoly = SerializableDescriptor(
        'SpatialFreqSFPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Polynomial that yields the *Spatial Frequency Scale Factor (KSF)* as a function of Polar '
        r'Angle. That is, :math:`Polar Angle[radians] \to KSF[dimensionless]`. Used to scale RF '
        'frequency *(fx, Hz)* to aperture spatial frequency *(Kap, cycles/m)*. Where,'
        r':math:`Kap = fx\cdot (2/c)\cdot KSF`, and `Kap` is the effective spatial '
        'frequency in the polar aperture.')  # type: Poly1DType
    Krg1 = FloatDescriptor(
        'Krg1',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Minimum *range spatial frequency (Krg)* output from the polar to rectangular '
        'resampling.')  # type: float
    Krg2 = FloatDescriptor(
        'Krg2',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Maximum *range spatial frequency (Krg)* output from the polar to rectangular '
        'resampling.')  # type: float
    Kaz1 = FloatDescriptor(
        'Kaz1',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Minimum *azimuth spatial frequency (Kaz)* output from the polar to rectangular '
        'resampling.')  # type: float
    Kaz2 = FloatDescriptor(
        'Kaz2',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Maximum *azimuth spatial frequency (Kaz)* output from the polar to rectangular '
        'resampling.')  # type: float
    STDeskew = SerializableDescriptor(
        'STDeskew',
        STDeskewType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters to describe image domain slow time *(ST)* Deskew processing.'
    )  # type: STDeskewType

    def __init__(self,
                 FPN=None,
                 IPN=None,
                 PolarAngRefTime=None,
                 PolarAngPoly=None,
                 SpatialFreqSFPoly=None,
                 Krg1=None,
                 Krg2=None,
                 Kaz1=None,
                 Kaz2=None,
                 STDeskew=None,
                 **kwargs):
        """

        Parameters
        ----------
        FPN : XYZType|numpy.ndarray|list|tuple
        IPN : XYZType|numpy.ndarray|list|tuple
        PolarAngRefTime : float
        PolarAngPoly : Poly1DType|numpy.ndarray|list|tuple
        SpatialFreqSFPoly : Poly1DType|numpy.ndarray|list|tuple
        Krg1 : float
        Krg2 : float
        Kaz1 : float
        Kaz2 : float
        STDeskew : STDeskewType
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.FPN = FPN
        self.IPN = IPN
        self.PolarAngRefTime = PolarAngRefTime
        self.PolarAngPoly = PolarAngPoly
        self.SpatialFreqSFPoly = SpatialFreqSFPoly
        self.Krg1, self.Krg2 = Krg1, Krg2
        self.Kaz1, self.Kaz2 = Kaz1, Kaz2
        self.STDeskew = STDeskew
        super(PFAType, self).__init__(**kwargs)

    def pfa_polar_coords(self, Position, SCP, times):
        """
        Calculate the PFA parameters necessary for mapping phase history to polar coordinates.

        Parameters
        ----------
        Position : sarpy.io.complex.sicd_elements.Position.PositionType
        SCP : numpy.ndarray
        times : numpy.ndarray|float|int

        Returns
        -------
        (numpy.ndarray, numpy.ndarray)|(float, float)
            `(k_a, k_sf)`, where `k_a` is polar angle, and `k_sf` is spatial
            frequency scale factor. The shape of the output array (or scalar) will
            match the shape of the `times` array (or scalar).
        """
        def project_to_image_plane(points):
            # type: (numpy.ndarray) -> numpy.ndarray
            # project into the image plane along line normal to the focus plane
            offset = (SCP - points).dot(ipn) / fpn.dot(ipn)
            if offset.ndim == 0:
                return points + offset * fpn
            else:
                return points + numpy.outer(offset, fpn)

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

        ipn = self.IPN.get_array(dtype='float64')
        fpn = self.FPN.get_array(dtype='float64')
        if isinstance(times, (float, int)) or times.ndim == 0:
            o_shape = None
            times = numpy.array([
                times,
            ], dtype='float64')
        else:
            o_shape = times.shape
            times = numpy.reshape(times, (-1, ))
        positions = Position.ARPPoly(times)
        reference_position = Position.ARPPoly(self.PolarAngRefTime)
        image_plane_positions = project_to_image_plane(positions)
        image_plane_coa = project_to_image_plane(reference_position)

        # establish image plane coordinate system
        ip_x = image_plane_coa - SCP
        ip_x /= numpy.linalg.norm(ip_x)
        ip_y = numpy.cross(ip_x, ipn)

        # compute polar angle of sensor position in image plane
        ip_range = image_plane_positions - SCP
        ip_range /= numpy.linalg.norm(ip_range, axis=1)[:, numpy.newaxis]
        k_a = -numpy.arctan2(ip_range.dot(ip_y), ip_range.dot(ip_x))

        # compute the spatial frequency scale factor
        range_vectors = positions - SCP
        range_vectors /= numpy.linalg.norm(range_vectors,
                                           axis=1)[:, numpy.newaxis]
        sin_graze = range_vectors.dot(fpn)
        sin_graze_ip = ip_range.dot(fpn)
        k_sf = numpy.sqrt(
            (1 - sin_graze * sin_graze) / (1 - sin_graze_ip * sin_graze_ip))
        if o_shape is None:
            return k_a[0], k_sf[0]
        elif len(o_shape) > 1:
            return numpy.reshape(k_a, o_shape), numpy.reshape(k_sf, o_shape)
        else:
            return k_a, k_sf

    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

    def _check_polar_ang_ref(self):
        """
        Checks the polar angle origin makes sense.

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

        if self.PolarAngPoly is None or self.PolarAngRefTime is None:
            return True

        cond = True
        polar_angle_ref = self.PolarAngPoly(self.PolarAngRefTime)
        if abs(polar_angle_ref) > 1e-4:
            self.log_validity_error(
                'The PolarAngPoly evaluated at PolarAngRefTime yields {}, which should be 0'
                .format(polar_angle_ref))
            cond = False
        return cond

    def _basic_validity_check(self):
        condition = super(PFAType, self)._basic_validity_check()
        condition &= self._check_polar_ang_ref()
        return condition
Exemple #12
0
class PVPType(Serializable):
    _fields = ('RcvTime', 'RcvPos', 'RcvVel', 'RefPhi0', 'RefFreq', 'DFIC0',
               'FICRate', 'FRCV1', 'FRCV2', 'DGRGC', 'SIGNAL', 'AmpSF',
               'RcvAntenna', 'TxPulse', 'AddedPVP')
    _required = ('RcvTime', 'RcvPos', 'RcvVel', 'RefPhi0', 'RefFreq', 'DFIC0',
                 'FICRate', 'FRCV1', 'FRCV2')
    _collections_tags = {'AddedPVP': {'array': False, 'child_tag': 'AddedPVP'}}
    # descriptors
    RcvTime = SerializableDescriptor(
        'RcvTime',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterF8
    RcvPos = SerializableDescriptor(
        'RcvPos',
        PerVectorParameterXYZ,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterXYZ
    RcvVel = SerializableDescriptor(
        'RcvVel',
        PerVectorParameterXYZ,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterXYZ
    RefPhi0 = SerializableDescriptor(
        'RefPhi0',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterF8
    RefFreq = SerializableDescriptor(
        'RefFreq',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterF8
    DFIC0 = SerializableDescriptor('DFIC0',
                                   PerVectorParameterF8,
                                   _required,
                                   strict=DEFAULT_STRICT,
                                   docstring='')  # type: PerVectorParameterF8
    FICRate = SerializableDescriptor(
        'FICRate',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: PerVectorParameterF8
    FRCV1 = SerializableDescriptor('FRCV1',
                                   PerVectorParameterF8,
                                   _required,
                                   strict=DEFAULT_STRICT,
                                   docstring='')  # type: PerVectorParameterF8
    FRCV2 = SerializableDescriptor('FRCV2',
                                   PerVectorParameterF8,
                                   _required,
                                   strict=DEFAULT_STRICT,
                                   docstring='')  # type: PerVectorParameterF8
    DGRGC = SerializableDescriptor(
        'DGRGC',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, PerVectorParameterF8]
    SIGNAL = SerializableDescriptor(
        'SIGNAL',
        PerVectorParameterI8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, PerVectorParameterI8]
    AmpSF = SerializableDescriptor(
        'AmpSF',
        PerVectorParameterF8,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, PerVectorParameterF8]
    RcvAntenna = SerializableDescriptor(
        'RcvAntenna',
        RcvAntennaType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, RcvAntennaType]
    TxPulse = SerializableDescriptor(
        'TxPulse', TxPulseType, _required, strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, TxPulseType]
    AddedPVP = SerializableListDescriptor(
        'AddedPVP',
        UserDefinedPVPType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, List[UserDefinedPVPType]]

    def __init__(self,
                 RcvTime=None,
                 RcvPos=None,
                 RcvVel=None,
                 RefPhi0=None,
                 RefFreq=None,
                 DFIC0=None,
                 FICRate=None,
                 FRCV1=None,
                 FRCV2=None,
                 DGRGC=None,
                 SIGNAL=None,
                 AmpSF=None,
                 RcvAntenna=None,
                 TxPulse=None,
                 AddedPVP=None,
                 **kwargs):
        """

        Parameters
        ----------
        RcvTime : PerVectorParameterF8
        RcvPos : PerVectorParameterXYZ
        RcvVel : PerVectorParameterXYZ
        RefPhi0 : PerVectorParameterF8
        RefFreq : PerVectorParameterF8
        DFIC0 : PerVectorParameterF8
        FICRate : PerVectorParameterF8
        FRCV1 : PerVectorParameterF8
        FRCV2 : PerVectorParameterF8
        DGRGC : None|PerVectorParameterF8
        SIGNAL : None|PerVectorParameterI8
        AmpSF : None|PerVectorParameterF8
        RcvAntenna : None|RcvAntennaType
        TxPulse : None|TxPulseType
        AddedPVP : None|List[UserDefinedPVPType]
        kwargs
        """
        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.RcvTime = RcvTime
        self.RcvPos = RcvPos
        self.RcvVel = RcvVel
        self.RefPhi0 = RefPhi0
        self.RefFreq = RefFreq
        self.DFIC0 = DFIC0
        self.FICRate = FICRate
        self.FRCV1 = FRCV1
        self.FRCV2 = FRCV2
        self.DGRGC = DGRGC
        self.SIGNAL = SIGNAL
        self.AmpSF = AmpSF
        self.RcvAntenna = RcvAntenna
        self.TxPulse = TxPulse
        self.AddedPVP = AddedPVP
        super(PVPType, self).__init__(**kwargs)

    def get_size(self):
        """
        Gets the size in bytes of each vector.

        Returns
        -------
        int
        """
        def get_num_words(obj):
            sz = getattr(obj, 'Size')
            if sz is not None:
                return sz
            sz = 0

            # noinspection PyProtectedMember
            for fld in obj._fields:
                fld_val = getattr(obj, fld)
                if fld_val is not None:
                    if fld_val.array:
                        for arr_val in fld_val:
                            sz += get_num_words(arr_val)
                    else:
                        sz += get_num_words(fld_val)
            return sz

        return get_num_words(self) * BYTES_PER_WORD

    def get_offset_size_format(self, field):
        """
        Get the Offset (in bytes), Size (in bytes) for the given field,
        as well as the corresponding struct format string.

        Parameters
        ----------
        field : str
            The desired field name.

        Returns
        -------
        None|(int, int, str)
        """
        def osf_tuple(val_in):
            return val_in.Offset * BYTES_PER_WORD, val_in.Size * BYTES_PER_WORD, homogeneous_dtype(
                val_in.Format).char

        # noinspection PyProtectedMember
        if field in self._fields[:-1]:
            val = getattr(self, field)
            if val is None:
                return None
            return osf_tuple(val)
        elif self.RcvAntenna and field in self.RcvAntenna._fields:
            val = getattr(self.RcvAntenna, field)
            if val is None:
                return None
            return osf_tuple(val)
        elif self.TxPulse and field in self.TxPulse._fields:
            val = getattr(self.TxPulse, field)
            if val is None:
                return None
            return osf_tuple(val)
        elif self.TxPulse and self.TxPulse.TxAntenna and field in self.TxPulse.TxAntenna._fields:
            val = getattr(self.TxPulse.TxAntenna, field)
            if val is None:
                return None
            return osf_tuple(val)
        else:
            if self.AddedPVP is None:
                return None
            for val in self.AddedPVP:
                if field == val.Name:
                    return osf_tuple(val)
        return None

    def get_vector_dtype(self):
        """
        Gets the dtype for the corresponding structured array for the full PVP set.

        Returns
        -------
        numpy.dtype
            This will be a compound dtype for a structured array.
        """

        names = []
        formats = []
        offsets = []

        for field in self._fields:
            val = getattr(self, field)
            if val is None:
                continue
            elif field == "AddedPVP":
                for entry in val:
                    names.append(entry.Name)
                    formats.append(binary_format_string_to_dtype(entry.Format))
                    offsets.append(entry.Offset * BYTES_PER_WORD)
            elif field == 'RcvAntenna' or field == 'TxPulse':
                continue
            else:
                names.append(field)
                formats.append(binary_format_string_to_dtype(val.Format))
                offsets.append(val.Offset * BYTES_PER_WORD)

        if self.RcvAntenna is not None:
            # noinspection PyProtectedMember
            for field in self.RcvAntenna._fields:
                val = getattr(self.RcvAntenna, field)
                if val is None:
                    continue
                else:
                    names.append(field)
                    formats.append(binary_format_string_to_dtype(val.Format))
                    offsets.append(val.Offset * BYTES_PER_WORD)

        if self.TxPulse is not None:
            # noinspection PyProtectedMember
            for field in self.TxPulse._fields:
                val = getattr(self.TxPulse, field)
                if val is None:
                    continue
                elif field == 'TxAntenna':
                    continue
                else:
                    names.append(field)
                    formats.append(binary_format_string_to_dtype(val.Format))
                    offsets.append(val.Offset * BYTES_PER_WORD)
            if self.TxPulse.TxAntenna is not None:
                # noinspection PyProtectedMember
                for field in self.TxPulse.TxAntenna._fields:
                    val = getattr(self.TxPulse.TxAntenna, field)
                    if val is None:
                        continue
                    else:
                        names.append(field)
                        formats.append(
                            binary_format_string_to_dtype(val.Format))
                        offsets.append(val.Offset * BYTES_PER_WORD)

        return numpy.dtype({
            'names': names,
            'formats': formats,
            'offsets': offsets
        })
Exemple #13
0
class TxPulseType(Serializable):
    _fields = ('TxTime', 'TxPos', 'TxVel', 'FX1', 'FX2', 'TXmt', 'TxLFM',
               'TxAntenna')
    _required = ('TxTime', 'TxPos', 'TxVel', 'FX1', 'FX2', 'TXmt')
    # descriptors
    TxTime = SerializableDescriptor('TxTime',
                                    PerVectorParameterF8,
                                    _required,
                                    strict=DEFAULT_STRICT,
                                    docstring='')  # type: PerVectorParameterF8
    TxPos = SerializableDescriptor('TxPos',
                                   PerVectorParameterXYZ,
                                   _required,
                                   strict=DEFAULT_STRICT,
                                   docstring='')  # type: PerVectorParameterXYZ
    TxVel = SerializableDescriptor('TxVel',
                                   PerVectorParameterXYZ,
                                   _required,
                                   strict=DEFAULT_STRICT,
                                   docstring='')  # type: PerVectorParameterXYZ
    FX1 = SerializableDescriptor('FX1',
                                 PerVectorParameterF8,
                                 _required,
                                 strict=DEFAULT_STRICT,
                                 docstring='')  # type: PerVectorParameterF8
    FX2 = SerializableDescriptor('FX2',
                                 PerVectorParameterF8,
                                 _required,
                                 strict=DEFAULT_STRICT,
                                 docstring='')  # type: PerVectorParameterF8
    TXmt = SerializableDescriptor('TXmt',
                                  PerVectorParameterF8,
                                  _required,
                                  strict=DEFAULT_STRICT,
                                  docstring='')  # type: PerVectorParameterF8
    TxLFM = SerializableDescriptor(
        'TxLFM',
        PerVectorParameterTxLFM,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, PerVectorParameterTxLFM]
    TxAntenna = SerializableDescriptor(
        'TxAntenna',
        TxAntennaType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='')  # type: Union[None, TxAntennaType]

    def __init__(self,
                 TxTime=None,
                 TxPos=None,
                 TxVel=None,
                 FX1=None,
                 FX2=None,
                 TXmt=None,
                 TxLFM=None,
                 TxAntenna=None,
                 **kwargs):
        """

        Parameters
        ----------
        TxTime : PerVectorParameterF8
        TxPos : PerVectorParameterXYZ
        TxVel : PerVectorParameterXYZ
        FX1 : PerVectorParameterF8
        FX2 : PerVectorParameterF8
        TXmt : PerVectorParameterF8
        TxLFM : None|PerVectorParameterTxLFM
        TxAntenna : None|TxAntennaType
        kwargs
        """
        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.TxTime = TxTime
        self.TxPos = TxPos
        self.TxVel = TxVel
        self.FX1 = FX1
        self.FX2 = FX2
        self.TXmt = TXmt
        self.TxLFM = TxLFM
        self.TxAntenna = TxAntenna
        super(TxPulseType, self).__init__(**kwargs)
Exemple #14
0
class GlobalType(Serializable):
    """
    The Global type definition.
    """

    _fields = (
        'DomainType', 'PhaseSGN', 'RefFreqIndex', 'CollectStart',
        'CollectDuration', 'TxTime1', 'TxTime2', 'ImageArea')
    _required = (
        'DomainType', 'PhaseSGN', 'CollectStart', 'CollectDuration',
        'TxTime1', 'TxTime2', 'ImageArea')
    _numeric_format = {
        'CollectDuration': FLOAT_FORMAT, 'TxTime1': FLOAT_FORMAT, 'TxTime2': FLOAT_FORMAT}
    # descriptors
    DomainType = StringEnumDescriptor(
        'DomainType', ('FX', 'TOA'), _required, strict=DEFAULT_STRICT,
        docstring='Indicates the domain represented by the sample dimension of the '
                  'CPHD signal array(s), where "FX" denotes Transmit Frequency, and '
                  '"TOA" denotes Difference in Time of Arrival')  # type: str
    PhaseSGN = IntegerEnumDescriptor(
        'PhaseSGN', (-1, 1), _required, strict=DEFAULT_STRICT,
        docstring='Phase SGN applied to compute target signal phase as a function of '
                  r'target :math:`\Delta TOA^{TGT}`. Target phase in cycles. '
                  r'For simple phase model :math:`Phase(fx) = SGN \times fx \times \Delta TOA^{TGT}` '
                  r'In TOA domain, phase of the mainlobe peak '
                  r':math:`Phase(\Delta TOA^{TGT}) = SGN \times fx_C \times \Delta TOA^{TGT}`'
                  '.')  # type: int
    RefFreqIndex = IntegerDescriptor(
        'RefFreqIndex', _required, strict=DEFAULT_STRICT,
        docstring='Indicates if the RF frequency values are expressed as offsets from '
                  'a reference frequency (RefFreq).')  # type: Union[None, int]
    CollectStart = DateTimeDescriptor(
        'CollectStart', _required, strict=DEFAULT_STRICT, numpy_datetime_units='us',
        docstring='Collection Start date and time (UTC). Time reference used for times '
                  'measured from collection start (i.e. slow time t = 0). For bistatic '
                  'collections, the time is the transmit platform collection '
                  'start time. The default display precision is microseconds, but this '
                  'does not that accuracy in value.')  # type: numpy.datetime64
    CollectDuration = FloatDescriptor(
        'CollectDuration', _required, strict=DEFAULT_STRICT,
        docstring='The duration of the collection, in seconds.')  # type: float
    TxTime1 = FloatDescriptor(
        'TxTime1', _required, strict=DEFAULT_STRICT, bounds=(0, None),
        docstring='Earliest TxTime value for any signal vector in the product. '
                  'Time relative to Collection Start in seconds.')  # type: float
    TxTime2 = FloatDescriptor(
        'TxTime2', _required, strict=DEFAULT_STRICT, bounds=(0, None),
        docstring='Latest TxTime value for any signal vector in the product. '
                  'Time relative to Collection Start in seconds.')  # type: float
    ImageArea = SerializableDescriptor(
        'ImageArea', ImageAreaType, _required, strict=DEFAULT_STRICT,
        docstring='Parameters describing the ground area covered by this '
                  'product.')  # type: ImageAreaType

    def __init__(self, DomainType=None, PhaseSGN=None, RefFreqIndex=None, CollectStart=None,
                 CollectDuration=None, TxTime1=None, TxTime2=None, ImageArea=None, **kwargs):
        """

        Parameters
        ----------
        DomainType : str
        PhaseSGN : int
        RefFreqIndex : None|int
        CollectStart : numpy.datetime64|datetime.datetime|str
        CollectDuration : float
        TxTime1 : float
        TxTime2 : float
        ImageArea : ImageAreaType
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.DomainType = DomainType
        self.PhaseSGN = PhaseSGN
        self.RefFreqIndex = RefFreqIndex
        self.CollectStart = CollectStart
        self.CollectDuration = CollectDuration
        self.TxTime1 = TxTime1
        self.TxTime2 = TxTime2
        self.ImageArea = ImageArea
        super(GlobalType, self).__init__(**kwargs)
Exemple #15
0
class GeoDataType(Serializable):
    """Container specifying the image coverage area in geographic coordinates."""
    _fields = ('EarthModel', 'SCP', 'ImageCorners', 'ValidData')
    _required = ('EarthModel', 'SCP', 'ImageCorners')
    _collections_tags = {
        'ValidData': {'array': True, 'child_tag': 'Vertex'},
        'ImageCorners': {'array': True, 'child_tag': 'ICP'},
    }
    # other class variables
    _EARTH_MODEL_VALUES = ('WGS_84', )
    # descriptors
    EarthModel = StringEnumDescriptor(
        'EarthModel', _EARTH_MODEL_VALUES, _required, strict=True, default_value='WGS_84',
        docstring='The Earth Model.')  # type: str
    SCP = SerializableDescriptor(
        'SCP', SCPType, _required, strict=DEFAULT_STRICT,
        docstring='The Scene Center Point *(SCP)* in full (global) image. This is the '
                  'precise location.')  # type: SCPType
    ImageCorners = SerializableCPArrayDescriptor(
        'ImageCorners', LatLonCornerStringType, _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='The geographic image corner points array. Image corners points projected to the '
                  'ground/surface level. Points may be projected to the same height as the SCP if ground/surface '
                  'height data is not available. The corner positions are approximate geographic locations and '
                  'not intended for analytical use.')  # type: Union[SerializableCPArray, List[LatLonCornerStringType]]
    ValidData = SerializableArrayDescriptor(
        'ValidData', LatLonArrayElementType, _collections_tags, _required,
        strict=DEFAULT_STRICT, minimum_length=3,
        docstring='The full image array includes both valid data and some zero filled pixels.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self, EarthModel='WGS_84', SCP=None, ImageCorners=None, ValidData=None, GeoInfos=None, **kwargs):
        """

        Parameters
        ----------
        EarthModel : str
        SCP : SCPType
        ImageCorners : SerializableCPArray|List[LatLonCornerStringType]|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        GeoInfos : List[GeoInfoType]
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.EarthModel = EarthModel
        self.SCP = SCP
        self.ImageCorners = ImageCorners
        self.ValidData = ValidData

        self._GeoInfos = []
        if GeoInfos is None:
            pass
        elif isinstance(GeoInfos, GeoInfoType):
            self.setGeoInfo(GeoInfos)
        elif isinstance(GeoInfos, (list, tuple)):
            for el in GeoInfos:
                self.setGeoInfo(el)
        else:
            raise ValueError('GeoInfos got unexpected type {}'.format(type(GeoInfos)))
        super(GeoDataType, self).__init__(**kwargs)

    def derive(self):
        """
        Populates any potential derived data in GeoData. Is expected to be called by
        the `SICD` parent as part of a more extensive derived data effort.

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

        pass

    @property
    def GeoInfos(self):
        """
        List[GeoInfoType]: list of GeoInfos.
        """

        return self._GeoInfos

    def getGeoInfo(self, key):
        """
        Get the GeoInfo(s) with name attribute == `key`

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfos if entry.name == key]

    def setGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfos list.

        Parameters
        ----------
        value : GeoInfoType

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

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfos', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfos.append(value)
        else:
            raise TypeError('Trying to set GeoInfo element with unexpected type {}'.format(type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfos', ns_key)
        kwargs['GeoInfos'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(GeoDataType, cls).from_node(node, xml_ns, ns_key=ns_key, kwargs=kwargs)

    def to_node(self, doc, tag, ns_key=None, parent=None, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        node = super(GeoDataType, self).to_node(
            doc, tag, ns_key=ns_key, parent=parent, check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        for entry in self._GeoInfos:
            entry.to_node(doc, 'GeoInfo', ns_key=ns_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoDataType, self).to_dict(check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        if len(self.GeoInfos) > 0:
            out['GeoInfos'] = [entry.to_dict(check_validity=check_validity, strict=strict) for entry in self._GeoInfos]
        return out

    def _basic_validity_check(self):
        condition = super(GeoDataType, self)._basic_validity_check()
        return condition
Exemple #16
0
class DetailImageInfoType(Serializable):
    _fields = ('DataFilename', 'ClassificationMarkings', 'Filetype',
               'DataCheckSum', 'DataSize', 'DataPlane', 'DataDomain',
               'DataType', 'BitsPerSample', 'DataFormat', 'DataByteOrder',
               'NumPixels', 'ImageCollectionDate', 'ZuluOffset',
               'SensorReferencePoint', 'SensorCalibrationFactor',
               'DataCalibrated', 'Resolution', 'PixelSpacing', 'WeightingType',
               'OverSamplingFactor', 'Width_3dB', 'ImageQualityDescription',
               'ImageHeading', 'ImageCorners', 'SlantPlane', 'GroundPlane',
               'SceneCenterReferenceLine')
    _required = ('DataFilename', 'ClassificationMarkings', 'DataPlane',
                 'DataType', 'DataFormat', 'NumPixels', 'ImageCollectionDate',
                 'SensorReferencePoint', 'Resolution', 'PixelSpacing',
                 'WeightingType', 'ImageCorners')
    _tag_overide = {'Width_3dB': '_3dBWidth'}
    # descriptors
    DataFilename = StringDescriptor(
        'DataFilename',
        _required,
        docstring='The base file name to which this information pertains'
    )  # type: str
    ClassificationMarkings = SerializableDescriptor(
        'ClassificationMarkings',
        ClassificationMarkingsType,
        _required,
        docstring='The classification information'
    )  # type: ClassificationMarkingsType
    Filetype = StringDescriptor(
        'Filetype', _required,
        docstring='The image file type')  # type: Optional[str]
    DataCheckSum = StringDescriptor(
        'DataCheckSum',
        _required,
        docstring='The unique 32-bit identifier for the sensor block data'
    )  # type: Optional[str]
    DataSize = IntegerDescriptor(
        'DataSize', _required,
        docstring='The image size in bytes')  # type: Optional[int]
    DataPlane = StringEnumDescriptor('DataPlane', {'Slant', 'Ground'},
                                     _required,
                                     default_value='Slant',
                                     docstring='The image plane.')  # type: str
    DataDomain = StringDescriptor(
        'DataDomain', _required,
        docstring='The image data domain')  # type: Optional[str]
    DataType = StringDescriptor(
        'DataType', _required,
        docstring='The image data type')  # type: Optional[str]
    BitsPerSample = IntegerDescriptor(
        'BitsPerSample', _required,
        docstring='The number of bits per sample')  # type: Optional[int]
    DataFormat = StringDescriptor(
        'DataFormat', _required,
        docstring='The image data format')  # type: str
    DataByteOrder = StringEnumDescriptor(
        'DataByteOrder', {'Big-Endian', 'Little-Endian'},
        _required,
        docstring='The image data byte order.')  # type: Optional[str]
    NumPixels = SerializableDescriptor(
        'NumPixels',
        NumPixelsType,
        _required,
        docstring='The number of image pixels')  # type: NumPixelsType
    ImageCollectionDate = DateTimeDescriptor(
        'ImageCollectionDate',
        _required,
        docstring='The date/time of the image collection in UTC'
    )  # type: Optional[numpy.datetime64]
    ZuluOffset = IntegerDescriptor(
        'ZuluOffset', _required, docstring='The local time offset from UTC'
    )  # type: Optional[int]  # TODO: this isn't always integer
    SensorReferencePoint = StringEnumDescriptor(
        'DataPlane', {'Left', 'Right', 'Top', 'Bottom'},
        _required,
        docstring='Description of the sensor location relative to the scene.'
    )  # type: Optional[str]
    SensorCalibrationFactor = FloatDescriptor(
        'SensorCalibrationFactor',
        _required,
        docstring=
        'Multiplicative factor used to scale raw image data to the return '
        'of a calibrated reference reflector or active source'
    )  # type: Optional[float]
    DataCalibrated = StringDescriptor(
        'DataCalibrated', _required, docstring='Has the data been calibrated?'
    )  # type: Optional[str]  # TODO: this obviously should be a xs:boolean
    Resolution = SerializableDescriptor(
        'Resolution',
        RangeCrossRangeType,
        _required,
        docstring='Resolution (intrinsic) of the sensor system/mode in meters.'
    )  # type: RangeCrossRangeType
    PixelSpacing = SerializableDescriptor(
        'PixelSpacing',
        RangeCrossRangeType,
        _required,
        docstring='Pixel spacing of the image in meters.'
    )  # type: RangeCrossRangeType
    WeightingType = SerializableDescriptor(
        'WeightingType',
        StringRangeCrossRangeType,
        _required,
        docstring='Weighting function applied to the image during formation.'
    )  # type: StringRangeCrossRangeType
    OverSamplingFactor = SerializableDescriptor(
        'OverSamplingFactor',
        RangeCrossRangeType,
        _required,
        docstring='The factor by which the pixel space is oversampled.'
    )  # type: Optional[RangeCrossRangeType]
    Width_3dB = SerializableDescriptor(
        'Width_3dB',
        RangeCrossRangeType,
        _required,
        docstring='The 3 dB system impulse response with, in meters'
    )  # type: Optional[RangeCrossRangeType]
    ImageQualityDescription = StringDescriptor(
        'ImageQualityDescription',
        _required,
        docstring='General description of image quality'
    )  # type: Optional[str]
    ImageHeading = FloatDescriptor(
        'ImageHeading',
        _required,
        docstring='Image heading relative to True North, in degrees'
    )  # type: Optional[float]
    ImageCorners = SerializableDescriptor(
        'ImageCorners',
        ImageCornerType,
        _required,
        docstring='The image corners')  # type: ImageCornerType
    SlantPlane = SerializableDescriptor(
        'SlantPlane',
        PixelSpacingType,
        _required,
        docstring='The slant plane pixel spacing'
    )  # type: Optional[PixelSpacingType]
    GroundPlane = SerializableDescriptor(
        'GroundPlane',
        PixelSpacingType,
        _required,
        docstring='The ground plane pixel spacing'
    )  # type: Optional[PixelSpacingType]
    SceneCenterReferenceLine = FloatDescriptor(
        'SceneCenterReferenceLine',
        _required,
        docstring='The ideal line (heading) at the intersection of the radar '
        'line-of-sight with the horizontal reference plane '
        'created by the forward motion of the aircraft, '
        'in degrees')  # type: Optional[float]

    def __init__(self,
                 DataFilename=None,
                 ClassificationMarkings=None,
                 FileType=None,
                 DataCheckSum=None,
                 DataSize=None,
                 DataPlane='Slant',
                 DataDomain=None,
                 DataType=None,
                 BitsPerSample=None,
                 DataFormat=None,
                 DataByteOrder=None,
                 NumPixels=None,
                 ImageCollectionDate=None,
                 ZuluOffset=None,
                 SensorReferencePoint=None,
                 SensorCalibrationFactor=None,
                 DataCalibrated=None,
                 Resolution=None,
                 PixelSpacing=None,
                 WeightingType=None,
                 OverSamplingFactor=None,
                 Width_3dB=None,
                 ImageQualityDescription=None,
                 ImageHeading=None,
                 ImageCorners=None,
                 SlantPlane=None,
                 GroundPlane=None,
                 SceneCenterReferenceLine=None,
                 **kwargs):
        """
        Parameters
        ----------
        DataFilename : str
        ClassificationMarkings : ClassificationMarkingsType
        FileType : str
        DataCheckSum : None|str
        DataSize : int
        DataPlane : str
        DataDomain : None|str
        DataType : None|str
        BitsPerSample : None|int
        DataFormat : None|str
        DataByteOrder : None|str
        NumPixels : NumPixelsType|numpy.ndarray|list|tuple
        ImageCollectionDate : numpy.datetime64|datetime|date|str
        ZuluOffset : None|int
        SensorReferencePoint : None|str
        SensorCalibrationFactor : None|float
        DataCalibrated : None|str
        Resolution : RangeCrossRangeType|numpy.ndarray|list|tuple
        PixelSpacing : RangeCrossRangeType|numpy.ndarray|list|tuple
        WeightingType : StringRangeCrossRangeType
        OverSamplingFactor : None|RangeCrossRangeType
        Width_3dB : None|RangeCrossRangeType|numpy.ndarray|list|tuple
        ImageQualityDescription : None|str
        ImageHeading : None|float
        ImageCorners : ImageCornerType
        SlantPlane : None|PixelSpacingType
        GroundPlane : None|PixelSpacingType
        SceneCenterReferenceLine : None|float
        kwargs
            Other keyword arguments
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']

        self.DataFilename = DataFilename

        if ClassificationMarkings is None:
            self.ClassificationMarkings = ClassificationMarkingsType()
        else:
            self.ClassificationMarkings = ClassificationMarkings

        self.Filetype = FileType
        self.DataCheckSum = DataCheckSum
        self.DataSize = DataSize
        self.DataPlane = DataPlane
        self.DataDomain = DataDomain
        self.DataType = DataType

        self.BitsPerSample = BitsPerSample
        self.DataFormat = DataFormat
        self.DataByteOrder = DataByteOrder
        self.NumPixels = NumPixels
        self.ImageCollectionDate = ImageCollectionDate
        self.ZuluOffset = ZuluOffset

        self.SensorReferencePoint = SensorReferencePoint
        self.SensorCalibrationFactor = SensorCalibrationFactor
        self.DataCalibrated = DataCalibrated
        self.Resolution = Resolution
        self.PixelSpacing = PixelSpacing
        self.WeightingType = WeightingType
        self.OverSamplingFactor = OverSamplingFactor
        self.Width_3dB = Width_3dB

        self.ImageQualityDescription = ImageQualityDescription
        self.ImageHeading = ImageHeading
        self.ImageCorners = ImageCorners
        self.SlantPlane = SlantPlane
        self.GroundPlane = GroundPlane
        self.SceneCenterReferenceLine = SceneCenterReferenceLine
        super(DetailImageInfoType, self).__init__(**kwargs)

    @classmethod
    def from_sicd(cls, sicd, base_file_name, file_type='NITF02.10'):
        """
        Construct the ImageInfo from the sicd object and given image file name.

        Parameters
        ----------
        sicd : SICDType
        base_file_name : str
        file_type : str
            The file type. This should probably always be NITF02.10 for now.

        Returns
        -------
        DetailImageInfoType
        """

        pixel_type = sicd.ImageData.PixelType
        if pixel_type == 'RE32F_IM32F':
            data_type = 'in-phase/quadrature'
            bits_per_sample = 32
            data_format = 'float'
        elif pixel_type == 'RE16I_IM16I':
            data_type = 'in-phase/quadrature'
            bits_per_sample = 16
            data_format = 'integer'
        elif pixel_type == 'AMP8I_PHS8I':
            data_type = 'magnitude-phase'
            bits_per_sample = 8
            data_format = 'unsigned integer'
        else:
            raise ValueError('Unhandled')

        icps = ImageCornerType(UpperLeft=sicd.GeoData.ImageCorners.FRFC,
                               UpperRight=sicd.GeoData.ImageCorners.FRLC,
                               LowerRight=sicd.GeoData.ImageCorners.LRLC,
                               LowerLeft=sicd.GeoData.ImageCorners.LRFC)

        if sicd.Grid.ImagePlane == 'SLANT':
            data_plane = 'Slant'
        elif sicd.Grid.ImagePlane == 'Ground':
            data_plane = 'Ground'
        else:
            data_plane = None

        return DetailImageInfoType(
            DataFilename=base_file_name,
            ClassificationMarkings=ClassificationMarkingsType(
                Classification=sicd.CollectionInfo.Classification),
            FileType=file_type,
            DataPlane=data_plane,
            DataType=data_type,
            BitsPerSample=bits_per_sample,
            DataFormat=data_format,
            DataByteOrder='Big-Endian',
            NumPixels=(sicd.ImageData.NumRows, sicd.ImageData.NumCols),
            ImageCollectionDate=sicd.Timeline.CollectStart,
            SensorReferencePoint='Top',
            Resolution=(sicd.Grid.Row.ImpRespWid, sicd.Grid.Col.ImpRespWid),
            PixelSpacing=(sicd.Grid.Row.SS, sicd.Grid.Col.SS),
            WeightingType=StringRangeCrossRangeType(
                Range=sicd.Grid.Row.WgtType.WindowName,
                CrossRange=sicd.Grid.Col.WgtType.WindowName),
            Width_3dB=(sicd.Grid.Row.ImpRespWid, sicd.Grid.Col.ImpRespWid
                       ),  # TODO: I don't think that this is correct?
            ImageHeading=sicd.SCPCOA.AzimAng,
            ImageCorners=icps)
Exemple #17
0
class RgAzCompType(Serializable):
    """
    Parameters included for a Range, Doppler image.
    """

    _fields = ('AzSF', 'KazPoly')
    _required = _fields
    _numeric_format = {'AzSF': '0.16G'}
    # descriptors
    AzSF = FloatDescriptor(
        'AzSF',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Scale factor that scales image coordinate az = ycol (meters) to a delta cosine of the '
        'Doppler Cone Angle at COA, *(in 1/m)*')  # type: float
    KazPoly = SerializableDescriptor(
        'KazPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Polynomial function that yields azimuth spatial frequency *(Kaz = Kcol)* as a function of '
        'slow time ``(variable 1)``. That is '
        r':math:`\text{Slow Time (sec)} \to \text{Azimuth spatial frequency (cycles/meter)}`. '
        'Time relative to collection start.')  # type: Poly1DType

    def __init__(self, AzSF=None, KazPoly=None, **kwargs):
        """

        Parameters
        ----------
        AzSF : float
        KazPoly : Poly1DType|numpy.ndarray|list|tuple
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.AzSF = AzSF
        self.KazPoly = KazPoly
        super(RgAzCompType, self).__init__(**kwargs)

    def _derive_parameters(self, Grid, Timeline, SCPCOA):
        """
        Expected to be called by the SICD object.

        Parameters
        ----------
        Grid : sarpy.io.complex.sicd_elements.GridType
        Timeline : sarpy.io.complex.sicd_elements.TimelineType
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType

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

        look = SCPCOA.look
        az_sf = -look * numpy.sin(numpy.deg2rad(
            SCPCOA.DopplerConeAng)) / SCPCOA.SlantRange
        if self.AzSF is None:
            self.AzSF = az_sf
        elif abs(self.AzSF - az_sf) > 1e-3:
            logger.warning('The derived value for RgAzComp.AzSF is {},\n\t'
                           'while the current setting is {}.'.format(
                               az_sf, self.AzSF))

        if self.KazPoly is None:
            if Grid.Row.KCtr is not None and Timeline is not None and Timeline.IPP is not None and \
                    Timeline.IPP.size == 1 and Timeline.IPP[0].IPPPoly is not None and SCPCOA.SCPTime is not None:

                st_rate_coa = Timeline.IPP[0].IPPPoly.derivative_eval(
                    SCPCOA.SCPTime, 1)

                krg_coa = Grid.Row.KCtr
                if Grid.Row is not None and Grid.Row.DeltaKCOAPoly is not None:
                    krg_coa += Grid.Row.DeltaKCOAPoly.Coefs[0, 0]

                # Scale factor described in SICD spec
                delta_kaz_per_delta_v = \
                    look*krg_coa*norm(SCPCOA.ARPVel.get_array()) * \
                    numpy.sin(numpy.deg2rad(SCPCOA.DopplerConeAng))/(SCPCOA.SlantRange*st_rate_coa)
                self.KazPoly = Poly1DType(Coefs=delta_kaz_per_delta_v *
                                          Timeline.IPP[0].IPPPoly.Coefs)
Exemple #18
0
class AntPatternType(Serializable):
    """
    Parameter set that defines each one-way Antenna Pattern.
    """

    _fields = ('Identifier', 'FreqZero', 'EBFreqShift', 'MLFreqDilation',
               'GainZero', 'GainBSPoly', 'ArrayGPId', 'ElementGPId')
    _required = ('Identifier', 'FreqZero', 'EBFreqShift', 'MLFreqDilation',
                 'ArrayGPId', 'ElementGPId')
    _numeric_format = {'FreqZero': '0.16G', 'GainZero': '0.16G'}
    # descriptors
    Identifier = StringDescriptor(
        'Identifier',
        _required,
        strict=DEFAULT_STRICT,
        docstring='String that uniquely identifies this Antenna Pattern'
    )  # type: str
    FreqZero = FloatDescriptor(
        'FreqZero',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'The reference frequency value for which the patterns are computed.'
    )  # type: float
    EBFreqShift = BooleanDescriptor(
        'EBFreqShift',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        "Parameter indicating whether the electronic boresight shifts with "
        "frequency.")  # type: bool
    MLFreqDilation = BooleanDescriptor(
        'MLFreqDilation',
        _required,
        strict=DEFAULT_STRICT,
        docstring="Parameter indicating the mainlobe (ML) width changes with "
        "frequency.")  # type: bool
    GainZero = FloatDescriptor(
        'GainZero',
        _required,
        strict=DEFAULT_STRICT,
        docstring='The reference antenna gain at zero steering angle at the '
        'reference frequency, measured in dB.')  # type: float
    GainBSPoly = SerializableDescriptor(
        'GainBSPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Gain polynomial *(in dB)* as a function of frequency for boresight *(BS)* '
        'at :math:`DCX=0, DCY=0`. '
        'Frequency ratio :math:`(f-f0)/f0` is the input variable, and the constant '
        'coefficient is always `0.0`.')  # type: Poly1DType
    ArrayGPId = StringDescriptor(
        'ArrayGPId',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Support array identifier of the sampled gain/phase of the array '
        'at ref frequency.')  # type: str
    ElementGPId = StringDescriptor(
        'ElementGPId',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Support array identifier of the sampled gain/phase of the element '
        'at ref frequency.')  # type: str

    def __init__(self,
                 Identifier=None,
                 FreqZero=None,
                 EBFreqShift=None,
                 MLFreqDilation=None,
                 GainZero=None,
                 GainBSPoly=None,
                 ArrayGPId=None,
                 ElementGPId=None,
                 **kwargs):
        """

        Parameters
        ----------
        Identifier : str
        FreqZero : float
        EBFreqShift : bool
        MLFreqDilation : bool
        GainZero : None|float
        GainBSPoly : None|Poly1DType
        ArrayGPId : str
        ElementGPId : str
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Identifier = Identifier
        self.FreqZero = FreqZero
        self.EBFreqShift = EBFreqShift
        self.MLFreqDilation = MLFreqDilation
        self.GainZero = GainZero
        self.GainBSPoly = GainBSPoly
        self.ArrayGPId = ArrayGPId
        self.ElementGPId = ElementGPId
        super(AntPatternType, self).__init__(**kwargs)
class ExploitationFeaturesCollectionPhenomenologyType(Serializable):
    """
    Phenomenology related to both the geometry and the final product processing.
    All values computed at the center time of the full collection.
    """

    _fields = ('Shadow', 'Layover', 'MultiPath', 'GroundTrack', 'Extensions')
    _required = ()
    _collections_tags = {
        'Extensions': {
            'array': False,
            'child_tag': 'Extension'
        }
    }
    _numeric_format = {'MultiPath': '0.16G', 'GroundTrack': '0.16G'}
    # Descriptor
    Shadow = SerializableDescriptor(
        'Shadow',
        AngleMagnitudeType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The phenomenon where vertical objects occlude radar '
        'energy.')  # type: Union[None, AngleMagnitudeType]
    Layover = SerializableDescriptor(
        'Layover',
        AngleMagnitudeType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'The phenomenon where vertical objects appear as ground objects with '
        'the same range/range rate.')  # type: Union[None, AngleMagnitudeType]
    MultiPath = FloatModularDescriptor(
        'MultiPath',
        180.0,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'This is a range dependent phenomenon which describes the energy from a '
        'single scatter returned to the radar via more than one path and results '
        'in a nominally constant direction in the ETP.'
    )  # type: Union[None, float]
    GroundTrack = FloatModularDescriptor(
        'GroundTrack',
        180.0,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Counter-clockwise angle from increasing row direction to ground track '
        'at the center of the image.')  # type: Union[None, float]
    Extensions = ParametersDescriptor(
        'Extensions',
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Exploitation feature extension related to geometry for a '
        'single input image.')  # type: ParametersCollection

    def __init__(self,
                 Shadow=None,
                 Layover=None,
                 MultiPath=None,
                 GroundTrack=None,
                 Extensions=None,
                 **kwargs):
        """

        Parameters
        ----------
        Shadow : None|AngleMagnitudeType|numpy.ndarray|list|tuple
        Layover : None|AngleMagnitudeType|numpy.ndarray|list|tuple
        MultiPath : None|float
        GroundTrack : None|float
        Extensions : None|ParametersCollection|dict
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Shadow = Shadow
        self.Layover = Layover
        self.MultiPath = MultiPath
        self.GroundTrack = GroundTrack
        self.Extensions = Extensions
        super(ExploitationFeaturesCollectionPhenomenologyType,
              self).__init__(**kwargs)

    @classmethod
    def from_calculator(cls, calculator):
        """
        Create from an ExploitationCalculator object.

        Parameters
        ----------
        calculator : ExploitationCalculator

        Returns
        -------
        ExploitationFeaturesCollectionPhenomenologyType
        """

        if not isinstance(calculator, ExploitationCalculator):
            raise TypeError(
                'Requires input which is an instance of ExploitationCalculator, got type {}'
                .format(type(calculator)))
        return cls(Shadow=calculator.Shadow,
                   Layover=calculator.Layover,
                   MultiPath=calculator.MultiPath,
                   GroundTrack=calculator.GroundTrack)
Exemple #20
0
class SICDType(Serializable):
    """
    Sensor Independent Complex Data object, containing all the relevant data to formulate products.
    """

    _fields = ('CollectionInfo', 'ImageCreation', 'ImageData', 'GeoData',
               'Grid', 'Timeline', 'Position', 'RadarCollection',
               'ImageFormation', 'SCPCOA', 'Radiometric', 'Antenna',
               'ErrorStatistics', 'MatchInfo', 'RgAzComp', 'PFA', 'RMA')
    _required = ('CollectionInfo', 'ImageData', 'GeoData', 'Grid', 'Timeline',
                 'Position', 'RadarCollection', 'ImageFormation', 'SCPCOA')
    _choice = ({'required': False, 'collection': ('RgAzComp', 'PFA', 'RMA')}, )
    # descriptors
    CollectionInfo = SerializableDescriptor(
        'CollectionInfo',
        CollectionInfoType,
        _required,
        strict=False,
        docstring='General information about the collection.'
    )  # type: CollectionInfoType
    ImageCreation = SerializableDescriptor(
        'ImageCreation',
        ImageCreationType,
        _required,
        strict=False,
        docstring='General information about the image creation.'
    )  # type: ImageCreationType
    ImageData = SerializableDescriptor(
        'ImageData',
        ImageDataType,
        _required,
        strict=False,  # it is senseless to not have this element
        docstring='The image pixel data.')  # type: ImageDataType
    GeoData = SerializableDescriptor(
        'GeoData',
        GeoDataType,
        _required,
        strict=False,
        docstring='The geographic coordinates of the image coverage area.'
    )  # type: GeoDataType
    Grid = SerializableDescriptor(
        'Grid',
        GridType,
        _required,
        strict=False,
        docstring='The image sample grid.')  # type: GridType
    Timeline = SerializableDescriptor(
        'Timeline',
        TimelineType,
        _required,
        strict=False,
        docstring='The imaging collection time line.')  # type: TimelineType
    Position = SerializableDescriptor(
        'Position',
        PositionType,
        _required,
        strict=False,
        docstring=
        'The platform and ground reference point coordinates as a function of time.'
    )  # type: PositionType
    RadarCollection = SerializableDescriptor(
        'RadarCollection',
        RadarCollectionType,
        _required,
        strict=False,
        docstring='The radar collection information.'
    )  # type: RadarCollectionType
    ImageFormation = SerializableDescriptor(
        'ImageFormation',
        ImageFormationType,
        _required,
        strict=False,
        docstring='The image formation process.')  # type: ImageFormationType
    SCPCOA = SerializableDescriptor(
        'SCPCOA',
        SCPCOAType,
        _required,
        strict=False,
        docstring=
        '*Center of Aperture (COA)* for the *Scene Center Point (SCP)*.'
    )  # type: SCPCOAType
    Radiometric = SerializableDescriptor(
        'Radiometric',
        RadiometricType,
        _required,
        strict=False,
        docstring='The radiometric calibration parameters.'
    )  # type: RadiometricType
    Antenna = SerializableDescriptor(
        'Antenna',
        AntennaType,
        _required,
        strict=False,
        docstring=
        'Parameters that describe the antenna illumination patterns during the collection.'
    )  # type: AntennaType
    ErrorStatistics = SerializableDescriptor(
        'ErrorStatistics',
        ErrorStatisticsType,
        _required,
        strict=False,
        docstring=
        'Parameters used to compute error statistics within the *SICD* sensor model.'
    )  # type: ErrorStatisticsType
    MatchInfo = SerializableDescriptor(
        'MatchInfo',
        MatchInfoType,
        _required,
        strict=False,
        docstring='Information about other collections that are matched to the '
        'current collection. The current collection is the collection '
        'from which this *SICD* product was generated.')  # type: MatchInfoType
    RgAzComp = SerializableDescriptor(
        'RgAzComp',
        RgAzCompType,
        _required,
        strict=False,
        docstring='Parameters included for a *Range, Doppler* image.'
    )  # type: RgAzCompType
    PFA = SerializableDescriptor(
        'PFA',
        PFAType,
        _required,
        strict=False,
        docstring='Parameters included when the image is formed using the '
        '*Polar Formation Algorithm (PFA)*.')  # type: PFAType
    RMA = SerializableDescriptor(
        'RMA',
        RMAType,
        _required,
        strict=False,
        docstring='Parameters included when the image is formed using the '
        '*Range Migration Algorithm (RMA)*.')  # type: RMAType

    def __init__(self,
                 CollectionInfo=None,
                 ImageCreation=None,
                 ImageData=None,
                 GeoData=None,
                 Grid=None,
                 Timeline=None,
                 Position=None,
                 RadarCollection=None,
                 ImageFormation=None,
                 SCPCOA=None,
                 Radiometric=None,
                 Antenna=None,
                 ErrorStatistics=None,
                 MatchInfo=None,
                 RgAzComp=None,
                 PFA=None,
                 RMA=None,
                 **kwargs):
        """

        Parameters
        ----------
        CollectionInfo : CollectionInfoType
        ImageCreation : ImageCreationType
        ImageData : ImageDataType
        GeoData : GeoDataType
        Grid : GridType
        Timeline : TimelineType
        Position : PositionType
        RadarCollection : RadarCollectionType
        ImageFormation : ImageFormationType
        SCPCOA : SCPCOAType
        Radiometric : RadiometricType
        Antenna : AntennaType
        ErrorStatistics : ErrorStatisticsType
        MatchInfo : MatchInfoType
        RgAzComp : RgAzCompType
        PFA : PFAType
        RMA : RMAType
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']

        nitf = kwargs.get('_NITF', {})
        if not isinstance(nitf, dict):
            raise TypeError(
                'Provided NITF options are required to be in dictionary form.')
        self._NITF = nitf

        self._coa_projection = None
        self.CollectionInfo = CollectionInfo
        self.ImageCreation = ImageCreation
        self.ImageData = ImageData
        self.GeoData = GeoData
        self.Grid = Grid
        self.Timeline = Timeline
        self.Position = Position
        self.RadarCollection = RadarCollection
        self.ImageFormation = ImageFormation
        self.SCPCOA = SCPCOA
        self.Radiometric = Radiometric
        self.Antenna = Antenna
        self.ErrorStatistics = ErrorStatistics
        self.MatchInfo = MatchInfo
        self.RgAzComp = RgAzComp
        self.PFA = PFA
        self.RMA = RMA
        super(SICDType, self).__init__(**kwargs)

    @property
    def coa_projection(self):
        """
        The COA Projection object, if previously defined through using :func:`define_coa_projection`.

        Returns
        -------
        None|sarpy.geometry.point_projection.COAProjection
        """

        return self._coa_projection

    @property
    def NITF(self):
        """
        Optional dictionary of NITF header information, pertains only to subsequent
        SICD file writing.

        Returns
        -------
        Dict
        """

        return self._NITF

    @property
    def ImageFormType(self):  # type: () -> str
        """
        str: *READ ONLY* Identifies the specific image formation type supplied. This is determined by
        returning the (first) attribute among `RgAzComp`, `PFA`, `RMA` which is populated. `OTHER` will be returned if
        none of them are populated.
        """

        for attribute in self._choice[0]['collection']:
            if getattr(self, attribute) is not None:
                return attribute
        return 'OTHER'

    def update_scp(self, point, coord_system='ECF'):
        """
        Modify the SCP point, and modify the associated SCPCOA fields.

        Parameters
        ----------
        point : numpy.ndarray|tuple|list
        coord_system : str
            Either 'ECF' or 'LLH', and 'ECF' will take precedence.

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

        if isinstance(point, (list, tuple)):
            point = numpy.array(point, dtype='float64')
        if not isinstance(point, numpy.ndarray):
            raise TypeError('point must be an numpy.ndarray')
        if point.shape != (3, ):
            raise ValueError(
                'point must be a one-dimensional, 3 element array')
        if coord_system == 'LLH':
            self.GeoData.SCP.LLH = point
        else:
            self.GeoData.SCP.ECF = point

        if self.SCPCOA is not None:
            self.SCPCOA.rederive(self.Grid, self.Position, self.GeoData)

    def _basic_validity_check(self):
        condition = super(SICDType, self)._basic_validity_check()
        condition &= detailed_validation_checks(self)
        return condition

    def is_valid(self, recursive=False, stack=False):
        all_required = self._basic_validity_check()
        if not recursive:
            return all_required

        valid_children = self._recursive_validity_check(stack=stack)
        return all_required & valid_children

    def define_geo_image_corners(self, override=False):
        """
        Defines the GeoData image corner points (if possible), if they are not already defined.

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

        if self.GeoData is None:
            self.GeoData = GeoDataType()

        if self.GeoData.ImageCorners is not None and not override:
            return  # nothing to be done

        try:
            vertex_data = self.ImageData.get_full_vertex_data(
                dtype=numpy.float64)
            corner_coords = self.project_image_to_ground_geo(vertex_data)
        except (ValueError, AttributeError):
            return

        self.GeoData.ImageCorners = corner_coords

    def define_geo_valid_data(self):
        """
        Defines the GeoData valid data corner points (if possible), if they are not already defined.

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

        if self.GeoData is None or self.GeoData.ValidData is not None:
            return  # nothing to be done

        try:
            valid_vertices = self.ImageData.get_valid_vertex_data(
                dtype=numpy.float64)
            if valid_vertices is not None:
                self.GeoData.ValidData = self.project_image_to_ground_geo(
                    valid_vertices)
        except AttributeError:
            pass

    def derive(self):
        """
        Populates any potential derived data in the SICD structure. This should get called after reading an XML,
        or as a user desires.

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

        # Note that there is dependency in calling order between steps - don't naively rearrange the following.
        if self.SCPCOA is None:
            self.SCPCOA = SCPCOAType()

        # noinspection PyProtectedMember
        self.SCPCOA._derive_scp_time(self.Grid)

        if self.Grid is not None:
            # noinspection PyProtectedMember
            self.Grid._derive_time_coa_poly(self.CollectionInfo, self.SCPCOA)

        # noinspection PyProtectedMember
        self.SCPCOA._derive_position(self.Position)

        if self.Position is None and self.SCPCOA.ARPPos is not None and \
                self.SCPCOA.ARPVel is not None and self.SCPCOA.SCPTime is not None:
            self.Position = PositionType(
            )  # important parameter derived in the next step
        if self.Position is not None:
            # noinspection PyProtectedMember
            self.Position._derive_arp_poly(self.SCPCOA)

        if self.GeoData is not None:
            self.GeoData.derive(
            )  # ensures both coordinate systems are defined for SCP

        if self.Grid is not None:
            # noinspection PyProtectedMember
            self.Grid.derive_direction_params(self.ImageData)

        if self.RadarCollection is not None:
            self.RadarCollection.derive()

        if self.ImageFormation is not None:
            # call after RadarCollection.derive(), and only if the entire transmitted bandwidth was used to process.
            # noinspection PyProtectedMember
            self.ImageFormation._derive_tx_frequency_proc(self.RadarCollection)

        # noinspection PyProtectedMember
        self.SCPCOA._derive_geometry_parameters(self.GeoData)

        # verify ImageFormation things make sense
        im_form_algo = None
        if self.ImageFormation is not None and self.ImageFormation.ImageFormAlgo is not None:
            im_form_algo = self.ImageFormation.ImageFormAlgo.upper()
        if im_form_algo == 'RGAZCOMP':
            # Check Grid settings
            if self.Grid is None:
                self.Grid = GridType()
            # noinspection PyProtectedMember
            self.Grid._derive_rg_az_comp(self.GeoData, self.SCPCOA,
                                         self.RadarCollection,
                                         self.ImageFormation)

            # Check RgAzComp settings
            if self.RgAzComp is None:
                self.RgAzComp = RgAzCompType()
            # noinspection PyProtectedMember
            self.RgAzComp._derive_parameters(self.Grid, self.Timeline,
                                             self.SCPCOA)
        elif im_form_algo == 'PFA':
            if self.PFA is None:
                self.PFA = PFAType()
            # noinspection PyProtectedMember
            self.PFA._derive_parameters(self.Grid, self.SCPCOA, self.GeoData,
                                        self.Position, self.Timeline)

            if self.Grid is not None:
                # noinspection PyProtectedMember
                self.Grid._derive_pfa(self.GeoData, self.RadarCollection,
                                      self.ImageFormation, self.Position,
                                      self.PFA)
        elif im_form_algo == 'RMA' or self.RMA is not None:
            if self.RMA is not None:
                # noinspection PyProtectedMember
                self.RMA._derive_parameters(self.SCPCOA, self.Position,
                                            self.RadarCollection,
                                            self.ImageFormation)
            if self.Grid is not None:
                # noinspection PyProtectedMember
                self.Grid._derive_rma(self.RMA, self.GeoData,
                                      self.RadarCollection,
                                      self.ImageFormation, self.Position)

        self.define_geo_image_corners()
        self.define_geo_valid_data()
        if self.Radiometric is not None:
            # noinspection PyProtectedMember
            self.Radiometric._derive_parameters(self.Grid, self.SCPCOA)

    def get_transmit_band_name(self):
        """
        Gets the processed transmit band name.

        Returns
        -------
        str
        """

        if self.ImageFormation is None:
            return 'UN'
        return self.ImageFormation.get_transmit_band_name()

    def get_processed_polarization_abbreviation(self):
        """
        Gets the processed polarization abbreviation (two letters).

        Returns
        -------
        str
        """

        if self.ImageFormation is None:
            return 'UN'
        return self.ImageFormation.get_polarization_abbreviation()

    def get_processed_polarization(self):
        """
        Gets the processed polarization.

        Returns
        -------
        str
        """

        if self.ImageFormation is None:
            return 'UN'
        return self.ImageFormation.get_polarization()

    def apply_reference_frequency(self, reference_frequency):
        """
        If the reference frequency is used, adjust the necessary fields accordingly.

        Parameters
        ----------
        reference_frequency : float
            The reference frequency.

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

        if self.RadarCollection is None:
            raise ValueError(
                'RadarCollection is not defined. The reference frequency cannot be applied.'
            )
        elif not self.RadarCollection.RefFreqIndex:  # it's None or 0
            raise ValueError(
                'RadarCollection.RefFreqIndex is not defined. The reference frequency should not be applied.'
            )

        # noinspection PyProtectedMember
        self.RadarCollection._apply_reference_frequency(reference_frequency)
        if self.ImageFormation is not None:
            # noinspection PyProtectedMember
            self.ImageFormation._apply_reference_frequency(reference_frequency)
        if self.Antenna is not None:
            # noinspection PyProtectedMember
            self.Antenna._apply_reference_frequency(reference_frequency)
        if self.RMA is not None:
            # noinspection PyProtectedMember
            self.RMA._apply_reference_frequency(reference_frequency)

    def get_ground_resolution(self):
        """
        Gets the ground resolution for the sicd.

        Returns
        -------
        (float, float)
        """

        graze = numpy.deg2rad(self.SCPCOA.GrazeAng)
        twist = numpy.deg2rad(self.SCPCOA.TwistAng)
        row_ss = self.Grid.Row.SS
        col_ss = self.Grid.Col.SS

        row_ground = abs(float(row_ss / numpy.cos(graze)))
        col_ground = float(
            numpy.sqrt((numpy.tan(graze) * numpy.tan(twist) * row_ss)**2 +
                       (col_ss / numpy.cos(twist))**2))
        return row_ground, col_ground

    def can_project_coordinates(self):
        """
        Determines whether the necessary elements are populated to permit projection
        between image and physical coordinates. If False, then the (first discovered)
        reason why not will be logged at error level.

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

        if self._coa_projection is not None:
            return True

        # GeoData elements?
        if self.GeoData is None:
            logger.error(
                'Formulating a projection is not feasible because GeoData is not populated.'
            )
            return False
        if self.GeoData.SCP is None:
            logger.error(
                'Formulating a projection is not feasible because GeoData.SCP is not populated.'
            )
            return False
        if self.GeoData.SCP.ECF is None:
            logger.error(
                'Formulating a projection is not feasible because GeoData.SCP.ECF is not populated.'
            )
            return False

        # ImageData elements?
        if self.ImageData is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData is not populated.'
            )
            return False
        if self.ImageData.FirstRow is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData.FirstRow is not populated.'
            )
            return False
        if self.ImageData.FirstCol is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData.FirstCol is not populated.'
            )
            return False
        if self.ImageData.SCPPixel is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData.SCPPixel is not populated.'
            )
            return False
        if self.ImageData.SCPPixel.Row is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData.SCPPixel.Row is not populated.'
            )
            return False
        if self.ImageData.SCPPixel.Col is None:
            logger.error(
                'Formulating a projection is not feasible because ImageData.SCPPixel.Col is not populated.'
            )
            return False

        # Position elements?
        if self.Position is None:
            logger.error(
                'Formulating a projection is not feasible because Position is not populated.'
            )
            return False
        if self.Position.ARPPoly is None:
            logger.error(
                'Formulating a projection is not feasible because Position.ARPPoly is not populated.'
            )
            return False

        # Grid elements?
        if self.Grid is None:
            logger.error(
                'Formulating a projection is not feasible because Grid is not populated.'
            )
            return False
        if self.Grid.TimeCOAPoly is None:
            logger.warning(
                'Formulating a projection may be inaccurate, because Grid.TimeCOAPoly is not populated and '
                'a constant approximation will be used.')
        if self.Grid.Row is None:
            logger.error(
                'Formulating a projection is not feasible because Grid.Row is not populated.'
            )
            return False
        if self.Grid.Row.SS is None:
            logger.error(
                'Formulating a projection is not feasible because Grid.Row.SS is not populated.'
            )
            return False
        if self.Grid.Col is None:
            logger.error(
                'Formulating a projection is not feasible because Grid.Col is not populated.'
            )
            return False
        if self.Grid.Col.SS is None:
            logger.error(
                'Formulating a projection is not feasible because Grid.Col.SS is not populated.'
            )
            return False
        if self.Grid.Type is None:
            logger.error(
                'Formulating a projection is not feasible because Grid.Type is not populated.'
            )
            return False

        # specifics for Grid.Type value
        if self.Grid.Type == 'RGAZIM':
            if self.ImageFormation is None:
                logger.error(
                    'Formulating a projection is not feasible because Grid.Type = "RGAZIM",\n\t'
                    'but ImageFormation is not populated.')
                return False
            if self.ImageFormation.ImageFormAlgo is None:
                logger.error(
                    'Formulating a projection is not feasible because Grid.Type = "RGAZIM",\n\t'
                    'but ImageFormation.ImageFormAlgo is not populated.')
                return False

            if self.ImageFormation.ImageFormAlgo == 'PFA':
                if self.PFA is None:
                    logger.error('ImageFormation.ImageFormAlgo is "PFA",\n\t'
                                 'but the PFA parameter is not populated.\n\t'
                                 'No projection can be done.')
                    return False
                if self.PFA.PolarAngPoly is None:
                    logger.error(
                        'ImageFormation.ImageFormAlgo is "PFA",\n\t'
                        'but the PFA.PolarAngPoly parameter is not populated.\n\t'
                        'No projection can be done.')
                    return False
                if self.PFA.SpatialFreqSFPoly is None:
                    logger.error(
                        'ImageFormation.ImageFormAlgo is "PFA",\n\t'
                        'but the PFA.SpatialFreqSFPoly parameter is not populated.\n\t'
                        'No projection can be done.')
                    return False
            elif self.ImageFormation.ImageFormAlgo == 'RGAZCOMP':
                if self.RgAzComp is None:
                    logger.error(
                        'ImageFormation.ImageFormAlgo is "RGAZCOMP",\n\t'
                        'but the RgAzComp parameter is not populated.\n\t'
                        'No projection can be done.')
                    return False
                if self.RgAzComp.AzSF is None:
                    logger.error(
                        'ImageFormation.ImageFormAlgo is "RGAZCOMP",\n\t'
                        'but the RgAzComp.AzSF parameter is not populated.\n\t'
                        'No projection can be done.')
                    return False
            else:
                logger.error(
                    'Grid.Type = "RGAZIM", and got unhandled ImageFormation.ImageFormAlgo {}.\n\t'
                    'No projection can be done.'.format(
                        self.ImageFormation.ImageFormAlgo))
                return False
        elif self.Grid.Type == 'RGZERO':
            if self.RMA is None or self.RMA.INCA is None:
                logger.error(
                    'Grid.Type is "RGZERO", but the RMA.INCA parameter is not populated.\n\t'
                    'No projection can be done.')
                return False
            if self.RMA.INCA.R_CA_SCP is None or self.RMA.INCA.TimeCAPoly is None \
                    or self.RMA.INCA.DRateSFPoly is None:
                logger.error(
                    'Grid.Type is "RGZERO", but the parameters\n\t'
                    'R_CA_SCP, TimeCAPoly, or DRateSFPoly of RMA.INCA parameter are not populated.\n\t'
                    'No projection can be done.')
                return False
        elif self.Grid.Type in ['XRGYCR', 'XCTYAT', 'PLANE']:
            if self.Grid.Row.UVectECF is None or self.Grid.Col.UVectECF is None:
                logger.error(
                    'Grid.Type is one of ["XRGYCR", "XCTYAT", "PLANE"], but the UVectECF parameter of '
                    'Grid.Row or Grid.Col is not populated.\n\t'
                    'No projection can be formulated.')
                return False
        else:
            logger.error('Unhandled Grid.Type {},\n\t'
                         'unclear how to formulate a projection.'.format(
                             self.Grid.Type))
            return False

        # logger.info('Consider calling sicd.define_coa_projection if the sicd structure is defined.')
        return True

    def define_coa_projection(self,
                              delta_arp=None,
                              delta_varp=None,
                              range_bias=None,
                              adj_params_frame='ECF',
                              overide=True):
        """
        Define the COAProjection object.

        Parameters
        ----------
        delta_arp : None|numpy.ndarray|list|tuple
            ARP position adjustable parameter (ECF, m).  Defaults to 0 in each coordinate.
        delta_varp : None|numpy.ndarray|list|tuple
            VARP position adjustable parameter (ECF, m/s).  Defaults to 0 in each coordinate.
        range_bias : float|int
            Range bias adjustable parameter (m), defaults to 0.
        adj_params_frame : str
            One of ['ECF', 'RIC_ECF', 'RIC_ECI'], specifying the coordinate frame used for
            expressing `delta_arp` and `delta_varp` parameters.
        overide : bool
            should we redefine, if it is previously defined?

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

        if not self.can_project_coordinates():
            logger.error('The COAProjection object cannot be defined.')
            return

        if self._coa_projection is not None and not overide:
            return

        self._coa_projection = point_projection.COAProjection.from_sicd(
            self,
            delta_arp=delta_arp,
            delta_varp=delta_varp,
            range_bias=range_bias,
            adj_params_frame=adj_params_frame)

    def project_ground_to_image(self, coords, **kwargs):
        """
        Transforms a 3D ECF point to pixel (row/column) coordinates. This is
        implemented in accordance with the SICD Image Projections Description Document.
        **Really Scene-To-Image projection.**"

        Parameters
        ----------
        coords : numpy.ndarray|tuple|list
            ECF coordinate to map to scene coordinates, of size `N x 3`.
        kwargs
            The keyword arguments for the :func:`sarpy.geometry.point_projection.ground_to_image` method.

        Returns
        -------
        Tuple[numpy.ndarray, float, int]
            * `image_points` - the determined image point array, of size `N x 2`. Following
              the SICD convention, he upper-left pixel is [0, 0].
            * `delta_gpn` - residual ground plane displacement (m).
            * `iterations` - the number of iterations performed.

        See Also
        --------
        sarpy.geometry.point_projection.ground_to_image
        """

        if 'use_structure_coa' not in kwargs:
            kwargs['use_structure_coa'] = True
        return point_projection.ground_to_image(coords, self, **kwargs)

    def project_ground_to_image_geo(self,
                                    coords,
                                    ordering='latlong',
                                    **kwargs):
        """
        Transforms a 3D Lat/Lon/HAE point to pixel (row/column) coordinates. This is
        implemented in accordance with the SICD Image Projections Description Document.
        **Really Scene-To-Image projection.**"

        Parameters
        ----------
        coords : numpy.ndarray|tuple|list
            ECF coordinate to map to scene coordinates, of size `N x 3`.
        ordering : str
            If 'longlat', then the input is `[longitude, latitude, hae]`.
            Otherwise, the input is `[latitude, longitude, hae]`. Passed through
            to :func:`sarpy.geometry.geocoords.geodetic_to_ecf`.
        kwargs
            The keyword arguments for the :func:`sarpy.geometry.point_projection.ground_to_image_geo` method.

        Returns
        -------
        Tuple[numpy.ndarray, float, int]
            * `image_points` - the determined image point array, of size `N x 2`. Following
              the SICD convention, he upper-left pixel is [0, 0].
            * `delta_gpn` - residual ground plane displacement (m).
            * `iterations` - the number of iterations performed.

        See Also
        --------
        sarpy.geometry.point_projection.ground_to_image_geo
        """

        if 'use_structure_coa' not in kwargs:
            kwargs['use_structure_coa'] = True
        return point_projection.ground_to_image_geo(coords,
                                                    self,
                                                    ordering=ordering,
                                                    **kwargs)

    def project_image_to_ground(self,
                                im_points,
                                projection_type='HAE',
                                **kwargs):
        """
        Transforms image coordinates to ground plane ECF coordinate via the algorithm(s)
        described in SICD Image Projections document.

        Parameters
        ----------
        im_points : numpy.ndarray|list|tuple
            the image coordinate array
        projection_type : str
            One of `['PLANE', 'HAE', 'DEM']`. Type `DEM` is a work in progress.
        kwargs
            The keyword arguments for the :func:`sarpy.geometry.point_projection.image_to_ground` method.

        Returns
        -------
        numpy.ndarray
            Ground Plane Point (in ECF coordinates) corresponding to the input image coordinates.

        See Also
        --------
        sarpy.geometry.point_projection.image_to_ground
        """

        if 'use_structure_coa' not in kwargs:
            kwargs['use_structure_coa'] = True
        return point_projection.image_to_ground(
            im_points, self, projection_type=projection_type, **kwargs)

    def project_image_to_ground_geo(self,
                                    im_points,
                                    ordering='latlong',
                                    projection_type='HAE',
                                    **kwargs):
        """
        Transforms image coordinates to ground plane WGS-84 coordinate via the algorithm(s)
        described in SICD Image Projections document.

        Parameters
        ----------
        im_points : numpy.ndarray|list|tuple
            the image coordinate array
        projection_type : str
            One of `['PLANE', 'HAE', 'DEM']`. Type `DEM` is a work in progress.
        ordering : str
            Determines whether return is ordered as `[lat, long, hae]` or `[long, lat, hae]`.
            Passed through to :func:`sarpy.geometry.geocoords.ecf_to_geodetic`.
        kwargs
            The keyword arguments for the :func:`sarpy.geometry.point_projection.image_to_ground_geo` method.

        Returns
        -------
        numpy.ndarray
            Ground Plane Point (in ECF coordinates) corresponding to the input image coordinates.
        See Also
        --------
        sarpy.geometry.point_projection.image_to_ground_geo
        """

        if 'use_structure_coa' not in kwargs:
            kwargs['use_structure_coa'] = True
        return point_projection.image_to_ground_geo(
            im_points,
            self,
            ordering=ordering,
            projection_type=projection_type,
            **kwargs)

    def populate_rniirs(self, signal=None, noise=None, override=False):
        """
        Given the signal and noise values (in sigma zero power units),
        calculate and populate an estimated RNIIRS value.

        Parameters
        ----------
        signal : None|float
        noise : None|float
        override : bool
            Override the value, if present.

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

        from sarpy.processing.rgiqe import populate_rniirs_for_sicd
        populate_rniirs_for_sicd(self,
                                 signal=signal,
                                 noise=noise,
                                 override=override)

    def get_suggested_name(self, product_number=1):
        """
        Get the suggested name stem for the sicd and derived data.

        Returns
        -------
        str
        """

        sugg_name = get_sicd_name(self, product_number)
        if sugg_name is not None:
            return sugg_name
        elif self.CollectionInfo.CoreName is not None:
            return self.CollectionInfo.CoreName
        return 'Unknown_Sicd{}'.format(product_number)

    def get_des_details(self, check_version1_compliance=False):
        """
        Gets the correct current SICD DES subheader details.

        Parameters
        ----------
        check_version1_compliance : bool
            If true and structure is compatible, the version 1.1 information will
            be returned. Otherwise, the most recent supported version will be
            returned .

        Returns
        -------
        dict
        """

        if check_version1_compliance and (
            (self.ImageFormation is None
             or self.ImageFormation.permits_version_1_1()) and
            (self.RadarCollection is None
             or self.RadarCollection.permits_version_1_1())):
            spec_version = _SICD_SPECIFICATION_VERSION_1_1
            spec_date = _SICD_SPECIFICATION_DATE_1_1
            spec_ns = _SICD_SPECIFICATION_NAMESPACE_1_1
        else:
            spec_version = _SICD_SPECIFICATION_VERSION_1_2
            spec_date = _SICD_SPECIFICATION_DATE_1_2
            spec_ns = _SICD_SPECIFICATION_NAMESPACE_1_2

        return OrderedDict([('DESSHSI', _SICD_SPECIFICATION_IDENTIFIER),
                            ('DESSHSV', spec_version), ('DESSHSD', spec_date),
                            ('DESSHTN', spec_ns)])

    def copy(self):
        """
        Provides a deep copy.

        Returns
        -------
        SICDType
        """

        out = super(SICDType, self).copy()
        out._NITF = deepcopy(self._NITF)
        return out

    def to_xml_bytes(self,
                     urn=None,
                     tag='SICD',
                     check_validity=False,
                     strict=DEFAULT_STRICT):
        if urn is None:
            urn = _SICD_SPECIFICATION_NAMESPACE_1_2
        return super(SICDType,
                     self).to_xml_bytes(urn=urn,
                                        tag=tag,
                                        check_validity=check_validity,
                                        strict=strict)

    def to_xml_string(self,
                      urn=None,
                      tag='SICD',
                      check_validity=False,
                      strict=DEFAULT_STRICT):
        return self.to_xml_bytes(urn=urn,
                                 tag=tag,
                                 check_validity=check_validity,
                                 strict=strict).decode('utf-8')

    def create_subset_structure(self, row_bounds=None, column_bounds=None):
        """
        Create a version of the SICD structure for a given subset.

        Parameters
        ----------
        row_bounds : None|tuple
        column_bounds : None|tuple

        Returns
        -------
        sicd : SICDType
            The sicd
        row_bounds : tuple
            Vetted tuple of the form `(min row, max row)`.
        column_bounds : tuple
            Vetted tuple of the form `(min column, max column)`.
        """

        sicd = self.copy()
        num_rows = self.ImageData.NumRows
        num_cols = self.ImageData.NumCols
        if row_bounds is not None:
            start_row = int(row_bounds[0])
            end_row = int(row_bounds[1])
            if not (0 <= start_row < end_row <= num_rows):
                raise ValueError(
                    'row bounds ({}, {}) are not sensible for NumRows {}'.
                    format(start_row, end_row, num_rows))
            sicd.ImageData.FirstRow = sicd.ImageData.FirstRow + start_row
            sicd.ImageData.NumRows = (end_row - start_row)
            out_row_bounds = (start_row, end_row)
        else:
            out_row_bounds = (0, num_rows)

        if column_bounds is not None:
            start_col = int(column_bounds[0])
            end_col = int(column_bounds[1])
            if not (0 <= start_col < end_col <= num_cols):
                raise ValueError(
                    'column bounds ({}, {}) are not sensible for NumCols {}'.
                    format(start_col, end_col, num_cols))
            sicd.ImageData.FirstCol = sicd.ImageData.FirstCol + start_col
            sicd.ImageData.NumCols = (end_col - start_col)
            out_col_bounds = (start_col, end_col)
        else:
            out_col_bounds = (0, num_cols)
        sicd.define_geo_image_corners(override=True)
        return sicd, out_row_bounds, out_col_bounds
class ExploitationFeaturesProductType(Serializable):
    """
    Metadata regarding the product.
    """

    _fields = ('Resolution', 'Ellipticity', 'Polarizations', 'North',
               'Extensions')
    _required = ('Resolution', 'Ellipticity', 'Polarizations')
    _collections_tags = {
        'Polarizations': {
            'array': False,
            'child_tag': 'Polarization'
        },
        'Extensions': {
            'array': False,
            'child_tag': 'Extension'
        }
    }
    _numeric_format = {'Ellipticity': '0.16G', 'North': '0.16G'}
    # Descriptor
    Resolution = SerializableDescriptor(
        'Resolution',
        RowColDoubleType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Uniformly-weighted resolution projected into the Earth Tangent '
        'Plane (ETP).')  # type: RowColDoubleType
    Ellipticity = FloatDescriptor(
        'Ellipticity',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        "Ellipticity of the 2D-IPR at the ORP, measured in the *Earth Geodetic "
        "Tangent Plane (EGTP)*. Ellipticity is the ratio of the IPR ellipse's "
        "major axis to minor axis.")  # type: float
    Polarizations = SerializableListDescriptor(
        'Polarizations',
        ProcTxRcvPolarizationType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Describes the processed transmit and receive polarizations for the '
        'product.')  # type: List[ProcTxRcvPolarizationType]
    North = FloatModularDescriptor(
        'North',
        180.0,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Counter-clockwise angle from increasing row direction to north at the center '
        'of the image.')  # type: float
    Extensions = ParametersDescriptor(
        'Extensions',
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Exploitation feature extension related to geometry for a '
        'single input image.')  # type: ParametersCollection

    def __init__(self,
                 Resolution=None,
                 Ellipticity=None,
                 Polarizations=None,
                 North=None,
                 Extensions=None,
                 **kwargs):
        """

        Parameters
        ----------
        Resolution : RowColDoubleType|numpy.ndarray|list|tuple
        Ellipticity : float
        Polarizations : List[ProcTxRcvPolarizationType]
        North : None|float
        Extensions : None|ParametersCollection|dict
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Resolution = Resolution
        self.Ellipticity = Ellipticity
        self.Polarizations = Polarizations
        self.North = North
        self.Extensions = Extensions
        super(ExploitationFeaturesProductType, self).__init__(**kwargs)

    @classmethod
    def from_calculator(cls, calculator, sicd):
        """
        Construct from a sicd element.

        Parameters
        ----------
        calculator : ExploitationCalculator
        sicd : SICDType

        Returns
        -------
        ExploitationFeaturesProductType
        """

        if not isinstance(sicd, SICDType):
            raise TypeError('Requires SICDType instance, got type {}'.format(
                type(sicd)))

        row_ground, col_ground = sicd.get_ground_resolution()
        ellipticity = row_ground / col_ground if row_ground >= col_ground else col_ground / row_ground

        return cls(Resolution=(row_ground, col_ground),
                   Ellipticity=ellipticity,
                   Polarizations=[
                       ProcTxRcvPolarizationType.from_sicd_value(
                           sicd.ImageFormation.TxRcvPolarizationProc),
                   ],
                   North=calculator.North)
Exemple #22
0
class CRSDType(Serializable):
    """
    The Compensated Received Signal Data definition.
    """

    _fields = ('CollectionID', 'Global', 'SceneCoordinates', 'Data', 'Channel',
               'PVP', 'SupportArray', 'Dwell', 'ReferenceGeometry', 'Antenna',
               'ErrorParameters', 'ProductInfo', 'GeoInfo', 'MatchInfo')
    _required = ('CollectionID', 'Global', 'Data', 'Channel', 'PVP',
                 'ReferenceGeometry')
    _collections_tags = {'GeoInfo': {'array': 'False', 'child_tag': 'GeoInfo'}}
    # descriptors
    CollectionID = SerializableDescriptor(
        'CollectionID',
        CollectionIDType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='General information about the collection.'
    )  # type: CollectionIDType
    Global = SerializableDescriptor(
        'Global',
        GlobalType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Global parameters that apply to metadata components and CRSD '
        'signal arrays.')  # type: GlobalType
    SceneCoordinates = SerializableDescriptor(
        'SceneCoordinates',
        SceneCoordinatesType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters that define geographic coordinates of the imaged '
        'scene.')  # type: Union[None, SceneCoordinatesType]
    Data = SerializableDescriptor(
        'Data',
        DataType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters that describe binary data components contained in '
        'the product.')  # type: DataType
    Channel = SerializableDescriptor(
        'Channel',
        ChannelType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters that describe the data channels contained in the '
        'product.')  # type: ChannelType
    PVP = SerializableDescriptor(
        'PVP',
        PVPType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Structure used to specify the Per Vector Parameters provided for '
        'each channel of a given product.')  # type: PVPType
    SupportArray = SerializableDescriptor(
        'SupportArray',
        SupportArrayType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the binary support array(s) content and '
        'grid coordinates.')  # type: Union[None, SupportArrayType]
    Dwell = SerializableDescriptor(
        'Dwell',
        DwellType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that specify the SAR dwell time supported by the signal '
        'arrays contained in the CRSD product.'
    )  # type: Union[None, DwellType]
    ReferenceGeometry = SerializableDescriptor(
        'ReferenceGeometry',
        ReferenceGeometryType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the collection geometry for the reference '
        'vector of the reference channel.')  # type: ReferenceGeometryType
    Antenna = SerializableDescriptor(
        'Antenna',
        AntennaType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='Parameters that describe the antennas antennas used '
        'to collect the signal array(s).')  # type: Union[None, AntennaType]
    ErrorParameters = SerializableDescriptor(
        'ErrorParameters',
        ErrorParametersType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that describe the statistics of errors in measured or estimated parameters '
        'that describe the collection.'
    )  # type: Union[None, ErrorParametersType]
    ProductInfo = SerializableDescriptor(
        'ProductInfo',
        ProductInfoType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters that provide general information about the CRSD product '
        'and/or the derived products that may be created '
        'from it.')  # type: Union[None, ProductInfoType]
    MatchInfo = SerializableDescriptor(
        'MatchInfo',
        MatchInfoType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Information about other collections that are matched to the collection from which '
        'this CRSD product was generated.')  # type: Union[None, MatchInfoType]

    def __init__(self,
                 CollectionID=None,
                 Global=None,
                 SceneCoordinates=None,
                 Data=None,
                 Channel=None,
                 PVP=None,
                 SupportArray=None,
                 Dwell=None,
                 ReferenceGeometry=None,
                 Antenna=None,
                 ErrorParameters=None,
                 ProductInfo=None,
                 GeoInfo=None,
                 MatchInfo=None,
                 **kwargs):
        """

        Parameters
        ----------
        CollectionID : CollectionIDType
        Global : GlobalType
        SceneCoordinates : None|SceneCoordinatesType
        Data : DataType
        Channel : ChannelType
        PVP : PVPType
        SupportArray : None|SupportArrayType
        Dwell : None|DwellType
        ReferenceGeometry : ReferenceGeometryType
        Antenna : None|AntennaType
        ErrorParameters : None|ErrorParametersType
        ProductInfo : None|ProductInfoType
        GeoInfo : None|List[GeoInfoType]|GeoInfoType
        MatchInfo : None|MatchInfoType
        kwargs
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.CollectionID = CollectionID
        self.Global = Global
        self.SceneCoordinates = SceneCoordinates
        self.Data = Data
        self.Channel = Channel
        self.PVP = PVP
        self.SupportArray = SupportArray
        self.Dwell = Dwell
        self.ReferenceGeometry = ReferenceGeometry
        self.Antenna = Antenna
        self.ErrorParameters = ErrorParameters
        self.ProductInfo = ProductInfo
        self.MatchInfo = MatchInfo

        self._GeoInfo = []
        if GeoInfo is None:
            pass
        elif isinstance(GeoInfo, GeoInfoType):
            self.addGeoInfo(GeoInfo)
        elif isinstance(GeoInfo, (list, tuple)):
            for el in GeoInfo:
                self.addGeoInfo(el)
        else:
            raise ValueError('GeoInfo got unexpected type {}'.format(
                type(GeoInfo)))

        super(CRSDType, self).__init__(**kwargs)

    @property
    def GeoInfo(self):
        """
        List[GeoInfoType]: Parameters that describe a geographic feature.
        """

        return self._GeoInfo

    def getGeoInfo(self, key):
        """
        Get GeoInfo(s) with name attribute == `key`.

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfo if entry.name == key]

    def addGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfo list.

        Parameters
        ----------
        value : GeoInfoType

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

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfo', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfo.append(value)
        else:
            raise TypeError(
                'Trying to set GeoInfo element with unexpected type {}'.format(
                    type(value)))

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfo', ns_key)
        kwargs['GeoInfo'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(CRSDType, cls).from_node(node,
                                              xml_ns,
                                              ns_key=ns_key,
                                              kwargs=kwargs)

    def to_node(self,
                doc,
                tag,
                ns_key=None,
                parent=None,
                check_validity=False,
                strict=DEFAULT_STRICT,
                exclude=()):
        node = super(CRSDType, self).to_node(doc,
                                             tag,
                                             ns_key=ns_key,
                                             parent=parent,
                                             check_validity=check_validity,
                                             strict=strict,
                                             exclude=exclude + ('GeoInfo', ))
        # slap on the GeoInfo children
        if self._GeoInfo is not None and len(self._GeoInfo) > 0:
            for entry in self._GeoInfo:
                entry.to_node(doc,
                              'GeoInfo',
                              ns_key=ns_key,
                              parent=node,
                              strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(CRSDType, self).to_dict(check_validity=check_validity,
                                            strict=strict,
                                            exclude=exclude + ('GeoInfo', ))
        # slap on the GeoInfo children
        if len(self.GeoInfo) > 0:
            out['GeoInfo'] = [
                entry.to_dict(check_validity=check_validity, strict=strict)
                for entry in self._GeoInfo
            ]
        return out

    def to_xml_bytes(self,
                     urn=None,
                     tag='CRSD',
                     check_validity=False,
                     strict=DEFAULT_STRICT):
        if urn is None:
            urn = _CRSD_SPECIFICATION_NAMESPACE
        return super(CRSDType,
                     self).to_xml_bytes(urn=urn,
                                        tag=tag,
                                        check_validity=check_validity,
                                        strict=strict)

    def to_xml_string(self,
                      urn=None,
                      tag='CRSD',
                      check_validity=False,
                      strict=DEFAULT_STRICT):
        return self.to_xml_bytes(urn=urn,
                                 tag=tag,
                                 check_validity=check_validity,
                                 strict=strict).decode('utf-8')

    def make_file_header(self, xml_offset=1024):
        """
        Forms a CRSD file header consistent with the information in the Data and CollectionID nodes.

        Parameters
        ----------
        xml_offset : int, optional
            Offset in bytes to the first byte of the XML block. If the provided value
            is not large enough to account for the length of the file header
            string, a larger value is chosen.

        Returns
        -------
        header : sarpy.io.phase_history.cphd1_elements.CRSD.CRSDHeader
        """

        _kvps = OrderedDict()

        def _align(val):
            align_to = 64
            return int(numpy.ceil(float(val) / align_to) * align_to)

        _kvps['XML_BLOCK_SIZE'] = len(self.to_xml_string())
        _kvps['XML_BLOCK_BYTE_OFFSET'] = xml_offset
        block_end = _kvps['XML_BLOCK_BYTE_OFFSET'] + _kvps[
            'XML_BLOCK_SIZE'] + len(_CRSD_SECTION_TERMINATOR)

        if self.Data.NumSupportArrays > 0:
            _kvps[
                'SUPPORT_BLOCK_SIZE'] = self.Data.calculate_support_block_size(
                )
            _kvps['SUPPORT_BLOCK_BYTE_OFFSET'] = _align(block_end)
            block_end = _kvps['SUPPORT_BLOCK_BYTE_OFFSET'] + _kvps[
                'SUPPORT_BLOCK_SIZE']

        _kvps['PVP_BLOCK_SIZE'] = self.Data.calculate_pvp_block_size()
        _kvps['PVP_BLOCK_BYTE_OFFSET'] = _align(block_end)
        block_end = _kvps['PVP_BLOCK_BYTE_OFFSET'] + _kvps['PVP_BLOCK_SIZE']

        _kvps['SIGNAL_BLOCK_SIZE'] = self.Data.calculate_signal_block_size()
        _kvps['SIGNAL_BLOCK_BYTE_OFFSET'] = _align(block_end)
        _kvps['CLASSIFICATION'] = self.CollectionID.Classification
        _kvps['RELEASE_INFO'] = self.CollectionID.ReleaseInfo

        header = CRSDHeader(**_kvps)
        header_str = header.to_string()
        min_xml_offset = len(header_str) + len(_CRSD_SECTION_TERMINATOR)
        if _kvps['XML_BLOCK_BYTE_OFFSET'] < min_xml_offset:
            header = self.make_file_header(xml_offset=_align(min_xml_offset +
                                                             32))

        return header

    def get_pvp_dtype(self):
        """
        Gets the dtype for the corresponding PVP structured array. Note that they
        must all have homogeneous dtype.

        Returns
        -------
        numpy.dtype
            This will be a compound dtype for a structured array.
        """

        if self.PVP is None:
            raise ValueError('No PVP defined.')
        return self.PVP.get_vector_dtype()

    @classmethod
    def from_xml_file(cls, file_path):
        """
        Construct the crsd object from a stand-alone xml file path.

        Parameters
        ----------
        file_path : str

        Returns
        -------
        CRSDType
        """

        root_node, xml_ns = parse_xml_from_file(file_path)
        ns_key = 'default' if 'default' in xml_ns else None
        return cls.from_node(root_node, xml_ns=xml_ns, ns_key=ns_key)

    @classmethod
    def from_xml_string(cls, xml_string):
        """
        Construct the crsd object from an xml string.

        Parameters
        ----------
        xml_string : str|bytes

        Returns
        -------
        CRSDType
        """

        root_node, xml_ns = parse_xml_from_string(xml_string)
        ns_key = 'default' if 'default' in xml_ns else None
        return cls.from_node(root_node, xml_ns=xml_ns, ns_key=ns_key)
Exemple #23
0
class AntennaType(Serializable):
    """Parameters that describe the antenna illumination patterns during the collection."""
    _fields = ('Tx', 'Rcv', 'TwoWay')
    _required = ()
    # descriptors
    Tx = SerializableDescriptor(
        'Tx',
        AntParamType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The transmit antenna parameters.')  # type: AntParamType
    Rcv = SerializableDescriptor(
        'Rcv',
        AntParamType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The receive antenna parameters.')  # type: AntParamType
    TwoWay = SerializableDescriptor(
        'TwoWay',
        AntParamType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The bidirectional transmit/receive antenna parameters.'
    )  # type: AntParamType

    def __init__(self, Tx=None, Rcv=None, TwoWay=None, **kwargs):
        """

        Parameters
        ----------
        Tx : AntParamType
        Rcv : AntParamType
        TwoWay : AntParamType
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.Tx, self.Rcv, self.TwoWay = Tx, Rcv, TwoWay
        super(AntennaType, self).__init__(**kwargs)

    def _apply_reference_frequency(self, reference_frequency):
        """
        If the reference frequency is used, adjust the necessary fields accordingly.
        Expected to be called by SICD parent.

        Parameters
        ----------
        reference_frequency : float
            The reference frequency.

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

        if self.Tx is not None:
            # noinspection PyProtectedMember
            self.Tx._apply_reference_frequency(reference_frequency)
        if self.Rcv is not None:
            # noinspection PyProtectedMember
            self.Rcv._apply_reference_frequency(reference_frequency)
        if self.TwoWay is not None:
            # noinspection PyProtectedMember
            self.TwoWay._apply_reference_frequency(reference_frequency)
Exemple #24
0
class RMAType(Serializable):
    """Parameters included when the image is formed using the Range Migration Algorithm."""
    _fields = ('RMAlgoType', 'ImageType', 'RMAT', 'RMCR', 'INCA')
    _required = ('RMAlgoType', 'ImageType')
    _choice = ({'required': True, 'collection': ('RMAT', 'RMCR', 'INCA')}, )
    # class variables
    _RM_ALGO_TYPE_VALUES = ('OMEGA_K', 'CSA', 'RG_DOP')
    # descriptors
    RMAlgoType = StringEnumDescriptor(
        'RMAlgoType', _RM_ALGO_TYPE_VALUES, _required, strict=DEFAULT_STRICT,
        docstring=r"""
        Identifies the type of migration algorithm used:

        * `OMEGA_K` - Algorithms that employ Stolt interpolation of the Kxt dimension. :math:`Kx = \sqrt{Kf^2 - Ky^2}`

        * `CSA` - Wave number algorithm that process two-dimensional chirp signals.

        * `RG_DOP` - Range-Doppler algorithms that employ *RCMC* in the compressed range domain.

        """)  # type: str
    RMAT = SerializableDescriptor(
        'RMAT', RMRefType, _required, strict=DEFAULT_STRICT,
        docstring='Parameters for *RMA with Along Track (RMAT)* motion compensation.')  # type: RMRefType
    RMCR = SerializableDescriptor(
        'RMCR', RMRefType, _required, strict=DEFAULT_STRICT,
        docstring='Parameters for *RMA with Cross Range (RMCR)* motion compensation.')  # type: RMRefType
    INCA = SerializableDescriptor(
        'INCA', INCAType, _required, strict=DEFAULT_STRICT,
        docstring='Parameters for *Imaging Near Closest Approach (INCA)* image description.')  # type: INCAType

    def __init__(self, RMAlgoType=None, RMAT=None, RMCR=None, INCA=None, **kwargs):
        """

        Parameters
        ----------
        RMAlgoType : str
        RMAT : RMRefType
        RMCR : RMRefType
        INCA : INCAType
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.RMAlgoType = RMAlgoType
        self.RMAT = RMAT
        self.RMCR = RMCR
        self.INCA = INCA
        super(RMAType, self).__init__(**kwargs)

    @property
    def ImageType(self):  # type: () -> Union[None, str]
        """
        str: READ ONLY attribute. Identifies the specific RM image type / metadata type supplied. This is determined by
        returning the (first) attribute among `'RMAT', 'RMCR', 'INCA'` which is populated. `None` will be returned if
        none of them are populated.
        """

        for attribute in self._choice[0]['collection']:
            if getattr(self, attribute) is not None:
                return attribute
        return None

    def _derive_parameters(self, SCPCOA, Position, RadarCollection, ImageFormation):
        """
        Expected to be called from SICD parent.

        Parameters
        ----------
        SCPCOA : sarpy.io.complex.sicd_elements.SCPCOA.SCPCOAType
        Position : sarpy.io.complex.sicd_elements.Position.PositionType
        RadarCollection : sarpy.io.complex.sicd_elements.RadarCollection.RadarCollectionType
        ImageFormation : sarpy.io.complex.sicd_elements.ImageFormation.ImageFormationType

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

        if SCPCOA is None:
            return

        scp = None if SCPCOA.ARPPos is None else SCPCOA.ARPPos.get_array()

        im_type = self.ImageType
        if im_type in ['RMAT', 'RMCR']:
            rm_ref = getattr(self, im_type)  # type: RMRefType
            if rm_ref.PosRef is None and SCPCOA.ARPPos is not None:
                rm_ref.PosRef = SCPCOA.ARPPos.copy()
            if rm_ref.VelRef is None and SCPCOA.ARPVel is not None:
                rm_ref.VelRef = SCPCOA.ARPVel.copy()
            if scp is not None and rm_ref.PosRef is not None and rm_ref.VelRef is not None:
                pos_ref = rm_ref.PosRef.get_array()
                vel_ref = rm_ref.VelRef.get_array()
                uvel_ref = vel_ref/norm(vel_ref)
                ulos = (scp - pos_ref)  # it absolutely could be that scp = pos_ref
                ulos_norm = norm(ulos)
                if ulos_norm > 0:
                    ulos /= ulos_norm
                    if rm_ref.DopConeAngRef is None:
                        rm_ref.DopConeAngRef = numpy.rad2deg(numpy.arccos(numpy.dot(uvel_ref, ulos)))
        elif im_type == 'INCA':
            if scp is not None and self.INCA.TimeCAPoly is not None and \
                    Position is not None and Position.ARPPoly is not None:
                t_zero = self.INCA.TimeCAPoly.Coefs[0]
                ca_pos = Position.ARPPoly(t_zero)
                if self.INCA.R_CA_SCP is None:
                    self.INCA.R_CA_SCP = norm(ca_pos - scp)
            if self.INCA.FreqZero is None:
                self.INCA.FreqZero = _get_center_frequency(RadarCollection, ImageFormation)

    def _apply_reference_frequency(self, reference_frequency):
        """
        If the reference frequency is used, adjust the necessary fields accordingly.
        Expected to be called by SICD parent.

        Parameters
        ----------
        reference_frequency : float
            The reference frequency.

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

        if self.INCA is not None:
            # noinspection PyProtectedMember
            self.INCA._apply_reference_frequency(reference_frequency)
Exemple #25
0
class ImageDataType(Serializable):
    """The image pixel data."""
    _collections_tags = {
        'AmpTable': {'array': True, 'child_tag': 'Amplitude'},
        'ValidData': {'array': True, 'child_tag': 'Vertex'},
    }
    _fields = (
        'PixelType', 'AmpTable', 'NumRows', 'NumCols', 'FirstRow', 'FirstCol', 'FullImage', 'SCPPixel', 'ValidData')
    _required = ('PixelType', 'NumRows', 'NumCols', 'FirstRow', 'FirstCol', 'FullImage', 'SCPPixel')
    _numeric_format = {'AmpTable': '0.16G'}
    _PIXEL_TYPE_VALUES = ("RE32F_IM32F", "RE16I_IM16I", "AMP8I_PHS8I")
    # descriptors
    PixelType = StringEnumDescriptor(
        'PixelType', _PIXEL_TYPE_VALUES, _required, strict=True,
        docstring="The PixelType attribute which specifies the interpretation of the file data.")  # type: str
    AmpTable = FloatArrayDescriptor(
        'AmpTable', _collections_tags, _required, strict=DEFAULT_STRICT,
        minimum_length=256, maximum_length=256,
        docstring="The amplitude look-up table. This is required if "
                  "`PixelType == 'AMP8I_PHS8I'`")  # type: numpy.ndarray
    NumRows = IntegerDescriptor(
        'NumRows', _required, strict=True,
        docstring='The number of Rows in the product. May include zero rows.')  # type: int
    NumCols = IntegerDescriptor(
        'NumCols', _required, strict=True,
        docstring='The number of Columns in the product. May include zero rows.')  # type: int
    FirstRow = IntegerDescriptor(
        'FirstRow', _required, strict=DEFAULT_STRICT,
        docstring='Global row index of the first row in the product. '
                  'Equal to 0 in full image product.')  # type: int
    FirstCol = IntegerDescriptor(
        'FirstCol', _required, strict=DEFAULT_STRICT,
        docstring='Global column index of the first column in the product. '
                  'Equal to 0 in full image product.')  # type: int
    FullImage = SerializableDescriptor(
        'FullImage', FullImageType, _required, strict=DEFAULT_STRICT,
        docstring='Original full image product.')  # type: FullImageType
    SCPPixel = SerializableDescriptor(
        'SCPPixel', RowColType, _required, strict=DEFAULT_STRICT,
        docstring='Scene Center Point pixel global row and column index. Should be located near the '
                  'center of the full image.')  # type: RowColType
    ValidData = SerializableArrayDescriptor(
        'ValidData', RowColArrayElement, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=3,
        docstring='Indicates the full image includes both valid data and some zero filled pixels. '
                  'Simple polygon encloses the valid data (may include some zero filled pixels for simplification). '
                  'Vertices in clockwise order.')  # type: Union[SerializableArray, List[RowColArrayElement]]

    def __init__(self, PixelType=None, AmpTable=None, NumRows=None, NumCols=None,
                 FirstRow=None, FirstCol=None, FullImage=None, SCPPixel=None, ValidData=None, **kwargs):
        """

        Parameters
        ----------
        PixelType : str
        AmpTable : numpy.ndarray|list|tuple
        NumRows : int
        NumCols : int
        FirstRow : int
        FirstCol : int
        FullImage : FullImageType|numpy.ndarray|list|tuple
        SCPPixel : RowColType|numpy.ndarray|list|tuple
        ValidData : SerializableArray|List[RowColArrayElement]|numpy.ndarray|list|tuple
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.PixelType = PixelType
        self.AmpTable = AmpTable
        self.NumRows, self.NumCols = NumRows, NumCols
        self.FirstRow, self.FirstCol = FirstRow, FirstCol
        self.FullImage = FullImage
        self.SCPPixel = SCPPixel
        self.ValidData = ValidData
        super(ImageDataType, self).__init__(**kwargs)

    def _check_valid_data(self):
        if self.ValidData is None:
            return True
        if len(self.ValidData) < 2:
            return True

        value = True
        valid_data = self.ValidData.get_array(dtype='float64')
        lin_ring = LinearRing(coordinates=valid_data)
        area = lin_ring.get_area()
        if area == 0:
            self.log_validity_error('ValidData encloses no area.')
            value = False
        elif area > 0:
            self.log_validity_error(
                "ValidData must be traversed in clockwise direction.")
            value = False
        for i, entry in enumerate(valid_data):
            if not ((self.FirstRow <= entry[0] <= self.FirstRow + self.NumRows) and
                    (self.FirstCol <= entry[1] <= self.FirstCol + self.NumCols)):
                self.log_validity_warning(
                    'ValidData entry {} is not contained in the image bounds'.format(i))
                value = False
        return value

    def _basic_validity_check(self):
        condition = super(ImageDataType, self)._basic_validity_check()
        if (self.PixelType == 'AMP8I_PHS8I') and (self.AmpTable is None):
            self.log_validity_error("We have `PixelType='AMP8I_PHS8I'` and `AmpTable` is not defined for ImageDataType.")
            condition = False
        if (self.PixelType != 'AMP8I_PHS8I') and (self.AmpTable is not None):
            self.log_validity_error("We have `PixelType != 'AMP8I_PHS8I'` and `AmpTable` is defined for ImageDataType.")
            condition = False
        if (self.ValidData is not None) and (len(self.ValidData) < 3):
            self.log_validity_error("We have `ValidData` defined with fewer than 3 entries.")
            condition = False
        condition &= self._check_valid_data()
        return condition

    def get_valid_vertex_data(self, dtype=numpy.int64):
        """
        Gets an array of `[row, col]` indices defining the valid data. If this is not viable, then `None`
        will be returned.

        Parameters
        ----------
        dtype : object
            the data type for the array

        Returns
        -------
        numpy.ndarray|None
        """

        if self.ValidData is None:
            return None
        out = numpy.zeros((self.ValidData.size, 2), dtype=dtype)
        for i, entry in enumerate(self.ValidData):
            out[i, :] = entry.get_array(dtype=dtype)
        return out

    def get_full_vertex_data(self, dtype=numpy.int64):
        """
        Gets an array of `[row, col]` indices defining the full vertex data. If this is not viable, then `None`
        will be returned.

        Parameters
        ----------
        dtype : object
            the data type for the array

        Returns
        -------
        numpy.ndarray|None
        """

        if self.NumRows is None or self.NumCols is None:
            return None
        return numpy.array(
            [[0, 0], [0, self.NumCols - 1], [self.NumRows - 1, self.NumCols - 1], [self.NumRows - 1, 0]], dtype=dtype)
Exemple #26
0
class INCAType(Serializable):
    """Parameters for Imaging Near Closest Approach (INCA) image description."""
    _fields = (
        'TimeCAPoly', 'R_CA_SCP', 'FreqZero', 'DRateSFPoly', 'DopCentroidPoly', 'DopCentroidCOA')
    _required = ('TimeCAPoly', 'R_CA_SCP', 'FreqZero', 'DRateSFPoly')
    _numeric_format = {'R_CA_SCP': '0.17E', 'FreqZero': '0.17E'}
    # descriptors
    TimeCAPoly = SerializableDescriptor(
        'TimeCAPoly', Poly1DType, _required, strict=DEFAULT_STRICT,
        docstring='Polynomial function that yields *Time of Closest Approach* as function of '
                  'image column *(azimuth)* coordinate in meters. Time relative to '
                  'collection start in seconds.')  # type: Poly1DType
    R_CA_SCP = FloatDescriptor(
        'R_CA_SCP', _required, strict=DEFAULT_STRICT,
        docstring='*Range at Closest Approach (R_CA)* for the *Scene Center Point (SCP)* in meters.')  # type: float
    FreqZero = FloatDescriptor(
        'FreqZero', _required, strict=DEFAULT_STRICT,
        docstring=r'*RF frequency* :\math:`(f_0)` in Hz used for computing '
                  r'Doppler Centroid values. Typical :math:`f_0` '
                  r'set equal to center transmit frequency.')  # type: float
    DRateSFPoly = SerializableDescriptor(
        'DRateSFPoly', Poly2DType, _required, strict=DEFAULT_STRICT,
        docstring='Polynomial function that yields *Doppler Rate scale factor (DRSF)* '
                  'as a function of image location. Yields `DRSF` as a function of image '
                  'range coordinate ``(variable 1)`` and azimuth coordinate ``(variable 2)``. '
                  'Used to compute Doppler Rate at closest approach.')  # type: Poly2DType
    DopCentroidPoly = SerializableDescriptor(
        'DopCentroidPoly', Poly2DType, _required, strict=DEFAULT_STRICT,
        docstring='Polynomial function that yields Doppler Centroid value as a '
                  'function of image location *(fdop_DC)*. The *fdop_DC* is the '
                  'Doppler frequency at the peak signal response. The polynomial is a function '
                  'of image range coordinate ``(variable 1)`` and azimuth coordinate ``(variable 2)``. '
                  '*Note: Only used for Stripmap and Dynamic Stripmap collections.*')  # type: Poly2DType
    DopCentroidCOA = BooleanDescriptor(
        'DopCentroidCOA', _required, strict=DEFAULT_STRICT,
        docstring="""Flag indicating that the COA is at the peak signal :math`fdop_COA = fdop_DC`.
        
        * `True` - if Pixel COA at peak signal for all pixels.
        
        * `False` otherwise.
        
        *Note:* Only used for Stripmap and Dynamic Stripmap.""")  # type: bool

    def __init__(self, TimeCAPoly=None, R_CA_SCP=None, FreqZero=None, DRateSFPoly=None,
                 DopCentroidPoly=None, DopCentroidCOA=None, **kwargs):
        """

        Parameters
        ----------
        TimeCAPoly : Poly1DType|numpy.ndarray|list|tuple
        R_CA_SCP : float
        FreqZero : float
        DRateSFPoly : Poly2DType|numpy.ndarray|list|tuple
        DopCentroidPoly : Poly2DType|numpy.ndarray|list|tuple
        DopCentroidCOA : bool
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.TimeCAPoly = TimeCAPoly
        self.R_CA_SCP = R_CA_SCP
        self.FreqZero = FreqZero
        self.DRateSFPoly = DRateSFPoly
        self.DopCentroidPoly = DopCentroidPoly
        self.DopCentroidCOA = DopCentroidCOA
        super(INCAType, self).__init__(**kwargs)

    def _apply_reference_frequency(self, reference_frequency):
        if self.FreqZero is not None:
            self.FreqZero += reference_frequency
Exemple #27
0
class ImageFormationType(Serializable):
    """The image formation process parameters."""
    _fields = ('RcvChanProc', 'TxRcvPolarizationProc', 'TStartProc',
               'TEndProc', 'TxFrequencyProc', 'SegmentIdentifier',
               'ImageFormAlgo', 'STBeamComp', 'ImageBeamComp', 'AzAutofocus',
               'RgAutofocus', 'Processings', 'PolarizationCalibration')
    _required = ('RcvChanProc', 'TxRcvPolarizationProc', 'TStartProc',
                 'TEndProc', 'TxFrequencyProc', 'ImageFormAlgo', 'STBeamComp',
                 'ImageBeamComp', 'AzAutofocus', 'RgAutofocus')
    _collections_tags = {
        'Processings': {
            'array': False,
            'child_tag': 'Processing'
        }
    }
    _numeric_format = {'TStartProc': '0.16G', 'EndProc': '0.16G'}
    # class variables
    _IMG_FORM_ALGO_VALUES = ('PFA', 'RMA', 'RGAZCOMP', 'OTHER')
    _ST_BEAM_COMP_VALUES = ('NO', 'GLOBAL', 'SV')
    _IMG_BEAM_COMP_VALUES = ('NO', 'SV')
    _AZ_AUTOFOCUS_VALUES = _ST_BEAM_COMP_VALUES
    _RG_AUTOFOCUS_VALUES = _ST_BEAM_COMP_VALUES
    # descriptors
    RcvChanProc = SerializableDescriptor(
        'RcvChanProc',
        RcvChanProcType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The received processed channels.')  # type: RcvChanProcType
    TxRcvPolarizationProc = StringEnumDescriptor(
        'TxRcvPolarizationProc',
        DUAL_POLARIZATION_VALUES,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'The combined transmit/receive polarization processed to form the image.'
    )  # type: str
    TStartProc = FloatDescriptor(
        'TStartProc',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Earliest slow time value for data processed to form the image '
        'from `CollectionStart`.')  # type: float
    TEndProc = FloatDescriptor(
        'TEndProc',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Latest slow time value for data processed to form the image from `CollectionStart`.'
    )  # type: float
    TxFrequencyProc = SerializableDescriptor(
        'TxFrequencyProc',
        TxFrequencyProcType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The range of transmit frequency processed to form the image.'
    )  # type: TxFrequencyProcType
    SegmentIdentifier = StringDescriptor(
        'SegmentIdentifier',
        _required,
        strict=DEFAULT_STRICT,
        docstring='Identifier that describes the image that was processed. '
        'Must be included when `SICD.RadarCollection.Area.Plane.SegmentList` is included.'
    )  # type: str
    ImageFormAlgo = StringEnumDescriptor('ImageFormAlgo',
                                         _IMG_FORM_ALGO_VALUES,
                                         _required,
                                         strict=DEFAULT_STRICT,
                                         docstring="""
        The image formation algorithm used:

        * `PFA` - Polar Format Algorithm

        * `RMA` - Range Migration (Omega-K, Chirp Scaling, Range-Doppler)

        * `RGAZCOMP` - Simple range, Doppler compression.

        """)  # type: str
    STBeamComp = StringEnumDescriptor('STBeamComp',
                                      _ST_BEAM_COMP_VALUES,
                                      _required,
                                      strict=DEFAULT_STRICT,
                                      docstring="""
        Indicates if slow time beam shape compensation has been applied.

        * `NO` - No ST beam shape compensation.

        * `GLOBAL` - Global ST beam shape compensation applied.

        * `SV` - Spatially variant beam shape compensation applied.

        """)  # type: str
    ImageBeamComp = StringEnumDescriptor('ImageBeamComp',
                                         _IMG_BEAM_COMP_VALUES,
                                         _required,
                                         strict=DEFAULT_STRICT,
                                         docstring="""
        Indicates if image domain beam shape compensation has been applied.

        * `NO` - No image domain beam shape compensation.

        * `SV` - Spatially variant image domain beam shape compensation applied.

        """)  # type: str
    AzAutofocus = StringEnumDescriptor(
        'AzAutofocus',
        _AZ_AUTOFOCUS_VALUES,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Indicates if azimuth autofocus correction has been applied, with similar '
        'interpretation as `STBeamComp`.')  # type: str
    RgAutofocus = StringEnumDescriptor(
        'RgAutofocus',
        _RG_AUTOFOCUS_VALUES,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Indicates if range autofocus correction has been applied, with similar '
        'interpretation as `STBeamComp`.')  # type: str
    Processings = SerializableListDescriptor(
        'Processings',
        ProcessingType,
        _collections_tags,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'Parameters to describe types of specific processing that may have been applied '
        'such as additional compensations.'
    )  # type: Union[None, List[ProcessingType]]
    PolarizationCalibration = SerializableDescriptor(
        'PolarizationCalibration',
        PolarizationCalibrationType,
        _required,
        strict=DEFAULT_STRICT,
        docstring='The polarization calibration details.'
    )  # type: PolarizationCalibrationType

    def __init__(self,
                 RcvChanProc=None,
                 TxRcvPolarizationProc=None,
                 TStartProc=None,
                 TEndProc=None,
                 TxFrequencyProc=None,
                 SegmentIdentifier=None,
                 ImageFormAlgo=None,
                 STBeamComp=None,
                 ImageBeamComp=None,
                 AzAutofocus=None,
                 RgAutofocus=None,
                 Processings=None,
                 PolarizationCalibration=None,
                 **kwargs):
        """

        Parameters
        ----------
        RcvChanProc : RcvChanProcType
        TxRcvPolarizationProc : str
        TStartProc : float
        TEndProc : float
        TxFrequencyProc : TxFrequencyProcType|numpy.ndarray|list|tuple
        SegmentIdentifier : str
        ImageFormAlgo : str
        STBeamComp : str
        ImageBeamComp :str
        AzAutofocus : str
        RgAutofocus : str
        Processings : None|List[ProcessingType]
        PolarizationCalibration : PolarizationCalibrationType
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.RcvChanProc = RcvChanProc
        self.TxRcvPolarizationProc = TxRcvPolarizationProc
        self.TStartProc, self.TEndProc = TStartProc, TEndProc
        if isinstance(
                TxFrequencyProc,
            (numpy.ndarray, list, tuple)) and len(TxFrequencyProc) >= 2:
            self.TxFrequencyProc = TxFrequencyProcType(
                MinProc=TxFrequencyProc[0], MaxProc=TxFrequencyProc[1])
        else:
            self.TxFrequencyProc = TxFrequencyProc
        self.SegmentIdentifier = SegmentIdentifier
        self.ImageFormAlgo = ImageFormAlgo
        self.STBeamComp, self.ImageBeamComp = STBeamComp, ImageBeamComp
        self.AzAutofocus, self.RgAutofocus = AzAutofocus, RgAutofocus
        self.Processings = Processings
        self.PolarizationCalibration = PolarizationCalibration
        super(ImageFormationType, self).__init__(**kwargs)

    def _basic_validity_check(self):
        condition = super(ImageFormationType, self)._basic_validity_check()
        if self.TStartProc is not None and self.TEndProc is not None and self.TEndProc < self.TStartProc:
            self.log_validity_error(
                'Invalid time processing bounds TStartProc ({}) > TEndProc ({})'
                .format(self.TStartProc, self.TEndProc))
            condition = False
        return condition

    def _derive_tx_frequency_proc(self, RadarCollection):
        """
        Populate a default for processed frequency values, based on the assumption that the entire
        transmitted bandwidth was processed. This is expected to be called by SICD parent.

        Parameters
        ----------
        RadarCollection : sarpy.io.complex.sicd_elements.RadarCollection.RadarCollectionType

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

        if RadarCollection is not None and RadarCollection.TxFrequency is not None and \
                RadarCollection.TxFrequency.Min is not None and RadarCollection.TxFrequency.Max is not None:
            # this is based on the assumption that the entire transmitted bandwidth was processed.
            if self.TxFrequencyProc is not None:
                self.TxFrequencyProc = TxFrequencyProcType(
                    MinProc=RadarCollection.TxFrequency.Min,
                    MaxProc=RadarCollection.TxFrequency.Max)
                # how would it make sense to set only one end?
            elif self.TxFrequencyProc.MinProc is None:
                self.TxFrequencyProc.MinProc = RadarCollection.TxFrequency.Min
            elif self.TxFrequencyProc.MaxProc is None:
                self.TxFrequencyProc.MaxProc = RadarCollection.TxFrequency.Max

    def _apply_reference_frequency(self, reference_frequency):
        """
        If the reference frequency is used, adjust the necessary fields accordingly.
        Expected to be called by SICD parent.

        Parameters
        ----------
        reference_frequency : float
            The reference frequency.

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

        if self.TxFrequencyProc is not None:
            # noinspection PyProtectedMember
            self.TxFrequencyProc._apply_reference_frequency(
                reference_frequency)

    def get_polarization(self):
        """
        Gets the transmit/receive polarization.

        Returns
        -------
        str
        """

        return self.TxRcvPolarizationProc if self.TxRcvPolarizationProc is not None else 'UNKNOWN'

    def get_polarization_abbreviation(self):
        """
        Gets the transmit/receive polarization abbreviation for the suggested name.

        Returns
        -------
        str
        """

        pol = self.TxRcvPolarizationProc
        if pol is None or pol in ('OTHER', 'UNKNOWN'):
            return 'UN'
        fp, sp = pol.split(':')
        return fp[0] + sp[0]

    def get_transmit_band_name(self):
        """
        Gets the transmit band name.

        Returns
        -------
        str
        """

        if self.TxFrequencyProc is not None:
            return self.TxFrequencyProc.get_band_name()
        else:
            return 'UN'

    def permits_version_1_1(self):
        """
        Does this value permit storage in SICD version 1.1?

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

        return is_polstring_version1(self.TxRcvPolarizationProc)
Exemple #28
0
class GeoInfoType(Serializable):
    """
    A geographic feature.
    """

    _fields = ('name', 'Descriptions', 'Point', 'Line', 'Polygon')
    _required = ('name', )
    _set_as_attribute = ('name', )
    _choice = ({'required': False, 'collection': ('Point', 'Line', 'Polygon')}, )
    _collections_tags = {
        'Descriptions': {'array': False, 'child_tag': 'Desc'},
        'Line': {'array': True, 'child_tag': 'Endpoint'},
        'Polygon': {'array': True, 'child_tag': 'Vertex'}, }
    # descriptors
    name = StringDescriptor(
        'name', _required, strict=DEFAULT_STRICT,
        docstring='The name.')  # type: str
    Descriptions = ParametersDescriptor(
        'Descriptions', _collections_tags, _required, strict=DEFAULT_STRICT,
        docstring='Descriptions of the geographic feature.')  # type: ParametersCollection
    Point = SerializableDescriptor(
        'Point', LatLonRestrictionType, _required, strict=DEFAULT_STRICT,
        docstring='A geographic point with WGS-84 coordinates.')  # type: LatLonRestrictionType
    Line = SerializableArrayDescriptor(
        'Line', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=2,
        docstring='A geographic line (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]
    Polygon = SerializableArrayDescriptor(
        'Polygon', LatLonArrayElementType, _collections_tags, _required, strict=DEFAULT_STRICT, minimum_length=3,
        docstring='A geographic polygon (array) with WGS-84 coordinates.'
    )  # type: Union[SerializableArray, List[LatLonArrayElementType]]

    def __init__(self, name=None, Descriptions=None, Point=None, Line=None, Polygon=None, GeoInfos=None, **kwargs):
        """

        Parameters
        ----------
        name : str
        Descriptions : ParametersCollection|dict
        Point : LatLonRestrictionType|numpy.ndarray|list|tuple
        Line : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        Polygon : SerializableArray|List[LatLonArrayElementType]|numpy.ndarray|list|tuple
        GeoInfos : Dict[GeoInfoTpe]
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.name = name
        self.Descriptions = Descriptions
        self.Point = Point
        self.Line = Line
        self.Polygon = Polygon

        self._GeoInfos = []
        if GeoInfos is None:
            pass
        elif isinstance(GeoInfos, GeoInfoType):
            self.addGeoInfo(GeoInfos)
        elif isinstance(GeoInfos, (list, tuple)):
            for el in GeoInfos:
                self.addGeoInfo(el)
        else:
            raise ValueError('GeoInfos got unexpected type {}'.format(type(GeoInfos)))
        super(GeoInfoType, self).__init__(**kwargs)

    @property
    def FeatureType(self):  # type: () -> Union[None, str]
        """
        str: READ ONLY attribute. Identifies the feature type among. This is determined by
        returning the (first) attribute among `Point`, `Line`, `Polygon` which is populated.
        `None` will be returned if none of them are populated.
        """

        for attribute in self._choice[0]['collection']:
            if getattr(self, attribute) is not None:
                return attribute
        return None

    @property
    def GeoInfos(self):
        """
        List[GeoInfoType]: list of GeoInfos.
        """

        return self._GeoInfos

    def getGeoInfo(self, key):
        """
        Get GeoInfo(s) with name attribute == `key`.

        Parameters
        ----------
        key : str

        Returns
        -------
        List[GeoInfoType]
        """

        return [entry for entry in self._GeoInfos if entry.name == key]

    def addGeoInfo(self, value):
        """
        Add the given GeoInfo to the GeoInfos list.

        Parameters
        ----------
        value : GeoInfoType

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

        if isinstance(value, ElementTree.Element):
            gi_key = self._child_xml_ns_key.get('GeoInfos', self._xml_ns_key)
            value = GeoInfoType.from_node(value, self._xml_ns, ns_key=gi_key)
        elif isinstance(value, dict):
            value = GeoInfoType.from_dict(value)

        if isinstance(value, GeoInfoType):
            self._GeoInfos.append(value)
        else:
            raise TypeError('Trying to set GeoInfo element with unexpected type {}'.format(type(value)))

    def _validate_features(self):
        if self.Line is not None and self.Line.size < 2:
            self.log_validity_error('GeoInfo has a Line feature with {} points defined.'.format(self.Line.size))
            return False
        if self.Polygon is not None and self.Polygon.size < 3:
            self.log_validity_error('GeoInfo has a Polygon feature with {} points defined.'.format(self.Polygon.size))
            return False
        return True

    def _basic_validity_check(self):
        condition = super(GeoInfoType, self)._basic_validity_check()
        return condition & self._validate_features()

    @classmethod
    def from_node(cls, node, xml_ns, ns_key=None, kwargs=None):
        if kwargs is None:
            kwargs = OrderedDict()
        gi_key = cls._child_xml_ns_key.get('GeoInfos', ns_key)
        kwargs['GeoInfos'] = find_children(node, 'GeoInfo', xml_ns, gi_key)
        return super(GeoInfoType, cls).from_node(node, xml_ns, ns_key=ns_key, kwargs=kwargs)

    def to_node(self, doc, tag, ns_key=None, parent=None, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        node = super(GeoInfoType, self).to_node(
            doc, tag, ns_key=ns_key, parent=parent, check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        for entry in self._GeoInfos:
            entry.to_node(doc, tag, ns_key=ns_key, parent=node, strict=strict)
        return node

    def to_dict(self, check_validity=False, strict=DEFAULT_STRICT, exclude=()):
        out = super(GeoInfoType, self).to_dict(check_validity=check_validity, strict=strict, exclude=exclude)
        # slap on the GeoInfo children
        if len(self.GeoInfos) > 0:
            out['GeoInfos'] = [entry.to_dict(check_validity=check_validity, strict=strict) for entry in self._GeoInfos]
        return out
Exemple #29
0
class ProjectionPerturbationType(Serializable):
    """
    Basic information required for SICD/SIDD projection model perturbation.
    """

    _fields = ('CoordinateFrame', 'DeltaArp', 'DeltaVarp', 'DeltaRange')
    _required = ('CoordinateFrame', )
    _numeric_format = {
        'Lat': '0.17G',
    }
    CoordinateFrame = StringEnumDescriptor('CoordinateFrame',
                                           {'ECF', 'RIC_ECI', 'RIC_ECF'},
                                           _required)  # type: str
    DeltaArp = SerializableDescriptor('DeltaArp', XYZType,
                                      _required)  # type: XYZType
    DeltaVarp = SerializableDescriptor('DeltaVarp', XYZType,
                                       _required)  # type: XYZType
    DeltaRange = FloatDescriptor('DeltaRange', _required)  # type: float

    def __init__(self,
                 CoordinateFrame=None,
                 DeltaArp=None,
                 DeltaVarp=None,
                 DeltaRange=None,
                 **kwargs):
        """
        Parameters
        ----------
        CoordinateFrame : str
        DeltaArp : None|XYZType|numpy.ndarray|list|tuple
        DeltaVarp : None|XYZType|numpy.ndarray|list|tuple
        DeltaRange : None|float
        kwargs
            Other keyword arguments
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']

        self.CoordinateFrame = CoordinateFrame
        self.DeltaArp = DeltaArp
        self.DeltaVarp = DeltaVarp
        self.DeltaRange = DeltaRange

        super(ProjectionPerturbationType, self).__init__(**kwargs)

    def set_coa_projection(self, structure):
        """
        Sets the sicd or sidd coa_projection property, as appropriate.

        Parameters
        ----------
        structure : SICDType|SIDDType1|SIDDType2
        """

        if not isinstance(structure, (SICDType, SIDDType1, SIDDType2)):
            raise TypeError(
                'Requires input of type SICDType or SIDDType, got {}'.format(
                    type(structure)))

        structure.define_coa_projection(
            delta_arp=None if self.DeltaArp is None else
            self.DeltaArp.get_array(dtype='float64'),
            delta_varp=None if self.DeltaVarp is None else
            self.DeltaVarp.get_array(dtype='float64'),
            range_bias=self.DeltaRange,
            adj_params_frame=self.CoordinateFrame,
            overide=True)
Exemple #30
0
class IPPSetType(Serializable):
    """
    The Inter-Pulse Parameter array element container.
    """

    # NOTE that this is simply defined as a child class ("Set") of the TimelineType in the SICD standard
    #   Defining it at root level clarifies the documentation, and giving it a more descriptive name is
    #   appropriate.
    _fields = ('TStart', 'TEnd', 'IPPStart', 'IPPEnd', 'IPPPoly', 'index')
    _required = _fields
    _set_as_attribute = ('index', )
    _numeric_format = {
        'TStart': '0.16G',
        'TEnd': '0.16G',
    }
    # descriptors
    TStart = FloatDescriptor(
        'TStart',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'IPP start time relative to collection start time, i.e. offsets in seconds.'
    )  # type: float
    TEnd = FloatDescriptor(
        'TEnd',
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'IPP end time relative to collection start time, i.e. offsets in seconds.'
    )  # type: float
    IPPStart = IntegerDescriptor(
        'IPPStart',
        _required,
        strict=True,
        docstring='Starting IPP index for the period described.')  # type: int
    IPPEnd = IntegerDescriptor(
        'IPPEnd',
        _required,
        strict=True,
        docstring='Ending IPP index for the period described.')  # type: int
    IPPPoly = SerializableDescriptor(
        'IPPPoly',
        Poly1DType,
        _required,
        strict=DEFAULT_STRICT,
        docstring=
        'IPP index polynomial coefficients yield IPP index as a function of time.'
    )  # type: Poly1DType
    index = IntegerDescriptor(
        'index',
        _required,
        strict=DEFAULT_STRICT,
        docstring='The element array index.')  # type: int

    def __init__(self,
                 TStart=None,
                 TEnd=None,
                 IPPStart=None,
                 IPPEnd=None,
                 IPPPoly=None,
                 index=None,
                 **kwargs):
        """

        Parameters
        ----------
        TStart : float
        TEnd : float
        IPPStart : int
        IPPEnd : int
        IPPPoly : Poly1DType|numpy.ndarray|list|tuple
        index : int
        kwargs : dict
        """

        if '_xml_ns' in kwargs:
            self._xml_ns = kwargs['_xml_ns']
        if '_xml_ns_key' in kwargs:
            self._xml_ns_key = kwargs['_xml_ns_key']
        self.TStart, self.TEnd = TStart, TEnd
        self.IPPStart, self.IPPEnd = IPPStart, IPPEnd
        self.IPPPoly = IPPPoly
        self.index = index
        super(IPPSetType, self).__init__(**kwargs)

    def _basic_validity_check(self):
        condition = super(IPPSetType, self)._basic_validity_check()
        if self.TStart >= self.TEnd:
            self.log_validity_error('TStart ({}) >= TEnd ({})'.format(
                self.TStart, self.TEnd))
            condition = False

        if self.IPPStart >= self.IPPEnd:
            self.log_validity_error('IPPStart ({}) >= IPPEnd ({})'.format(
                self.IPPStart, self.IPPEnd))
            condition = False

        exp_ipp_start = self.IPPPoly(self.TStart)
        exp_ipp_end = self.IPPPoly(self.TEnd)
        if abs(exp_ipp_start - self.IPPStart) > 1:
            self.log_validity_error(
                'IPPStart populated as {}, inconsistent with value ({}) '
                'derived from IPPPoly and TStart'.format(
                    exp_ipp_start, self.IPPStart))
        if abs(exp_ipp_end - self.IPPEnd) > 1:
            self.log_validity_error(
                'IPPEnd populated as {}, inconsistent with value ({}) '
                'derived from IPPPoly and TEnd'.format(self.IPPEnd,
                                                       exp_ipp_end))
        return condition