Esempio n. 1
0
    def _find_sicd(self):
        self._is_sicd = False
        self._sicd_meta = None
        if self.des_subheader_offsets is None:
            return

        for i in range(self.des_subheader_offsets.size):
            subhead_bytes = self.get_des_subheader_bytes(i)
            if subhead_bytes.startswith(b'DEXML_DATA_CONTENT'):
                des_header = DataExtensionHeader.from_bytes(subhead_bytes, start=0)
                des_bytes = self.get_des_bytes(i)
                # noinspection PyBroadException
                try:
                    root_node, xml_ns = parse_xml_from_string(des_bytes.decode('utf-8').strip())
                    if 'SIDD' in root_node.tag:  # namespace makes this ugly
                        # NOTE that SIDD files are supposed to have the corresponding
                        # SICD xml as one of the DES AFTER the SIDD xml.
                        # The same basic format is used for both headers.
                        # So, abandon if we find a SIDD xml
                        self._des_index = None
                        self._des_header = None
                        self._is_sicd = False
                        break
                    elif 'SICD' in root_node.tag:  # namespace makes this ugly
                        self._des_index = i
                        self._des_header = des_header
                        self._is_sicd = True
                        self._sicd_meta = SICDType.from_node(root_node, xml_ns, ns_key='default')
                        break
                except Exception:
                    continue
            elif subhead_bytes.startswith(b'DESIDD_XML'):
                # This is an old format SIDD and can't be a SICD
                self._des_index = None
                self._des_header = None
                self._is_sicd = False
                break
            elif subhead_bytes.startswith(b'DESICD_XML'):
                # This is an old format SICD
                des_bytes = self.get_des_bytes(i)
                try:
                    root_node, xml_ns = parse_xml_from_string(des_bytes)
                    if 'SICD' in root_node.tag:  # namespace makes this ugly
                        self._des_index = i
                        self._des_header = None
                        self._is_sicd = True
                        self._sicd_meta = SICDType.from_node(root_node, xml_ns, ns_key='default')
                        break
                except Exception as e:
                    logging.error('We found an apparent old-style SICD DES header, '
                                  'but failed parsing with error {}'.format(e))
                    continue

        if not self._is_sicd:
            return
        self._sicd_meta.derive()
Esempio n. 2
0
    def _find_sidd(self):
        self._is_sidd = False
        if self.des_subheader_offsets is None:
            return

        self._sidd_meta = []
        self._sicd_meta = []

        for i in range(self.des_subheader_offsets.size):
            subhead_bytes = self.get_des_subheader_bytes(i)
            if subhead_bytes.startswith(b'DEXML_DATA_CONTENT'):
                des_bytes = self.get_des_bytes(i)
                try:
                    root_node, xml_ns = parse_xml_from_string(des_bytes)
                    if 'SIDD' in root_node.tag:
                        self._is_sidd = True
                        self._sidd_meta.append(SIDDType.from_node(root_node, xml_ns, ns_key='default'))
                    elif 'SICD' in root_node.tag:
                        self._sicd_meta.append(SICDType.from_node(root_node, xml_ns, ns_key='default'))
                except Exception as e:
                    logger.error('Failed checking des xml header at index {} with error {}'.format(i, e))
                    continue
            elif subhead_bytes.startswith(b'DESIDD_XML'):
                # This is an old format SIDD header
                des_bytes = self.get_des_bytes(i)
                try:
                    root_node, xml_ns = parse_xml_from_string(des_bytes)
                    if 'SIDD' in root_node.tag:
                        self._is_sidd = True
                        self._sidd_meta.append(SIDDType.from_node(root_node, xml_ns, ns_key='default'))
                except Exception as e:
                    logger.error(
                        'We found an apparent old-style SIDD DES header at index {},\n\t'
                        'but failed parsing with error {}'.format(i, e))
                    continue
            elif subhead_bytes.startswith(b'DESICD_XML'):
                # This is an old format SICD header
                des_bytes = self.get_des_bytes(i)
                try:
                    root_node, xml_ns = parse_xml_from_string(des_bytes)
                    if 'SICD' in root_node.tag:
                        self._sicd_meta.append(SICDType.from_node(root_node, xml_ns, ns_key='default'))
                except Exception as e:
                    logger.error(
                        'We found an apparent old-style SICD DES header at index {},\n\t'
                        'but failed parsing with error {}'.format(i, e))
                    continue

        if not self._is_sidd:
            return

        for sicd in self._sicd_meta:
            sicd.derive()
Esempio n. 3
0
        def update_geodata(sicd: SICDType) -> None:
            scp_pixel = [
                sicd.ImageData.SCPPixel.Row, sicd.ImageData.SCPPixel.Col
            ]
            ecf = sicd.project_image_to_ground(scp_pixel,
                                               projection_type='HAE')
            sicd.update_scp(ecf, coord_system='ECF')

            SCP = sicd.GeoData.SCP.ECF.get_array(dtype='float64')
            scp_time = sicd.RMA.INCA.TimeCAPoly[0]
            ca_pos = sicd.Position.ARPPoly(scp_time)
            RG = SCP - ca_pos
            sicd.RMA.INCA.R_CA_SCP = numpy.linalg.norm(RG)
Esempio n. 4
0
        def update_image_data(
                sicd: SICDType,
                band_name: str) -> (float, float, float, float, int):
            cols, rows = shape_dict[band_name]
            # zero doppler time of first/last columns
            t_az_first_time = band_dict[band_name][
                'Zero Doppler Azimuth First Time']
            t_az_last_time = band_dict[band_name][
                'Zero Doppler Azimuth Last Time']
            t_ss_az_s = band_dict[band_name]['Line Time Interval']
            t_use_sign2 = 1
            if h5_dict['Look Side'].upper() == 'LEFT':
                t_use_sign2 = -1
                t_az_first_time, t_az_last_time = t_az_last_time, t_az_first_time
            # zero doppler time of first row
            t_rg_first_time = band_dict[band_name][
                'Zero Doppler Range First Time']
            # row spacing in range time (seconds)
            t_ss_rg_s = band_dict[band_name]['Column Time Interval']

            sicd.ImageData = ImageDataType(NumRows=rows,
                                           NumCols=cols,
                                           FirstRow=0,
                                           FirstCol=0,
                                           FullImage=(rows, cols),
                                           PixelType=pixeltype_dict[band_name],
                                           SCPPixel=RowColType(
                                               Row=int(rows / 2),
                                               Col=int(cols / 2)))
            return t_rg_first_time, t_ss_rg_s, t_az_first_time, t_ss_az_s, t_use_sign2
Esempio n. 5
0
 def update_scp_prelim(sicd: SICDType, band_name: str) -> None:
     if self._mission_id in ['CSK', 'KMPS']:
         LLH = band_dict[band_name]['Centre Geodetic Coordinates']
     elif self._mission_id == 'CSG':
         LLH = h5_dict['Scene Centre Geodetic Coordinates']
     else:
         raise ValueError(_unhandled_id_text.format(self._mission_id))
     sicd.GeoData = GeoDataType(
         SCP=SCPType(LLH=LLH))  # EarthModel & ECF will be populated
Esempio n. 6
0
def validate_sicd_for_writing(sicd_meta: SICDType) -> SICDType:
    """
    Helper method which ensures the provided SICD structure provides enough
    information to support file writing, as well as ensures a few basic items
    are populated as appropriate.

    Parameters
    ----------
    sicd_meta : SICDType

    Returns
    -------
    SICDType
        This returns a deep copy of the provided SICD structure, with any
        necessary modifications.
    """

    if not isinstance(sicd_meta, SICDType):
        raise ValueError('sicd_meta is required to be an instance of SICDType, got {}'.format(type(sicd_meta)))
    if sicd_meta.ImageData is None:
        raise ValueError('The sicd_meta has un-populated ImageData, and nothing useful can be inferred.')
    if sicd_meta.ImageData.NumCols is None or sicd_meta.ImageData.NumRows is None:
        raise ValueError('The sicd_meta has ImageData with unpopulated NumRows or NumCols, '
                         'and nothing useful can be inferred.')
    if sicd_meta.ImageData.PixelType is None:
        logger.warning('The PixelType for sicd_meta is unset, so defaulting to RE32F_IM32F.')
        sicd_meta.ImageData.PixelType = 'RE32F_IM32F'

    sicd_meta = sicd_meta.copy()

    profile = '{} {}'.format(__title__, __version__)
    if sicd_meta.ImageCreation is None:
        sicd_meta.ImageCreation = ImageCreationType(
            Application=profile,
            DateTime=numpy.datetime64(datetime.now()),
            Profile=profile)
    else:
        sicd_meta.ImageCreation.Profile = profile
        if sicd_meta.ImageCreation.DateTime is None:
            sicd_meta.ImageCreation.DateTime = numpy.datetime64(datetime.now())
    return sicd_meta
Esempio n. 7
0
    def get_sicd(self):
        """
        Extract the SICD details.

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

        if self._sicd is not None:
            return self._sicd
        if self._user_data is None:
            self._read_user_data()
        if self._caspr_data is None:
            self._find_caspr_data()
        # Check if the user data contains a sicd structure.
        sicd_string = None
        for nam in ['SICDMETA', 'SICD_META', 'SICD']:
            if sicd_string is None:
                sicd_string = self._user_data.get(nam, None)
        # If so, assume that this SICD is valid and simply present it
        if sicd_string is not None:
            root_node, xml_ns = parse_xml_from_string(sicd_string)
            self._sicd = SICDType.from_node(root_node, xml_ns)
            self._sicd.derive()
        else:
            # otherwise, we populate a really minimal sicd structure
            num_rows, num_cols = self.data_size
            self._sicd = SICDType(ImageData=ImageDataType(NumRows=num_rows,
                                                          NumCols=num_cols,
                                                          FirstRow=0,
                                                          FirstCol=0,
                                                          PixelType=self.pixel_type,
                                                          FullImage=FullImageType(NumRows=num_rows,
                                                                                  NumCols=num_cols),
                                                          SCPPixel=RowColType(Row=num_rows/2,
                                                                              Col=num_cols/2)))
        return self._sicd
Esempio n. 8
0
def check_file(file_name):
    """
    Check the SICD validity for the given file SICD (i.e. appropriately styled NITF)
    or xml file containing the SICD structure alone.

    Parameters
    ----------
    file_name : str|SICDDetails

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

    sicd_xml, root_node, xml_ns, urn_string = None, None, None, None
    if isinstance(file_name, SICDDetails):
        sicd_xml, root_node, xml_ns = _get_sicd_xml_from_nitf(file_name)
    else:
        # check if this is just an xml file
        with open(file_name, 'rb') as fi:
            initial_bits = fi.read(100)
            if initial_bits.startswith(b'<?xml') or initial_bits.startswith(b'<SICD'):
                sicd_xml = fi.read().decode('utf-8')
                root_node, xml_ns = parse_xml_from_string(sicd_xml)

        if sicd_xml is None:
            # try to first test whether this is SICD file
            try:
                sicd_details = SICDDetails(file_name)
                if not sicd_details.is_sicd:
                    logger.error('File {} is a NITF file, but is apparently not a SICD file.')
                    return False
                sicd_xml, root_node, xml_ns = _get_sicd_xml_from_nitf(sicd_details)
            except IOError:
                pass

    if sicd_xml is None:
        logger.error('Could not interpret input {}'.format(file_name))
        return False

    urn_string = xml_ns['default']
    valid_xml = evaluate_xml_versus_schema(sicd_xml, urn_string)
    if valid_xml is None:
        valid_xml = True

    the_sicd = SICDType.from_node(root_node, xml_ns=xml_ns)
    valid_sicd_contents = the_sicd.is_valid(recursive=True, stack=False)
    return valid_xml & valid_sicd_contents
Esempio n. 9
0
def _evaluate_xml_string_validity(xml_string):
    """
    Check the validity of the SICD xml, as defined by the given string.

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

    Returns
    -------
    (bool, str, SICDType)
    """

    root_node, xml_ns = parse_xml_from_string(xml_string)
    if xml_ns is None:
        raise ValueError(
            'SICD XML invalid, because no apparent namespace defined in the xml,\n\t'
            'which starts `{}...`'.format(xml_string[:15]))

    if 'default' not in xml_ns:
        raise ValueError(
            'Could not properly interpret the namespace collection from xml\n{}'
            .format(xml_ns))

    sicd_urn = xml_ns['default']
    # check that our urn is mapped
    try:
        _ = get_urn_details(sicd_urn)
        check_schema = True
    except Exception as e:
        logger.exception('SICD: The SICD namespace has unrecognized value')
        check_schema = False

    valid_xml = None
    if check_schema:
        valid_xml = evaluate_xml_versus_schema(xml_string, sicd_urn)
    if valid_xml is None:
        valid_xml = True

    # perform the various sicd structure checks
    the_sicd = SICDType.from_node(root_node, xml_ns=xml_ns)
    valid_sicd_contents = the_sicd.is_valid(recursive=True, stack=False)
    return valid_xml & valid_sicd_contents, sicd_urn, the_sicd
Esempio n. 10
0
    def get_sicd_collection(self):
        """
        Gets the list of sicd objects, one per polarimetric entry.

        Returns
        -------
        Tuple[SICDType]
        """

        nitf, collection_info = self._get_collection_info()
        image_creation = self._get_image_creation()
        image_data, geo_data = self._get_image_and_geo_data()
        position = self._get_position()
        grid = self._get_grid()
        radar_collection = self._get_radar_collection()
        timeline = self._get_timeline()
        image_formation = self._get_image_formation(timeline, radar_collection)
        scpcoa = self._get_scpcoa()
        rma = self._get_rma_adjust_grid(scpcoa, grid, image_data, position,
                                        collection_info)
        radiometric = self._get_radiometric(image_data, grid)
        base_sicd = SICDType(CollectionInfo=collection_info,
                             ImageCreation=image_creation,
                             GeoData=geo_data,
                             ImageData=image_data,
                             Position=position,
                             Grid=grid,
                             RadarCollection=radar_collection,
                             Timeline=timeline,
                             ImageFormation=image_formation,
                             SCPCOA=scpcoa,
                             RMA=rma,
                             Radiometric=radiometric)
        if len(nitf) > 0:
            base_sicd._NITF = nitf
        self._update_geo_data(base_sicd)
        base_sicd.derive()  # derive all the fields
        # now, make one copy per polarimetric entry, as appropriate
        tx_pols, tx_rcv_pols = self._get_polarizations()
        sicd_list = []
        for i, entry in enumerate(tx_rcv_pols):
            this_sicd = base_sicd.copy()
            this_sicd.ImageFormation.RcvChanProc.ChanIndices = [
                i + 1,
            ]
            this_sicd.ImageFormation.TxRcvPolarizationProc = \
                this_sicd.RadarCollection.RcvChannels[i].TxRcvPolarization
            this_sicd.populate_rniirs(override=False)
            sicd_list.append(this_sicd)
        return tuple(sicd_list)
Esempio n. 11
0
 def update_radiometric(sicd: SICDType, band_name: str) -> None:
     if self.mission_id in ['KMPS', 'CSG']:
         # TODO: skipping for now - strange results for flag == 77. Awaiting gidance - see Wade.
         return
     if h5_dict['Range Spreading Loss Compensation Geometry'] != 'NONE':
         slant_range = h5_dict['Reference Slant Range']
         exp = h5_dict['Reference Slant Range Exponent']
         sf = slant_range**(2 * exp)
         rsf = h5_dict['Rescaling Factor']
         sf /= rsf * rsf
         if h5_dict.get('Calibration Constant Compensation Flag',
                        None) == 0:
             cal = band_dict[band_name]['Calibration Constant']
             sf /= cal
         sicd.Radiometric = RadiometricType(BetaZeroSFPoly=Poly2DType(
             Coefs=[
                 [
                     sf,
                 ],
             ]))
Esempio n. 12
0
    def get_sicd(self):
        """
        Get the SICD metadata for the image.

        Returns
        -------
        SICDType
        """
        def convert_string_dict(dict_in):
            # type: (dict) -> dict
            dict_out = OrderedDict()
            for key, val in dict_in.items():
                if isinstance(val, str):
                    dict_out[key] = val
                elif isinstance(val, int):
                    dict_out[key] = str(val)
                elif isinstance(val, float):
                    dict_out[key] = '{0:0.17G}'.format(val)
                else:
                    raise TypeError('Got unhandled type {}'.format(type(val)))
            return dict_out

        def extract_state_vector():
            # type: () -> (numpy.ndarray, numpy.ndarray, numpy.ndarray)
            vecs = collect['state']['state_vectors']
            times = numpy.zeros((len(vecs), ), dtype=numpy.float64)
            positions = numpy.zeros((len(vecs), 3), dtype=numpy.float64)
            velocities = numpy.zeros((len(vecs), 3), dtype=numpy.float64)
            for i, entry in enumerate(vecs):
                times[i] = get_seconds(parse_timestring(entry['time'],
                                                        precision='ns'),
                                       start_time,
                                       precision='ns')
                positions[i, :] = entry['position']
                velocities[i, :] = entry['velocity']
            return times, positions, velocities

        def get_radar_parameter(name):
            if name in radar:
                return radar[name]
            if len(radar_time_varying) > 0:
                element = radar_time_varying[0]
                if name in element:
                    return element[name]
            raise ValueError(
                'Unable to determine radar parameter `{}`'.format(name))

        def get_collection_info():
            # type: () -> CollectionInfoType
            coll_name = collect['platform']
            mode = collect['mode'].strip().lower()
            if mode == 'stripmap':
                radar_mode = RadarModeType(ModeType='STRIPMAP', ModeID=mode)
            elif mode == 'spotlight':
                radar_mode = RadarModeType(ModeType='SPOTLIGHT', ModeID=mode)
            elif mode == 'sliding_spotlight':
                radar_mode = RadarModeType(ModeType='DYNAMIC STRIPMAP',
                                           ModeID=mode)
            else:
                raise ValueError('Got unhandled radar mode {}'.format(mode))

            return CollectionInfoType(CollectorName=coll_name,
                                      CoreName=collect['collect_id'],
                                      RadarMode=radar_mode,
                                      Classification='UNCLASSIFIED',
                                      CollectType='MONOSTATIC')

        def get_image_creation():
            # type: () -> ImageCreationType
            from sarpy.__about__ import __version__
            return ImageCreationType(
                Application=self._tiff_details.tags['Software'],
                DateTime=parse_timestring(
                    self._img_desc_tags['processing_time'], precision='us'),
                Profile='sarpy {}'.format(__version__),
                Site='Unknown')

        def get_image_data():
            # type: () -> ImageDataType
            rows = int(
                img['columns'])  # capella uses flipped row/column definition?
            cols = int(img['rows'])
            if img['data_type'] == 'CInt16':
                pixel_type = 'RE16I_IM16I'
            else:
                raise ValueError('Got unhandled data_type {}'.format(
                    img['data_type']))

            scp_pixel = (int(0.5 * rows), int(0.5 * cols))
            if radar['pointing'] == 'left':
                scp_pixel = (rows - scp_pixel[0] - 1, cols - scp_pixel[1] - 1)

            return ImageDataType(NumRows=rows,
                                 NumCols=cols,
                                 FirstRow=0,
                                 FirstCol=0,
                                 PixelType=pixel_type,
                                 FullImage=(rows, cols),
                                 SCPPixel=scp_pixel)

        def get_geo_data():
            # type: () -> GeoDataType
            return GeoDataType(SCP=SCPType(
                ECF=img['center_pixel']['target_position']))

        def get_position():
            # type: () -> PositionType
            px, py, pz = fit_position_xvalidation(state_time,
                                                  state_position,
                                                  state_velocity,
                                                  max_degree=8)
            return PositionType(ARPPoly=XYZPolyType(X=px, Y=py, Z=pz))

        def get_grid():
            # type: () -> GridType

            def get_weight(window_dict):
                window_name = window_dict['name']
                if window_name.lower() == 'rectangular':
                    return WgtTypeType(WindowName='UNIFORM'), None
                elif window_name.lower() == 'avci-nacaroglu':
                    return WgtTypeType(
                        WindowName=window_name.upper(),
                        Parameters=convert_string_dict(window_dict['parameters'])), \
                           avci_nacaroglu_window(64, alpha=window_dict['parameters']['alpha'])
                else:
                    return WgtTypeType(WindowName=window_name,
                                       Parameters=convert_string_dict(
                                           window_dict['parameters'])), None

            image_plane = 'SLANT'
            grid_type = 'RGZERO'

            coa_time = parse_timestring(img['center_pixel']['center_time'],
                                        precision='ns')
            row_bw = img.get('processed_range_bandwidth', bw)
            row_imp_rsp_bw = 2 * row_bw / speed_of_light
            row_wgt, row_wgt_funct = get_weight(img['range_window'])
            row = DirParamType(SS=img['image_geometry']['delta_range_sample'],
                               Sgn=-1,
                               ImpRespBW=row_imp_rsp_bw,
                               ImpRespWid=img['range_resolution'],
                               KCtr=2 * fc / speed_of_light,
                               DeltaK1=-0.5 * row_imp_rsp_bw,
                               DeltaK2=0.5 * row_imp_rsp_bw,
                               DeltaKCOAPoly=[
                                   [
                                       0.0,
                                   ],
                               ],
                               WgtFunct=row_wgt_funct,
                               WgtType=row_wgt)

            # get timecoa value
            timecoa_value = get_seconds(coa_time, start_time)
            # find an approximation for zero doppler spacing - necessarily rough for backprojected images
            col_ss = img['pixel_spacing_row']
            dop_bw = img['processed_azimuth_bandwidth']

            col_wgt, col_wgt_funct = get_weight(img['azimuth_window'])
            col = DirParamType(SS=col_ss,
                               Sgn=-1,
                               ImpRespWid=img['azimuth_resolution'],
                               ImpRespBW=dop_bw * abs(ss_zd_s) / col_ss,
                               KCtr=0,
                               WgtFunct=col_wgt_funct,
                               WgtType=col_wgt)

            # TODO:
            #   column deltakcoa poly - it's in there at ["image"]["frequency_doppler_centroid_polynomial"]

            return GridType(ImagePlane=image_plane,
                            Type=grid_type,
                            TimeCOAPoly=[
                                [
                                    timecoa_value,
                                ],
                            ],
                            Row=row,
                            Col=col)

        def get_radar_collection():
            # type: () -> RadarCollectionType

            freq_min = fc - 0.5 * bw
            return RadarCollectionType(
                TxPolarization=radar['transmit_polarization'],
                TxFrequency=(freq_min, freq_min + bw),
                Waveform=[
                    WaveformParametersType(
                        TxRFBandwidth=bw,
                        TxPulseLength=get_radar_parameter('pulse_duration'),
                        RcvDemodType='CHIRP',
                        ADCSampleRate=radar['sampling_frequency'],
                        TxFreqStart=freq_min)
                ],
                RcvChannels=[
                    ChanParametersType(TxRcvPolarization='{}:{}'.format(
                        radar['transmit_polarization'],
                        radar['receive_polarization']))
                ])

        def get_timeline():
            # type: () -> TimelineType
            prf = radar['prf'][0]['prf']
            return TimelineType(CollectStart=start_time,
                                CollectDuration=duration,
                                IPP=[
                                    IPPSetType(TStart=0,
                                               TEnd=duration,
                                               IPPStart=0,
                                               IPPEnd=duration * prf,
                                               IPPPoly=(0, prf)),
                                ])

        def get_image_formation():
            # type: () -> ImageFormationType

            algo = img['algorithm'].upper()
            processings = None
            if algo == 'BACKPROJECTION':
                processings = [
                    ProcessingType(Type='Backprojected to DEM', Applied=True),
                ]
            else:
                logger.warning('Got unexpected algorithm, the results for the '
                               'sicd struture might be unexpected')

            if algo not in ('PFA', 'RMA', 'RGAZCOMP'):
                logger.warning(
                    'Image formation algorithm {} not one of the recognized SICD options, '
                    'being set to "OTHER".'.format(algo))
                algo = 'OTHER'

            return ImageFormationType(
                RcvChanProc=RcvChanProcType(NumChanProc=1, PRFScaleFactor=1),
                ImageFormAlgo=algo,
                TStartProc=0,
                TEndProc=duration,
                TxRcvPolarizationProc='{}:{}'.format(
                    radar['transmit_polarization'],
                    radar['receive_polarization']),
                TxFrequencyProc=(radar_collection.TxFrequency.Min,
                                 radar_collection.TxFrequency.Max),
                STBeamComp='NO',
                ImageBeamComp='NO',
                AzAutofocus='NO',
                RgAutofocus='NO',
                Processings=processings)

        def get_rma():
            # type: () -> RMAType
            img_geometry = img['image_geometry']
            near_range = img_geometry['range_to_first_sample']
            center_time = parse_timestring(img['center_pixel']['center_time'],
                                           precision='us')
            first_time = parse_timestring(img_geometry['first_line_time'],
                                          precision='us')
            zd_time_scp = get_seconds(center_time, first_time, 'us')
            r_ca_scp = near_range + image_data.SCPPixel.Row * grid.Row.SS
            time_ca_poly = numpy.array(
                [zd_time_scp, -look * ss_zd_s / grid.Col.SS], dtype='float64')

            timecoa_value = get_seconds(center_time, start_time)
            arp_velocity = position.ARPPoly.derivative_eval(timecoa_value,
                                                            der_order=1)
            vm_ca = numpy.linalg.norm(arp_velocity)
            inca = INCAType(R_CA_SCP=r_ca_scp,
                            FreqZero=fc,
                            TimeCAPoly=time_ca_poly,
                            DRateSFPoly=[
                                [1 / (vm_ca * ss_zd_s / grid.Col.SS)],
                            ])

            return RMAType(RMAlgoType='RG_DOP', INCA=inca)

        def get_radiometric():
            # type: () -> Union[None, RadiometricType]
            if img['radiometry'].lower() != 'beta_nought':
                logger.warning('Got unrecognized Capella radiometry {},\n\t'
                               'skipping the radiometric metadata'.format(
                                   img['radiometry']))
                return None

            return RadiometricType(BetaZeroSFPoly=[
                [
                    img['scale_factor']**2,
                ],
            ])

        def add_noise():
            if sicd.Radiometric is None:
                return

            nesz_raw = numpy.array(img['nesz_polynomial']['coefficients'],
                                   dtype='float64')
            test_value = polynomial.polyval(rma.INCA.R_CA_SCP, nesz_raw)
            if abs(test_value - img['nesz_peak']) > 100:
                # this polynomial reversed in early versions, so reverse if evaluated results are nonsense
                nesz_raw = nesz_raw[::-1]
            nesz_poly_raw = Poly2DType(Coefs=numpy.reshape(nesz_raw, (-1, 1)))
            noise_coeffs = nesz_poly_raw.shift(-rma.INCA.R_CA_SCP,
                                               1,
                                               0,
                                               1,
                                               return_poly=False)
            # this is in nesz units, so shift to absolute units
            noise_coeffs[0] -= 10 * numpy.log10(
                sicd.Radiometric.SigmaZeroSFPoly[0, 0])
            sicd.Radiometric.NoiseLevel = NoiseLevelType_(
                NoiseLevelType='ABSOLUTE', NoisePoly=noise_coeffs)

        # extract general use information
        collect = self._img_desc_tags['collect']
        img = collect['image']
        radar = collect['radar']
        radar_time_varying = radar.get('time_varying_parameters', [])

        start_time = parse_timestring(collect['start_timestamp'],
                                      precision='ns')
        end_time = parse_timestring(collect['stop_timestamp'], precision='ns')
        duration = get_seconds(end_time, start_time, precision='ns')
        state_time, state_position, state_velocity = extract_state_vector()
        bw = get_radar_parameter('pulse_bandwidth')
        fc = get_radar_parameter('center_frequency')
        ss_zd_s = img['image_geometry']['delta_line_time']
        look = -1 if radar['pointing'] == 'right' else 1

        # define the sicd elements
        collection_info = get_collection_info()
        image_creation = get_image_creation()
        image_data = get_image_data()
        geo_data = get_geo_data()
        position = get_position()
        grid = get_grid()
        radar_collection = get_radar_collection()
        timeline = get_timeline()
        image_formation = get_image_formation()
        rma = get_rma()
        radiometric = get_radiometric()

        sicd = SICDType(CollectionInfo=collection_info,
                        ImageCreation=image_creation,
                        ImageData=image_data,
                        GeoData=geo_data,
                        Position=position,
                        Grid=grid,
                        RadarCollection=radar_collection,
                        Timeline=timeline,
                        ImageFormation=image_formation,
                        RMA=rma,
                        Radiometric=radiometric)
        sicd.derive()

        add_noise()
        sicd.populate_rniirs(override=False)
        return sicd
Esempio n. 13
0
def extract_sicd(img_header, symmetry, nitf_header=None):
    """
    Extract the best available SICD structure from relevant nitf header structures.

    Parameters
    ----------
    img_header : ImageSegmentHeader|ImageSegmentHeader0
    symmetry : tuple
    nitf_header : None|NITFHeader|NITFHeader0

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

    def get_collection_info():
        # type: () -> CollectionInfoType
        isorce = img_header.ISORCE.strip()
        collector_name = None if len(isorce) < 1 else isorce

        iid2 = img_header.IID2.strip()
        core_name = img_header.IID1.strip() if len(iid2) < 1 else iid2

        class_str = img_header.Security.CLAS
        if class_str == 'T':
            classification = 'TOPSECRET'
        elif class_str == 'S':
            classification = 'SECRET'
        elif class_str == 'C':
            classification = 'CONFIDENTIAL'
        elif class_str == 'U':
            classification = 'UNCLASSIFIED'
        else:
            classification = ''
        ctlh = img_header.Security.CTLH.strip()
        if len(ctlh) < 1:
            classification += '//' + ctlh
        code = img_header.Security.CODE.strip()
        if len(code) < 1:
            classification += '//' + code

        return CollectionInfoType(
            CollectorName=collector_name,
            CoreName=core_name,
            Classification=classification)

    def get_image_data():
        # type: () -> ImageDataType
        pvtype = img_header.PVTYPE
        if pvtype == 'C':
            if img_header.NBPP != 64:
                logger.warning(
                    'This NITF has complex bands that are not 64-bit.\n\t'
                    'This is not currently supported.')
            pixel_type = 'RE32F_IM32F'
        elif pvtype == 'R':
            if img_header.NBPP == 64:
                logger.warning(
                    'The real/imaginary data in the NITF are stored as 64-bit floating point.\n\t'
                    'The closest Pixel Type, RE32F_IM32F, will be used,\n\t'
                    'but there may be overflow issues if converting this file.')
            pixel_type = 'RE32F_IM32F'
        elif pvtype == 'SI':
            pixel_type = 'RE16I_IM16I'
        else:
            raise ValueError('Got unhandled PVTYPE {}'.format(pvtype))

        if symmetry[2]:
            rows = img_header.NCOLS
            cols = img_header.NROWS
        else:
            rows = img_header.NROWS
            cols = img_header.NCOLS
        return ImageDataType(
            PixelType=pixel_type,
            NumRows=rows,
            NumCols=cols,
            FirstRow=0,
            FirstCol=0,
            FullImage=(rows, cols),
            SCPPixel=(0.5 * rows, 0.5 * cols))

    def append_country_code(cc):
        if len(cc) > 0:
            if the_sicd.CollectionInfo is None:
                the_sicd.CollectionInfo = CollectionInfoType(CountryCodes=[cc, ])
            elif the_sicd.CollectionInfo.CountryCodes is None:
                the_sicd.CollectionInfo.CountryCodes = [cc, ]
            elif cc not in the_sicd.CollectionInfo.CountryCodes:
                the_sicd.CollectionInfo.CountryCodes.append(cc)

    def set_image_corners(icps, override=False):
        if the_sicd.GeoData is None:
             the_sicd.GeoData = GeoDataType(ImageCorners=icps)
        elif the_sicd.GeoData.ImageCorners is None or override:
            the_sicd.GeoData.ImageCorners = icps

    def set_arp_position(arp_ecf, override=False):
        if the_sicd.SCPCOA is None:
            the_sicd.SCPCOA = SCPCOAType(ARPPos=arp_ecf)
        elif override:
            # prioritize this information first - it should be more reliable than other sources
            the_sicd.SCPCOA.ARPPos = arp_ecf

    def set_scp(scp_ecf, scp_pixel, override=False):
        def set_scppixel():
            if the_sicd.ImageData is None:
                the_sicd.ImageData = ImageDataType(SCPPixel=scp_pixel)
            else:
                the_sicd.ImageData.SCPPixel = scp_pixel
        if the_sicd.GeoData is None:
            the_sicd.GeoData = GeoDataType(SCP=SCPType(ECF=scp_ecf))
            set_scppixel()
        elif the_sicd.GeoData.SCP is None or override:
            the_sicd.GeoData.SCP = SCPType(ECF=scp_ecf)
            set_scppixel()

    def set_collect_start(collect_start, override=False):
        if the_sicd.Timeline is None:
            the_sicd.Timeline = TimelineType(CollectStart=collect_start)
        elif the_sicd.Timeline.CollectStart is None or override:
            the_sicd.Timeline.CollectStart = collect_start

    def set_uvects(row_unit, col_unit):
        if the_sicd.Grid is None:
            the_sicd.Grid = GridType(Row=DirParamType(UVectECF=row_unit),
                                     Col=DirParamType(UVectECF=col_unit))
            return

        if the_sicd.Grid.Row is None:
            the_sicd.Grid.Row = DirParamType(UVectECF=row_unit)
        elif the_sicd.Grid.Row.UVectECF is None:
            the_sicd.Grid.Row.UVectECF = row_unit

        if the_sicd.Grid.Col is None:
            the_sicd.Grid.Col = DirParamType(UVectECF=col_unit)
        elif the_sicd.Grid.Col.UVectECF is None:
            the_sicd.Grid.Col.UVectECF = col_unit

    def try_CMETAA():
        tre = None if tres is None else tres['CMETAA']  # type: CMETAA
        if tre is None:
            return

        cmetaa = tre.DATA

        if the_sicd.GeoData is None:
            the_sicd.GeoData = GeoDataType()
        if the_sicd.SCPCOA is None:
            the_sicd.SCPCOA = SCPCOAType()
        if the_sicd.Grid is None:
            the_sicd.Grid = GridType()
        if the_sicd.Timeline is None:
            the_sicd.Timeline = TimelineType()
        if the_sicd.RadarCollection is None:
            the_sicd.RadarCollection = RadarCollectionType()
        if the_sicd.ImageFormation is None:
            the_sicd.ImageFormation = ImageFormationType()

        the_sicd.SCPCOA.SCPTime = 0.5*float(cmetaa.WF_CDP)
        the_sicd.GeoData.SCP = SCPType(ECF=tre.get_scp())
        the_sicd.SCPCOA.ARPPos = tre.get_arp()

        the_sicd.SCPCOA.SideOfTrack = cmetaa.CG_LD.strip().upper()
        the_sicd.SCPCOA.SlantRange = float(cmetaa.CG_SRAC)
        the_sicd.SCPCOA.DopplerConeAng = float(cmetaa.CG_CAAC)
        the_sicd.SCPCOA.GrazeAng = float(cmetaa.CG_GAAC)
        the_sicd.SCPCOA.IncidenceAng = 90 - float(cmetaa.CG_GAAC)
        if hasattr(cmetaa, 'CG_TILT'):
            the_sicd.SCPCOA.TwistAng = float(cmetaa.CG_TILT)
        if hasattr(cmetaa, 'CG_SLOPE'):
            the_sicd.SCPCOA.SlopeAng = float(cmetaa.CG_SLOPE)

        the_sicd.ImageData.SCPPixel = [int(cmetaa.IF_DC_IS_COL), int(cmetaa.IF_DC_IS_ROW)]
        img_corners = tre.get_image_corners()
        if img_corners is not None:
            the_sicd.GeoData.ImageCorners = img_corners

        if cmetaa.CMPLX_SIGNAL_PLANE.upper() == 'S':
            the_sicd.Grid.ImagePlane = 'SLANT'
        elif cmetaa.CMPLX_SIGNAL_PLANE.upper() == 'G':
            the_sicd.Grid.ImagePlane = 'GROUND'
        else:
            logger.warning(
                'Got unexpected CMPLX_SIGNAL_PLANE value {},\n\t'
                'setting ImagePlane to SLANT'.format(cmetaa.CMPLX_SIGNAL_PLANE))

        the_sicd.Grid.Row = DirParamType(
            SS=float(cmetaa.IF_RSS),
            ImpRespWid=float(cmetaa.IF_RGRES),
            Sgn=1 if cmetaa.IF_RFFTS.strip() == '-' else -1,  # opposite sign convention
            ImpRespBW=float(cmetaa.IF_RFFT_SAMP)/(float(cmetaa.IF_RSS)*float(cmetaa.IF_RFFT_TOT)))
        the_sicd.Grid.Col = DirParamType(
            SS=float(cmetaa.IF_AZSS),
            ImpRespWid=float(cmetaa.IF_AZRES),
            Sgn=1 if cmetaa.IF_AFFTS.strip() == '-' else -1,  # opposite sign convention
            ImpRespBW=float(cmetaa.IF_AZFFT_SAMP)/(float(cmetaa.IF_AZSS)*float(cmetaa.IF_AZFFT_TOT)))
        cmplx_weight = cmetaa.CMPLX_WEIGHT.strip().upper()
        if cmplx_weight == 'UWT':
            the_sicd.Grid.Row.WgtType = WgtTypeType(WindowName='UNIFORM')
            the_sicd.Grid.Col.WgtType = WgtTypeType(WindowName='UNIFORM')
        elif cmplx_weight == 'HMW':
            the_sicd.Grid.Row.WgtType = WgtTypeType(WindowName='HAMMING')
            the_sicd.Grid.Col.WgtType = WgtTypeType(WindowName='HAMMING')
        elif cmplx_weight == 'HNW':
            the_sicd.Grid.Row.WgtType = WgtTypeType(WindowName='HANNING')
            the_sicd.Grid.Col.WgtType = WgtTypeType(WindowName='HANNING')
        elif cmplx_weight == 'TAY':
            the_sicd.Grid.Row.WgtType = WgtTypeType(
                WindowName='TAYLOR',
                Parameters={
                    'SLL': '-{0:d}'.format(int(cmetaa.CMPLX_RNG_SLL)),
                    'NBAR': '{0:d}'.format(int(cmetaa.CMPLX_RNG_TAY_NBAR))})
            the_sicd.Grid.Col.WgtType = WgtTypeType(
                WindowName='TAYLOR',
                Parameters={
                    'SLL': '-{0:d}'.format(int(cmetaa.CMPLX_AZ_SLL)),
                    'NBAR': '{0:d}'.format(int(cmetaa.CMPLX_AZ_TAY_NBAR))})
        else:
            logger.warning(
                'Got unsupported CMPLX_WEIGHT value {}.\n\tThe resulting SICD will '
                'not have valid weight array populated'.format(cmplx_weight))
        the_sicd.Grid.Row.define_weight_function()
        the_sicd.Grid.Col.define_weight_function()

        # noinspection PyBroadException
        try:
            date_str = cmetaa.T_UTC_YYYYMMMDD
            time_str = cmetaa.T_HHMMSSUTC
            date_time = _iso_date_format.format(
                date_str[:4], date_str[4:6], date_str[6:8],
                time_str[:2], time_str[2:4], time_str[4:6])
            the_sicd.Timeline.CollectStart = numpy.datetime64(date_time, 'us')
        except Exception:
            logger.info('Failed extracting start time from CMETAA')
            pass
        the_sicd.Timeline.CollectDuration = float(cmetaa.WF_CDP)
        the_sicd.Timeline.IPP = [
            IPPSetType(TStart=0,
                       TEnd=float(cmetaa.WF_CDP),
                       IPPStart=0,
                       IPPEnd=numpy.floor(float(cmetaa.WF_CDP)*float(cmetaa.WF_PRF)),
                       IPPPoly=[0, float(cmetaa.WF_PRF)])]

        the_sicd.RadarCollection.TxFrequency = TxFrequencyType(
            Min=float(cmetaa.WF_SRTFR),
            Max=float(cmetaa.WF_ENDFR))
        the_sicd.RadarCollection.TxPolarization = cmetaa.POL_TR.upper()
        the_sicd.RadarCollection.Waveform = [WaveformParametersType(
            TxPulseLength=float(cmetaa.WF_WIDTH),
            TxRFBandwidth=float(cmetaa.WF_BW),
            TxFreqStart=float(cmetaa.WF_SRTFR),
            TxFMRate=float(cmetaa.WF_CHRPRT)*1e12)]
        tx_rcv_pol = '{}:{}'.format(cmetaa.POL_TR.upper(), cmetaa.POL_RE.upper())
        the_sicd.RadarCollection.RcvChannels = [
            ChanParametersType(TxRcvPolarization=tx_rcv_pol)]

        the_sicd.ImageFormation.TxRcvPolarizationProc = tx_rcv_pol
        if_process = cmetaa.IF_PROCESS.strip().upper()
        if if_process == 'PF':
            the_sicd.ImageFormation.ImageFormAlgo = 'PFA'
            scp_ecf = tre.get_scp()
            fpn_ned = numpy.array(
                [float(cmetaa.CG_FPNUV_X), float(cmetaa.CG_FPNUV_Y), float(cmetaa.CG_FPNUV_Z)], dtype='float64')
            ipn_ned = numpy.array(
                [float(cmetaa.CG_IDPNUVX), float(cmetaa.CG_IDPNUVY), float(cmetaa.CG_IDPNUVZ)], dtype='float64')
            fpn_ecf = ned_to_ecf(fpn_ned, scp_ecf, absolute_coords=False)
            ipn_ecf = ned_to_ecf(ipn_ned, scp_ecf, absolute_coords=False)
            the_sicd.PFA = PFAType(FPN=fpn_ecf, IPN=ipn_ecf)
        elif if_process in ['RM', 'CD']:
            the_sicd.ImageFormation.ImageFormAlgo = 'RMA'

        # the remainder of this is guesswork to define required fields
        the_sicd.ImageFormation.TStartProc = 0  # guess work
        the_sicd.ImageFormation.TEndProc = float(cmetaa.WF_CDP)
        the_sicd.ImageFormation.TxFrequencyProc = TxFrequencyProcType(
            MinProc=float(cmetaa.WF_SRTFR), MaxProc=float(cmetaa.WF_ENDFR))
        # all remaining guess work
        the_sicd.ImageFormation.STBeamComp = 'NO'
        the_sicd.ImageFormation.ImageBeamComp = 'SV' if cmetaa.IF_BEAM_COMP[0] == 'Y' else 'NO'
        the_sicd.ImageFormation.AzAutofocus = 'NO' if cmetaa.AF_TYPE[0] == 'N' else 'SV'
        the_sicd.ImageFormation.RgAutofocus = 'NO'

    def try_AIMIDA():
        tre = None if tres is None else tres['AIMIDA']
        if tre is None:
            return
        aimida = tre.DATA

        append_country_code(aimida.COUNTRY.strip())

        create_time = datetime.strptime(aimida.CREATION_DATE, '%d%b%y')
        if the_sicd.ImageCreation is None:
            the_sicd.ImageCreation = ImageCreationType(DateTime=create_time)
        elif the_sicd.ImageCreation.DateTime is None:
            the_sicd.ImageCreation.DateTime = create_time

        collect_start = datetime.strptime(aimida.MISSION_DATE+aimida.TIME, '%d%b%y%H%M')
        set_collect_start(collect_start, override=False)

    def try_AIMIDB():
        tre = None if tres is None else tres['AIMIDB']
        if tre is None:
            return
        aimidb = tre.DATA

        append_country_code(aimidb.COUNTRY.strip())

        if the_sicd.ImageFormation is not None and the_sicd.ImageFormation.SegmentIdentifier is None:
            the_sicd.ImageFormation.SegmentIdentifier = aimidb.CURRENT_SEGMENT.strip()

        date_str = aimidb.ACQUISITION_DATE
        collect_start = numpy.datetime64(_iso_date_format.format(
            date_str[:4], date_str[4:6], date_str[6:8],
            date_str[8:10], date_str[10:12], date_str[12:14]), 'us')
        set_collect_start(collect_start, override=False)

    def try_ACFT():
        if tres is None:
            return
        tre = tres['ACFTA']
        if tre is None:
            tre = tres['ACFTB']
        if tre is None:
            return
        acft = tre.DATA

        sensor_id = acft.SENSOR_ID.strip()
        if len(sensor_id) > 1:
            if the_sicd.CollectionInfo is None:
                the_sicd.CollectionInfo = CollectionInfoType(CollectorName=sensor_id)
            elif the_sicd.CollectionInfo.CollectorName is None:
                the_sicd.CollectionInfo.CollectorName = sensor_id

        row_ss = float(acft.ROW_SPACING)
        col_ss = float(acft.COL_SPACING)

        if hasattr(acft, 'ROW_SPACING_UNITS') and acft.ROW_SPACING_UNITS.strip().lower() == 'f':
            row_ss *= foot
        if hasattr(acft, 'COL_SPACING_UNITS') and acft.COL_SPACING_UNITS.strip().lower() == 'f':
            col_ss *= foot

        # NB: these values are actually ground plane values, and should be
        # corrected to slant plane if possible
        if the_sicd.SCPCOA is not None:
            if the_sicd.SCPCOA.GrazeAng is not None:
                col_ss *= numpy.cos(numpy.deg2rad(the_sicd.SCPCOA.GrazeAng))
            if the_sicd.SCPCOA.TwistAng is not None:
                row_ss *= numpy.cos(numpy.deg2rad(the_sicd.SCPCOA.TwistAng))

        if the_sicd.Grid is None:
            the_sicd.Grid = GridType(Row=DirParamType(SS=row_ss), Col=DirParamType(SS=col_ss))
            return

        if the_sicd.Grid.Row is None:
            the_sicd.Grid.Row = DirParamType(SS=row_ss)
        elif the_sicd.Grid.Row.SS is None:
            the_sicd.Grid.Row.SS = row_ss

        if the_sicd.Grid.Col is None:
            the_sicd.Grid.Col = DirParamType(SS=col_ss)
        elif the_sicd.Grid.Col.SS is None:
            the_sicd.Grid.Col.SS = col_ss

    def try_BLOCKA():
        tre = None if tres is None else tres['BLOCKA']
        if tre is None:
            return
        blocka = tre.DATA

        icps = []
        for fld_name in ['FRFC_LOC', 'FRLC_LOC', 'LRLC_LOC', 'LRFC_LOC']:
            value = getattr(blocka, fld_name)
            # noinspection PyBroadException
            try:
                lat_val = float(value[:10])
                lon_val = float(value[10:21])
            except ValueError:
                lat_val = lat_lon_parser(value[:10])
                lon_val = lat_lon_parser(value[10:21])

            icps.append([lat_val, lon_val])
        set_image_corners(icps, override=False)

    def try_MPDSRA():
        def valid_array(arr):
            return numpy.all(numpy.isfinite(arr)) and numpy.any(arr != 0)

        tre = None if tres is None else tres['MPDSRA']
        if tre is None:
            return
        mpdsra = tre.DATA

        scp_ecf = foot*numpy.array(
            [float(mpdsra.ORO_X), float(mpdsra.ORO_Y), float(mpdsra.ORO_Z)], dtype='float64')
        if valid_array(scp_ecf):
            set_scp(scp_ecf, (int(mpdsra.ORP_COLUMN) - 1, int(mpdsra.ORP_ROW) - 1), override=False)

        arp_pos_ned = foot*numpy.array(
            [float(mpdsra.ARP_POS_N), float(mpdsra.ARP_POS_E), float(mpdsra.ARP_POS_D)], dtype='float64')
        arp_vel_ned = foot*numpy.array(
            [float(mpdsra.ARP_VEL_N), float(mpdsra.ARP_VEL_E), float(mpdsra.ARP_VEL_D)], dtype='float64')
        arp_acc_ned = foot*numpy.array(
            [float(mpdsra.ARP_ACC_N), float(mpdsra.ARP_ACC_E), float(mpdsra.ARP_ACC_D)], dtype='float64')
        arp_pos = ned_to_ecf(arp_pos_ned, scp_ecf, absolute_coords=True) if valid_array(arp_pos_ned) else None
        set_arp_position(arp_pos, override=False)

        arp_vel = ned_to_ecf(arp_vel_ned, scp_ecf, absolute_coords=False) if valid_array(arp_vel_ned) else None
        if the_sicd.SCPCOA.ARPVel is None:
            the_sicd.SCPCOA.ARPVel = arp_vel
        arp_acc = ned_to_ecf(arp_acc_ned, scp_ecf, absolute_coords=False) if valid_array(arp_acc_ned) else None
        if the_sicd.SCPCOA.ARPAcc is None:
            the_sicd.SCPCOA.ARPAcc = arp_acc

        if the_sicd.PFA is not None and the_sicd.PFA.FPN is None:
            # TODO: is this already in meters?
            fpn_ecf = numpy.array(
                [float(mpdsra.FOC_X), float(mpdsra.FOC_Y), float(mpdsra.FOC_Z)], dtype='float64')  # *foot
            if valid_array(fpn_ecf):
                the_sicd.PFA.FPN = fpn_ecf

    def try_MENSRB():
        tre = None if tres is None else tres['MENSRB']
        if tre is None:
            return
        mensrb = tre.DATA

        arp_llh = numpy.array(
            [lat_lon_parser(mensrb.ACFT_LOC[:12]),
             lat_lon_parser(mensrb.ACFT_LOC[12:25]),
             foot*float(mensrb.ACFT_ALT)], dtype='float64')
        scp_llh = numpy.array(
            [lat_lon_parser(mensrb.RP_LOC[:12]),
             lat_lon_parser(mensrb.RP_LOC[12:25]),
             foot*float(mensrb.RP_ELV)], dtype='float64')
        # TODO: handle the conversion from msl to hae

        arp_ecf = geodetic_to_ecf(arp_llh)
        scp_ecf = geodetic_to_ecf(scp_llh)
        set_arp_position(arp_ecf, override=True)

        set_scp(scp_ecf, (int(mensrb.RP_COL)-1, int(mensrb.RP_ROW)-1), override=False)

        row_unit_ned = numpy.array(
            [float(mensrb.C_R_NC), float(mensrb.C_R_EC), float(mensrb.C_R_DC)], dtype='float64')
        col_unit_ned = numpy.array(
            [float(mensrb.C_AZ_NC), float(mensrb.C_AZ_EC), float(mensrb.C_AZ_DC)], dtype='float64')
        set_uvects(ned_to_ecf(row_unit_ned, scp_ecf, absolute_coords=False),
                   ned_to_ecf(col_unit_ned, scp_ecf, absolute_coords=False))

    def try_MENSRA():
        tre = None if tres is None else tres['MENSRA']
        if tre is None:
            return
        mensra = tre.DATA

        arp_llh = numpy.array(
            [lat_lon_parser(mensra.ACFT_LOC[:10]),
             lat_lon_parser(mensra.ACFT_LOC[10:21]),
             foot*float(mensra.ACFT_ALT)], dtype='float64')
        scp_llh = numpy.array(
            [lat_lon_parser(mensra.CP_LOC[:10]),
             lat_lon_parser(mensra.CP_LOC[10:21]),
             foot*float(mensra.CP_ALT)], dtype='float64')
        # TODO: handle the conversion from msl to hae

        arp_ecf = geodetic_to_ecf(arp_llh)
        scp_ecf = geodetic_to_ecf(scp_llh)
        set_arp_position(arp_ecf, override=True)

        # TODO: is this already zero based?
        set_scp(geodetic_to_ecf(scp_llh), (int(mensra.CCRP_COL), int(mensra.CCRP_ROW)), override=False)

        row_unit_ned = numpy.array(
            [float(mensra.C_R_NC), float(mensra.C_R_EC), float(mensra.C_R_DC)], dtype='float64')
        col_unit_ned = numpy.array(
            [float(mensra.C_AZ_NC), float(mensra.C_AZ_EC), float(mensra.C_AZ_DC)], dtype='float64')
        set_uvects(ned_to_ecf(row_unit_ned, scp_ecf, absolute_coords=False),
                   ned_to_ecf(col_unit_ned, scp_ecf, absolute_coords=False))

    def extract_corners():
        icps = extract_image_corners(img_header)
        if icps is None:
            return
        # TODO: include symmetry transform issue
        set_image_corners(icps, override=False)

    def extract_start():
        # noinspection PyBroadException
        try:
            date_str = img_header.IDATIM
            collect_start = numpy.datetime64(
                _iso_date_format.format(
                    date_str[:4], date_str[4:6], date_str[6:8],
                    date_str[8:10], date_str[10:12], date_str[12:14]), 'us')
        except Exception:
            logger.info('failed extracting start time from IDATIM tre')
            return

        set_collect_start(collect_start, override=False)

    # noinspection PyUnresolvedReferences
    tres = None if img_header.ExtendedHeader.data is None \
        else img_header.ExtendedHeader.data  # type: Union[None, TREList]

    collection_info = get_collection_info()
    image_data = get_image_data()
    the_sicd = SICDType(
        CollectionInfo=collection_info,
        ImageData=image_data)
    # apply the various tres and associated logic
    # NB: this should generally be in order of preference
    try_CMETAA()
    try_AIMIDB()
    try_AIMIDA()
    try_ACFT()
    try_BLOCKA()
    try_MPDSRA()
    try_MENSRA()
    try_MENSRB()
    extract_corners()
    extract_start()
    return the_sicd
Esempio n. 14
0
File: csk.py Progetto: LordHui/sarpy
    def _get_base_sicd(self, h5_dict, band_dict):
        # type: (dict, dict) -> SICDType

        def get_collection_info():  # type: () -> CollectionInfoType
            mode_type = 'STRIPMAP' if h5_dict['Acquisition Mode'] in \
                                      ['HIMAGE', 'PINGPONG', 'WIDEREGION', 'HUGEREGION'] else 'DYNAMIC STRIPMAP'
            return CollectionInfoType(Classification='UNCLASSIFIED',
                                      CollectorName=h5_dict['Satellite ID'],
                                      CoreName=str(h5_dict['Programmed Image ID']),
                                      CollectType='MONOSTATIC',
                                      RadarMode=RadarModeType(ModeID=h5_dict['Multi-Beam ID'],
                                                              ModeType=mode_type))

        def get_image_creation():  # type: () -> ImageCreationType
            from sarpy.__about__ import __version__
            return ImageCreationType(
                DateTime=parse_timestring(h5_dict['Product Generation UTC'], precision='ns'),
                Profile='sarpy {}'.format(__version__))

        def get_grid():  # type: () -> GridType
            if h5_dict['Projection ID'] == 'SLANT RANGE/AZIMUTH':
                image_plane = 'SLANT'
                gr_type = 'RGZERO'
            else:
                image_plane = 'GROUND'
                gr_type = None
            # Row
            row_window_name = h5_dict['Range Focusing Weighting Function'].rstrip().upper()
            row_params = None
            if row_window_name == 'HAMMING':
                row_params = {'COEFFICIENT': '{0:15f}'.format(h5_dict['Range Focusing Weighting Coefficient'])}
            row = DirParamType(Sgn=-1,
                               KCtr=2*center_frequency/speed_of_light,
                               DeltaKCOAPoly=Poly2DType(Coefs=[[0, ], ]),
                               WgtType=WgtTypeType(WindowName=row_window_name, Parameters=row_params))
            # Col
            col_window_name = h5_dict['Azimuth Focusing Weighting Function'].rstrip().upper()
            col_params = None
            if col_window_name == 'HAMMING':
                col_params = {'COEFFICIENT': '{0:15f}'.format(h5_dict['Azimuth Focusing Weighting Coefficient'])}
            col = DirParamType(Sgn=-1,
                               KCtr=0,
                               WgtType=WgtTypeType(WindowName=col_window_name, Parameters=col_params))
            return GridType(ImagePlane=image_plane, Type=gr_type, Row=row, Col=col)

        def get_timeline():  # type: () -> TimelineType
            return TimelineType(CollectStart=collect_start,
                                CollectDuration=duration,
                                IPP=[IPPSetType(index=0, TStart=0, TEnd=0, IPPStart=0, IPPEnd=0), ])  # NB: IPPEnd must be set, but will be replaced

        def get_position():  # type: () -> PositionType
            T = h5_dict['State Vectors Times']  # in seconds relative to ref time
            T += ref_time_offset
            Pos = h5_dict['ECEF Satellite Position']
            Vel = h5_dict['ECEF Satellite Velocity']
            P_x, P_y, P_z = fit_position_xvalidation(T, Pos, Vel, max_degree=8)
            return PositionType(ARPPoly=XYZPolyType(X=P_x, Y=P_y, Z=P_z))

        def get_radar_collection():
            # type: () -> RadarCollectionType
            tx_pols = []
            chan_params = []
            for i, bdname in enumerate(band_dict):
                pol = band_dict[bdname]['Polarisation']
                tx_pols.append(pol[0])
                chan_params.append(ChanParametersType(TxRcvPolarization=self._parse_pol(pol), index=i))
            if len(tx_pols) == 1:
                return RadarCollectionType(RcvChannels=chan_params, TxPolarization=tx_pols[0])
            else:
                return RadarCollectionType(RcvChannels=chan_params,
                                           TxPolarization='SEQUENCE',
                                           TxSequence=[TxStepType(TxPolarization=pol,
                                                                  index=i+1) for i, pol in enumerate(tx_pols)])

        def get_image_formation():
            # type: () -> ImageFormationType
            return ImageFormationType(ImageFormAlgo='RMA',
                                      TStartProc=0,
                                      TEndProc=duration,
                                      STBeamComp='SV',
                                      ImageBeamComp='SV',
                                      AzAutofocus='NO',
                                      RgAutofocus='NO',
                                      RcvChanProc=RcvChanProcType(NumChanProc=1,
                                                                  PRFScaleFactor=1))

        def get_rma():
            # type: () -> RMAType
            inca = INCAType(FreqZero=center_frequency)
            return RMAType(RMAlgoType='OMEGA_K',
                           INCA=inca)

        def get_scpcoa():
            # type: () -> SCPCOAType
            return SCPCOAType(SideOfTrack=h5_dict['Look Side'][0:1].upper())

        # some common use parameters
        center_frequency = h5_dict['Radar Frequency']
        # relative times in csk are wrt some reference time - for sicd they should be relative to start time
        collect_start = parse_timestring(h5_dict['Scene Sensing Start UTC'], precision='ns')
        collect_end = parse_timestring(h5_dict['Scene Sensing Stop UTC'], precision='ns')
        duration = get_seconds(collect_end, collect_start, precision='ns')
        ref_time = parse_timestring(h5_dict['Reference UTC'], precision='ns')
        ref_time_offset = get_seconds(ref_time, collect_start, precision='ns')

        # assemble our pieces
        collection_info = get_collection_info()
        image_creation = get_image_creation()
        grid = get_grid()
        timeline = get_timeline()
        position = get_position()
        radar_collection = get_radar_collection()
        image_formation = get_image_formation()
        rma = get_rma()
        scpcoa = get_scpcoa()

        return SICDType(CollectionInfo=collection_info,
                        ImageCreation=image_creation,
                        Grid=grid,
                        Timeline=timeline,
                        Position=position,
                        RadarCollection=radar_collection,
                        ImageFormation=image_formation,
                        RMA=rma,
                        SCPCOA=scpcoa)
Esempio n. 15
0
    def get_sicd(self):
        """
        Gets the SICD structure.

        Returns
        -------
        Tuple[SICDType, tuple, tuple]
            The sicd structure, the data size argument, and the symmetry argument.
        """
        def get_collection_info():
            # type: () -> CollectionInfoType
            return CollectionInfoType(
                CollectorName=_stringify(hf['satellite_name'][()]),
                CoreName=_stringify(hf['product_name'][()]),
                CollectType='MONOSTATIC',
                Classification='UNCLASSIFIED',
                RadarMode=RadarModeType(
                    ModeType=_stringify(hf['acquisition_mode'][()]).upper(),
                    ModeID=_stringify(hf['product_type'][()])))

        def get_image_creation():
            # type: () -> ImageCreationType
            from sarpy.__about__ import __version__
            return ImageCreationType(
                Application='ICEYE_P_{}'.format(hf['processor_version'][()]),
                DateTime=_parse_time(hf['processing_time'][()]),
                Site='Unknown',
                Profile='sarpy {}'.format(__version__))

        def get_image_data():
            # type: () -> ImageDataType

            samp_prec = _stringify(hf['sample_precision'][()])
            if samp_prec.upper() == 'INT16':
                pixel_type = 'RE16I_IM16I'
            elif samp_prec.upper() == 'FLOAT32':
                pixel_type = 'RE32F_IM32F'
            else:
                raise ValueError(
                    'Got unhandled sample precision {}'.format(samp_prec))

            num_rows = int_func(number_of_range_samples)
            num_cols = int_func(number_of_azimuth_samples)
            scp_row = int_func(coord_center[0]) - 1
            scp_col = int_func(coord_center[1]) - 1
            if 0 < scp_col < num_rows - 1:
                if look_side == 'left':
                    scp_col = num_cols - scp_col - 1
            else:
                # early ICEYE processing bug led to nonsensical SCP
                scp_col = int_func(num_cols / 2.0)

            return ImageDataType(PixelType=pixel_type,
                                 NumRows=num_rows,
                                 NumCols=num_cols,
                                 FirstRow=0,
                                 FirstCol=0,
                                 FullImage=(num_rows, num_cols),
                                 SCPPixel=(scp_row, scp_col))

        def get_geo_data():
            # type: () -> GeoDataType
            # NB: the remainder will be derived.
            return GeoDataType(SCP=SCPType(
                LLH=[coord_center[2], coord_center[3], avg_scene_height]))

        def get_timeline():
            # type: () -> TimelineType

            acq_prf = hf['acquisition_prf'][()]
            return TimelineType(CollectStart=start_time,
                                CollectDuration=duration,
                                IPP=[
                                    IPPSetType(index=0,
                                               TStart=0,
                                               TEnd=duration,
                                               IPPStart=0,
                                               IPPEnd=int_func(
                                                   round(acq_prf * duration)),
                                               IPPPoly=[0, acq_prf]),
                                ])

        def get_position():
            # type: () -> PositionType
            # fetch the state information
            times_str = hf['state_vector_time_utc'][:, 0]
            times = numpy.zeros((times_str.shape[0], ), dtype='float64')
            positions = numpy.zeros((times.size, 3), dtype='float64')
            velocities = numpy.zeros((times.size, 3), dtype='float64')
            for i, entry in enumerate(times_str):
                times[i] = get_seconds(_parse_time(entry),
                                       start_time,
                                       precision='us')
            positions[:, 0], positions[:, 1], positions[:, 2] = hf[
                'posX'][:], hf['posY'][:], hf['posZ'][:]
            velocities[:, 0], velocities[:, 1], velocities[:, 2] = hf[
                'velX'][:], hf['velY'][:], hf['velZ'][:]
            # fir the the position polynomial using cross validation
            P_x, P_y, P_z = fit_position_xvalidation(times,
                                                     positions,
                                                     velocities,
                                                     max_degree=8)
            return PositionType(ARPPoly=XYZPolyType(X=P_x, Y=P_y, Z=P_z))

        def get_radar_collection():
            # type : () -> RadarCollection
            return RadarCollectionType(
                TxPolarization=tx_pol,
                TxFrequency=TxFrequencyType(Min=min_freq, Max=max_freq),
                Waveform=[
                    WaveformParametersType(
                        TxFreqStart=min_freq,
                        TxRFBandwidth=tx_bandwidth,
                        TxPulseLength=hf['chirp_duration'][()],
                        ADCSampleRate=hf['range_sampling_rate'][()],
                        RcvDemodType='CHIRP',
                        RcvFMRate=0,
                        index=1)
                ],
                RcvChannels=[
                    ChanParametersType(TxRcvPolarization=polarization, index=1)
                ])

        def get_image_formation():
            # type: () -> ImageFormationType
            return ImageFormationType(
                TxRcvPolarizationProc=polarization,
                ImageFormAlgo='RMA',
                TStartProc=0,
                TEndProc=duration,
                TxFrequencyProc=TxFrequencyProcType(MinProc=min_freq,
                                                    MaxProc=max_freq),
                STBeamComp='NO',
                ImageBeamComp='SV',
                AzAutofocus='NO',
                RgAutofocus='NO',
                RcvChanProc=RcvChanProcType(NumChanProc=1,
                                            PRFScaleFactor=1,
                                            ChanIndices=[
                                                1,
                                            ]),
            )

        def get_radiometric():
            # type: () -> RadiometricType
            return RadiometricType(BetaZeroSFPoly=[
                [
                    float(hf['calibration_factor'][()]),
                ],
            ])

        def calculate_drate_sf_poly():
            r_ca_coeffs = numpy.array([r_ca_scp, 1], dtype='float64')
            dop_rate_coeffs = hf['doppler_rate_coeffs'][:]
            # Prior to ICEYE 1.14 processor, absolute value of Doppler rate was
            # provided, not true Doppler rate. Doppler rate should always be negative
            if dop_rate_coeffs[0] > 0:
                dop_rate_coeffs *= -1
            dop_rate_poly = Poly1DType(Coefs=dop_rate_coeffs)
            # now adjust to create
            t_drate_ca_poly = dop_rate_poly.shift(t_0=zd_ref_time -
                                                  rg_time_scp,
                                                  alpha=2 / speed_of_light,
                                                  return_poly=False)
            return t_drate_ca_poly, -polynomial.polymul(
                t_drate_ca_poly, r_ca_coeffs) * speed_of_light / (
                    2 * center_freq * vm_ca_sq)

        def calculate_doppler_polys():
            # extract doppler centroid coefficients
            dc_estimate_coeffs = hf['dc_estimate_coeffs'][:]
            dc_time_str = hf['dc_estimate_time_utc'][:, 0]
            dc_zd_times = numpy.zeros((dc_time_str.shape[0], ),
                                      dtype='float64')
            for i, entry in enumerate(dc_time_str):
                dc_zd_times[i] = get_seconds(_parse_time(entry),
                                             start_time,
                                             precision='us')
            # create a sampled doppler centroid
            samples = 49  # copied from corresponding matlab, we just need enough for appropriate refitting
            # create doppler time samples
            diff_time_rg = first_pixel_time - zd_ref_time + \
                           numpy.linspace(0, number_of_range_samples/range_sampling_rate, samples)
            # doppler centroid samples definition
            dc_sample_array = numpy.zeros((samples, dc_zd_times.size),
                                          dtype='float64')
            for i, coeffs in enumerate(dc_estimate_coeffs):
                dc_sample_array[:,
                                i] = polynomial.polyval(diff_time_rg, coeffs)
            # create arrays for range/azimuth from scp in meters
            azimuth_scp_m, range_scp_m = numpy.meshgrid(
                col_ss * (dc_zd_times - zd_time_scp) / ss_zd_s,
                (diff_time_rg + zd_ref_time - rg_time_scp) * speed_of_light /
                2)

            # fit the doppler centroid sample array
            x_order = min(3, range_scp_m.shape[0] - 1)
            y_order = min(3, range_scp_m.shape[1] - 1)

            t_dop_centroid_poly, residuals, rank, sing_values = two_dim_poly_fit(
                range_scp_m,
                azimuth_scp_m,
                dc_sample_array,
                x_order=x_order,
                y_order=y_order,
                x_scale=1e-3,
                y_scale=1e-3,
                rcond=1e-40)
            logging.info(
                'The dop_centroid_poly fit details:\nroot mean square '
                'residuals = {}\nrank = {}\nsingular values = {}'.format(
                    residuals, rank, sing_values))

            # define and fit the time coa array
            doppler_rate_sampled = polynomial.polyval(azimuth_scp_m,
                                                      drate_ca_poly)
            time_coa = dc_zd_times + dc_sample_array / doppler_rate_sampled
            t_time_coa_poly, residuals, rank, sing_values = two_dim_poly_fit(
                range_scp_m,
                azimuth_scp_m,
                time_coa,
                x_order=x_order,
                y_order=y_order,
                x_scale=1e-3,
                y_scale=1e-3,
                rcond=1e-40)
            logging.info(
                'The time_coa_poly fit details:\nroot mean square '
                'residuals = {}\nrank = {}\nsingular values = {}'.format(
                    residuals, rank, sing_values))
            return t_dop_centroid_poly, t_time_coa_poly

        def get_rma():
            # type: () -> RMAType
            dop_centroid_poly = Poly2DType(Coefs=dop_centroid_poly_coeffs)
            dop_centroid_coa = True
            if collect_info.RadarMode.ModeType == 'SPOTLIGHT':
                dop_centroid_poly = None
                dop_centroid_coa = None
            # NB: DRateSFPoly is defined as a function of only range - reshape appropriately
            inca = INCAType(
                R_CA_SCP=r_ca_scp,
                FreqZero=center_freq,
                DRateSFPoly=Poly2DType(
                    Coefs=numpy.reshape(drate_sf_poly_coefs, (-1, 1))),
                DopCentroidPoly=dop_centroid_poly,
                DopCentroidCOA=dop_centroid_coa,
                TimeCAPoly=Poly1DType(Coefs=time_ca_poly_coeffs))
            return RMAType(RMAlgoType='OMEGA_K', INCA=inca)

        def get_grid():
            # type: () -> GridType
            time_coa_poly = Poly2DType(Coefs=time_coa_poly_coeffs)
            if collect_info.RadarMode.ModeType == 'SPOTLIGHT':
                time_coa_poly = Poly2DType(Coefs=[
                    [
                        float(time_coa_poly_coeffs[0, 0]),
                    ],
                ])

            row_win = _stringify(hf['window_function_range'][()])
            if row_win == 'NONE':
                row_win = 'UNIFORM'
            row = DirParamType(SS=row_ss,
                               Sgn=-1,
                               KCtr=2 * center_freq / speed_of_light,
                               ImpRespBW=2 * tx_bandwidth / speed_of_light,
                               DeltaKCOAPoly=Poly2DType(Coefs=[[
                                   0,
                               ]]),
                               WgtType=WgtTypeType(WindowName=row_win))
            col_win = _stringify(hf['window_function_azimuth'][()])
            if col_win == 'NONE':
                col_win = 'UNIFORM'
            col = DirParamType(
                SS=col_ss,
                Sgn=-1,
                KCtr=0,
                ImpRespBW=col_imp_res_bw,
                WgtType=WgtTypeType(WindowName=col_win),
                DeltaKCOAPoly=Poly2DType(Coefs=dop_centroid_poly_coeffs *
                                         ss_zd_s / col_ss))
            return GridType(Type='RGZERO',
                            ImagePlane='SLANT',
                            TimeCOAPoly=time_coa_poly,
                            Row=row,
                            Col=col)

        def correct_scp():
            scp_pixel = sicd.ImageData.SCPPixel.get_array()
            scp_ecf = sicd.project_image_to_ground(scp_pixel,
                                                   projection_type='HAE')
            sicd.update_scp(scp_ecf, coord_system='ECF')

        with h5py.File(self._file_name, 'r') as hf:
            # some common use variables
            look_side = _stringify(hf['look_side'][()])
            coord_center = hf['coord_center'][:]
            avg_scene_height = float(hf['avg_scene_height'][()])
            start_time = _parse_time(hf['acquisition_start_utc'][()])
            end_time = _parse_time(hf['acquisition_end_utc'][()])
            duration = get_seconds(end_time, start_time, precision='us')

            center_freq = float(hf['carrier_frequency'][()])
            tx_bandwidth = float(hf['chirp_bandwidth'][()])
            min_freq = center_freq - 0.5 * tx_bandwidth
            max_freq = center_freq + 0.5 * tx_bandwidth

            pol_temp = _stringify(hf['polarization'][()])
            tx_pol = pol_temp[0]
            rcv_pol = pol_temp[1]
            polarization = tx_pol + ':' + rcv_pol

            first_pixel_time = float(hf['first_pixel_time'][()])
            near_range = first_pixel_time * speed_of_light / 2
            number_of_range_samples = float(hf['number_of_range_samples'][()])
            number_of_azimuth_samples = float(
                hf['number_of_azimuth_samples'][()])
            range_sampling_rate = float(hf['range_sampling_rate'][()])
            row_ss = speed_of_light / (2 * range_sampling_rate)

            # define the sicd elements
            collect_info = get_collection_info()
            image_creation = get_image_creation()
            image_data = get_image_data()
            geo_data = get_geo_data()
            timeline = get_timeline()
            position = get_position()
            radar_collection = get_radar_collection()
            image_formation = get_image_formation()
            radiometric = get_radiometric()

            # calculate some zero doppler parameters
            ss_zd_s = float(hf['azimuth_time_interval'][()])
            if look_side == 'left':
                ss_zd_s *= -1
                zero_doppler_left = _parse_time(hf['zerodoppler_end_utc'][()])
            else:
                zero_doppler_left = _parse_time(
                    hf['zerodoppler_start_utc'][()])
            dop_bw = hf['total_processed_bandwidth_azimuth'][()]
            zd_time_scp = get_seconds(zero_doppler_left, start_time, precision='us') + \
                          image_data.SCPPixel.Col*ss_zd_s
            zd_ref_time = first_pixel_time + number_of_range_samples / (
                2 * range_sampling_rate)
            vel_scp = position.ARPPoly.derivative_eval(zd_time_scp,
                                                       der_order=1)
            vm_ca_sq = numpy.sum(vel_scp * vel_scp)
            rg_time_scp = first_pixel_time + image_data.SCPPixel.Row / range_sampling_rate
            r_ca_scp = rg_time_scp * speed_of_light / 2
            # calculate the doppler rate sf polynomial
            drate_ca_poly, drate_sf_poly_coefs = calculate_drate_sf_poly()

            # calculate some doppler dependent grid parameters
            col_ss = float(
                numpy.sqrt(vm_ca_sq) * abs(ss_zd_s) * drate_sf_poly_coefs[0])
            col_imp_res_bw = dop_bw * abs(ss_zd_s) / col_ss
            time_ca_poly_coeffs = [zd_time_scp, ss_zd_s / col_ss]

            # calculate the doppler polynomials
            dop_centroid_poly_coeffs, time_coa_poly_coeffs = calculate_doppler_polys(
            )

            # finish definition of sicd elements
            rma = get_rma()
            grid = get_grid()
            sicd = SICDType(CollectionInfo=collect_info,
                            ImageCreation=image_creation,
                            ImageData=image_data,
                            GeoData=geo_data,
                            Timeline=timeline,
                            Position=position,
                            RadarCollection=radar_collection,
                            ImageFormation=image_formation,
                            Radiometric=radiometric,
                            RMA=rma,
                            Grid=grid)
        # adjust the scp location
        correct_scp()
        # derive sicd fields
        sicd.derive()
        # TODO: RNIIRS?
        data_size = (image_data.NumCols, image_data.NumRows)
        symmetry = (True, False,
                    True) if look_side == 'left' else (False, False, True)
        return sicd, data_size, symmetry
Esempio n. 16
0
    def _get_base_sicd(self, h5_dict, band_dict):
        # type: (dict, dict) -> SICDType

        def get_collection_info():  # type: () -> (dict, CollectionInfoType)
            acq_mode = h5_dict['Acquisition Mode'].upper()
            if self.mission_id == 'CSK':
                if acq_mode in ['HIMAGE', 'PINGPONG']:
                    mode_type = 'STRIPMAP'
                elif acq_mode in ['WIDEREGION', 'HUGEREGION']:
                    # scansar, processed as stripmap
                    mode_type = 'STRIPMAP'
                elif acq_mode in ['ENHANCED SPOTLIGHT', 'SMART']:
                    mode_type = 'DYNAMIC STRIPMAP'
                else:
                    logging.warning(
                        'Got unexpected acquisition mode {}'.format(acq_mode))
                    mode_type = 'DYNAMIC STRIPMAP'
            elif self.mission_id == 'KMPS':
                if acq_mode in ['STANDARD', 'ENHANCED STANDARD']:
                    mode_type = 'STRIPMAP'
                elif acq_mode in ['WIDE SWATH', 'ENHANCED WIDE SWATH']:
                    # scansar, processed as stripmap
                    mode_type = 'STRIPMAP'
                elif acq_mode in [
                        'HIGH RESOLUTION', 'ENHANCED HIGH RESOLUTION',
                        'ULTRA HIGH RESOLUTION'
                ]:
                    # "spotlight"
                    mode_type = 'DYNAMIC STRIPMAP'
                else:
                    logging.warning(
                        'Got unexpected acquisition mode {}'.format(acq_mode))
                    mode_type = 'DYNAMIC STRIPMAP'
            elif self.mission_id == 'CSG':
                if acq_mode.startswith('SPOTLIGHT'):
                    mode_type = 'DYNAMIC STRIPMAP'
                elif acq_mode in ['STRIPMAP', 'QUADPOL']:
                    mode_type = "STRIPMAP"
                else:
                    logging.warning(
                        'Got unhandled acquisition mode {}, setting to DYNAMIC STRIPMAP'
                        .format(acq_mode))
                    mode_type = 'DYNAMIC STRIPMAP'
            else:
                raise ValueError('Unhandled mission id {}'.format(
                    self._mission_id))

            start_time_dt = collect_start.astype('datetime64[s]').astype(
                datetime)
            date_str = start_time_dt.strftime('%d%b%y').upper()
            time_str = start_time_dt.strftime('%H%M%S') + 'Z'
            core_name = '{}_{}_{}'.format(date_str, self.mission_id, time_str)
            collect_info = CollectionInfoType(
                Classification='UNCLASSIFIED',
                CollectorName=h5_dict['Satellite ID'],
                CoreName=core_name,
                CollectType='MONOSTATIC',
                RadarMode=RadarModeType(ModeID=h5_dict['Multi-Beam ID'],
                                        ModeType=mode_type))
            return collect_info

        def get_image_creation():  # type: () -> ImageCreationType
            from sarpy.__about__ import __version__
            return ImageCreationType(
                DateTime=parse_timestring(h5_dict['Product Generation UTC'],
                                          precision='ns'),
                Site=h5_dict['Processing Centre'],
                Application='L0: `{}`, L1: `{}`'.format(
                    h5_dict.get('L0 Software Version', 'NONE'),
                    h5_dict.get('L1A Software Version', 'NONE')),
                Profile='sarpy {}'.format(__version__))

        def get_grid():  # type: () -> GridType
            def get_wgt_type(weight_name, coefficient, direction):
                if weight_name == 'GENERAL_COSINE':
                    # probably only for kompsat?
                    weight_name = 'HAMMING'
                    coefficient = 1 - coefficient
                if coefficient is None:
                    params = None
                else:
                    params = {'COEFFICIENT': '{0:0.16G}'.format(coefficient)}
                out = WgtTypeType(WindowName=weight_name, Parameters=params)
                if weight_name != 'HAMMING':
                    logging.warning(
                        'Got unexpected weight scheme {} for {}. The weighting will '
                        'not be properly populated.'.format(
                            weight_name, direction))
                return out

            if h5_dict['Projection ID'] == 'SLANT RANGE/AZIMUTH':
                image_plane = 'SLANT'
                gr_type = 'RGZERO'
            else:
                image_plane = 'GROUND'
                gr_type = None
            # Row
            row_window_name = h5_dict[
                'Range Focusing Weighting Function'].rstrip().upper()
            row_coefficient = h5_dict.get(
                'Range Focusing Weighting Coefficient', None)
            row_weight = get_wgt_type(row_window_name, row_coefficient, 'Row')
            row = DirParamType(Sgn=-1,
                               KCtr=2 * center_frequency / speed_of_light,
                               DeltaKCOAPoly=Poly2DType(Coefs=[
                                   [
                                       0,
                                   ],
                               ]),
                               WgtType=row_weight)
            # Col
            col_window_name = h5_dict[
                'Azimuth Focusing Weighting Function'].rstrip().upper()
            col_coefficient = h5_dict.get(
                'Azimuth Focusing Weighting Coefficient', None)
            col_weight = get_wgt_type(col_window_name, col_coefficient, 'Col')
            col = DirParamType(Sgn=-1, KCtr=0, WgtType=col_weight)
            return GridType(ImagePlane=image_plane,
                            Type=gr_type,
                            Row=row,
                            Col=col)

        def get_timeline():  # type: () -> TimelineType
            # NB: IPPEnd must be set, but will be replaced
            return TimelineType(CollectStart=collect_start,
                                CollectDuration=duration,
                                IPP=[
                                    IPPSetType(index=0,
                                               TStart=0,
                                               TEnd=0,
                                               IPPStart=0,
                                               IPPEnd=0),
                                ])

        def get_position():  # type: () -> PositionType
            T = h5_dict[
                'State Vectors Times']  # in seconds relative to ref time
            T += ref_time_offset
            Pos = h5_dict['ECEF Satellite Position']
            Vel = h5_dict['ECEF Satellite Velocity']
            P_x, P_y, P_z = fit_position_xvalidation(T, Pos, Vel, max_degree=8)
            return PositionType(ARPPoly=XYZPolyType(X=P_x, Y=P_y, Z=P_z))

        def get_radar_collection():
            # type: () -> RadarCollectionType
            tx_pols = []
            chan_params = []
            for i, bdname in enumerate(band_dict):
                if 'Polarisation' in band_dict[bdname]:
                    pol = band_dict[bdname]['Polarisation']
                elif 'Polarization' in h5_dict:
                    pol = h5_dict['Polarization']
                else:
                    raise ValueError(
                        'Failed finding polarization for file {}\nmission id {}'
                        .format(self._file_name, self._mission_id))
                tx_pols.append(pol[0])
                chan_params.append(
                    ChanParametersType(TxRcvPolarization=self._parse_pol(pol),
                                       index=i))
            if len(tx_pols) == 1:
                return RadarCollectionType(RcvChannels=chan_params,
                                           TxPolarization=tx_pols[0])
            else:
                return RadarCollectionType(RcvChannels=chan_params,
                                           TxPolarization='SEQUENCE',
                                           TxSequence=[
                                               TxStepType(TxPolarization=pol,
                                                          index=i + 1)
                                               for i, pol in enumerate(tx_pols)
                                           ])

        def get_image_formation():
            # type: () -> ImageFormationType
            return ImageFormationType(ImageFormAlgo='RMA',
                                      TStartProc=0,
                                      TEndProc=duration,
                                      STBeamComp='NO',
                                      ImageBeamComp='SV',
                                      AzAutofocus='NO',
                                      RgAutofocus='NO',
                                      RcvChanProc=RcvChanProcType(
                                          NumChanProc=1, PRFScaleFactor=1))

        def get_rma():
            # type: () -> RMAType
            inca = INCAType(FreqZero=center_frequency)
            return RMAType(RMAlgoType='OMEGA_K', INCA=inca)

        def get_scpcoa():
            # type: () -> SCPCOAType
            return SCPCOAType(SideOfTrack=h5_dict['Look Side'][0:1].upper())

        # some common use parameters
        center_frequency = h5_dict['Radar Frequency']
        # relative times in csk are wrt some reference time - for sicd they should be relative to start time
        collect_start = parse_timestring(h5_dict['Scene Sensing Start UTC'],
                                         precision='ns')
        collect_end = parse_timestring(h5_dict['Scene Sensing Stop UTC'],
                                       precision='ns')
        duration = get_seconds(collect_end, collect_start, precision='ns')
        ref_time = parse_timestring(h5_dict['Reference UTC'], precision='ns')
        ref_time_offset = get_seconds(ref_time, collect_start, precision='ns')

        # assemble our pieces
        collection_info = get_collection_info()
        image_creation = get_image_creation()
        grid = get_grid()
        timeline = get_timeline()
        position = get_position()
        radar_collection = get_radar_collection()
        image_formation = get_image_formation()
        rma = get_rma()
        scpcoa = get_scpcoa()
        sicd = SICDType(CollectionInfo=collection_info,
                        ImageCreation=image_creation,
                        Grid=grid,
                        Timeline=timeline,
                        Position=position,
                        RadarCollection=radar_collection,
                        ImageFormation=image_formation,
                        RMA=rma,
                        SCPCOA=scpcoa)
        return sicd
Esempio n. 17
0
    def _get_base_sicd(self, hf):
        """
        Defines the base SICD object, to be refined with further details.

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

        def get_collection_info():
            # type: () -> CollectionInfoType
            gp = hf['/science/LSAR/identification']
            return CollectionInfoType(
                CollectorName=_stringify(hf.attrs['mission_name']),
                CoreName='{0:07d}_{1:s}'.format(gp['absoluteOrbitNumber'][()],
                                                _stringify(gp['trackNumber'][()])),
                CollectType='MONOSTATIC',
                Classification='UNCLASSIFIED',
                RadarMode=RadarModeType(ModeType='STRIPMAP'))

        def get_image_creation():
            # type: () -> ImageCreationType
            application = 'ISCE'
            # noinspection PyBroadException
            try:
                application = '{} {}'.format(
                    application,
                    _stringify(hf['/science/LSAR/SLC/metadata/processingInformation/algorithms/ISCEVersion'][()]))
            except:
                pass

            from sarpy.__about__ import __version__
            # TODO: DateTime?
            return ImageCreationType(
                Application=application,
                Site='Unknown',
                Profile='sarpy {}'.format(__version__))

        def get_geo_data():
            # type: () -> GeoDataType
            # seeds a rough SCP for projection usage
            poly_str = _stringify(hf['/science/LSAR/identification/boundingPolygon'][()])
            beg_str = 'POLYGON (('
            if not poly_str.startswith(beg_str):
                raise ValueError('Unexpected polygon string {}'.format(poly_str))
            parts = poly_str[len(beg_str):-2].strip().split(',')
            if len(parts) != 5:
                raise ValueError('Unexpected polygon string parts {}'.format(parts))
            lats_lons = numpy.zeros((4, 2), dtype=numpy.float64)
            for i, part in enumerate(parts[:-1]):
                spart = part.strip().split()
                if len(spart) != 2:
                    raise ValueError('Unexpected polygon string parts {}'.format(parts))
                lats_lons[i, :] = float(spart[1]), float(spart[0])

            llh = numpy.zeros((3, ), dtype=numpy.float64)
            llh[0:2] = numpy.mean(lats_lons, axis=0)
            llh[2] = numpy.mean(hf['/science/LSAR/SLC/metadata/processingInformation/parameters/referenceTerrainHeight'][:])
            return GeoDataType(SCP=SCPType(LLH=llh))

        def get_grid():
            # type: () -> GridType

            # TODO: Future Change Required - JPL states that uniform weighting in data simulated
            #  from UAVSAR is a placeholder, not an accurate description of the data.
            #  At this point, it is not clear what the final weighting description for NISAR
            #  will be.

            gp = hf['/science/LSAR/SLC/metadata/processingInformation/parameters']
            row_wgt = gp['rangeChirpWeighting'][:]
            win_name = 'UNIFORM' if numpy.all(row_wgt == row_wgt[0]) else 'UNKNOWN'
            row = DirParamType(
                Sgn=-1,
                DeltaKCOAPoly=[[0,]],
                WgtFunct=numpy.cast[numpy.float64](row_wgt),
                WgtType=WgtTypeType(WindowName=win_name))

            col_wgt = gp['azimuthChirpWeighting'][:]
            win_name = 'UNIFORM' if numpy.all(col_wgt == col_wgt[0]) else 'UNKNOWN'
            col = DirParamType(
                Sgn=-1,
                KCtr=0,
                WgtFunct=numpy.cast[numpy.float64](col_wgt),
                WgtType=WgtTypeType(WindowName=win_name))

            return GridType(ImagePlane='SLANT', Type='RGZERO', Row=row, Col=col)

        def get_timeline():
            # type: () -> TimelineType
            # NB: IPPEnd must be set, but will be replaced
            return TimelineType(
                CollectStart=collect_start,
                CollectDuration=duration,
                IPP=[IPPSetType(index=0, TStart=0, TEnd=duration, IPPStart=0, IPPEnd=0), ])

        def get_position():
            # type: () -> PositionType
            gp = hf['/science/LSAR/SLC/metadata/orbit']
            ref_time = _get_ref_time(gp['time'].attrs['units'])
            T = gp['time'][:] + get_seconds(ref_time, collect_start, precision='ns')
            Pos = gp['position'][:]
            Vel = gp['velocity'][:]
            P_x, P_y, P_z = fit_position_xvalidation(T, Pos, Vel, max_degree=8)
            return PositionType(ARPPoly=XYZPolyType(X=P_x, Y=P_y, Z=P_z))

        def get_scpcoa():
            # type: () -> SCPCOAType
            # remaining fields set later
            sot = _stringify(hf['/science/LSAR/identification/lookDirection'][()])[0].upper()
            return SCPCOAType(SideOfTrack=sot)

        def get_image_formation():
            # type: () -> ImageFormationType
            return ImageFormationType(
                ImageFormAlgo='RMA',
                TStartProc=0,
                TEndProc=duration,
                STBeamComp='NO',
                ImageBeamComp='SV',
                AzAutofocus='NO',
                RgAutofocus='NO',
                RcvChanProc=RcvChanProcType(NumChanProc=1, PRFScaleFactor=1))

        def get_rma():
            # type: () -> RMAType
            return RMAType(RMAlgoType='OMEGA_K', INCA=INCAType(DopCentroidCOA=True))

        collect_start, collect_end, duration = self._get_collection_times(hf)
        collection_info = get_collection_info()
        image_creation = get_image_creation()
        geo_data = get_geo_data()
        grid = get_grid()
        timeline = get_timeline()
        position = get_position()
        scpcoa = get_scpcoa()
        image_formation = get_image_formation()
        rma = get_rma()

        return SICDType(
            CollectionInfo=collection_info,
            ImageCreation=image_creation,
            GeoData=geo_data,
            Grid=grid,
            Timeline=timeline,
            Position=position,
            SCPCOA=scpcoa,
            ImageFormation=image_formation,
            RMA=rma)
Esempio n. 18
0
    def _get_freq_specific_sicd(
            gp: h5pyGroup,
            base_sicd: SICDType) -> Tuple[SICDType, List[str], List[str], float]:
        """
        Gets the frequency specific sicd.

        Parameters
        ----------
        gp : h5py.Group
        base_sicd : SICDType

        Returns
        -------
        sicd: SICDType
        pol_names : numpy.ndarray
        pols : List[str]
        center_frequency : float
        """

        def update_grid() -> None:
            row_imp_resp_bw = 2*gp['processedRangeBandwidth'][()]/speed_of_light
            t_sicd.Grid.Row.SS = gp['slantRangeSpacing'][()]
            t_sicd.Grid.Row.ImpRespBW = row_imp_resp_bw
            t_sicd.Grid.Row.DeltaK1 = -0.5*row_imp_resp_bw
            t_sicd.Grid.Row.DeltaK2 = -t_sicd.Grid.Row.DeltaK1

        def update_timeline() -> None:
            prf = gp['nominalAcquisitionPRF'][()]
            t_sicd.Timeline.IPP[0].IPPEnd = prf*t_sicd.Timeline.CollectDuration
            t_sicd.Timeline.IPP[0].IPPPoly = [0, prf]

        def define_radar_collection() -> List[str]:
            tx_rcv_pol_t = []
            tx_pol = []
            for entry in pols:
                tx_rcv_pol_t.append('{}:{}'.format(entry[0], entry[1]))
                if entry[0] not in tx_pol:
                    tx_pol.append(entry[0])
            center_freq_t = gp['acquiredCenterFrequency'][()]
            bw = gp['acquiredRangeBandwidth'][()]
            tx_freq = (center_freq_t - 0.5*bw, center_freq_t + 0.5*bw)
            rcv_chans = [ChanParametersType(TxRcvPolarization=pol) for pol in tx_rcv_pol_t]
            if len(tx_pol) == 1:
                tx_sequence = None
                tx_pol = tx_pol[0]
            else:
                tx_sequence = [TxStepType(WFIndex=j+1, TxPolarization=pol) for j, pol in enumerate(tx_pol)]
                tx_pol = 'SEQUENCE'

            t_sicd.RadarCollection = RadarCollectionType(
                TxFrequency=tx_freq,
                RcvChannels=rcv_chans,
                TxPolarization=tx_pol,
                TxSequence=tx_sequence)
            return tx_rcv_pol_t

        def update_image_formation() -> float:
            center_freq_t = gp['processedCenterFrequency'][()]
            bw = gp['processedRangeBandwidth'][()]
            t_sicd.ImageFormation.TxFrequencyProc = (center_freq_t - 0.5*bw, center_freq_t + 0.5*bw)
            return center_freq_t

        pols = _get_string_list(gp['listOfPolarizations'][:])
        t_sicd = base_sicd.copy()

        update_grid()
        update_timeline()
        tx_rcv_pol = define_radar_collection()
        center_freq = update_image_formation()
        return t_sicd, pols, tx_rcv_pol, center_freq
Esempio n. 19
0
    def _get_pol_specific_sicd(
            hf: h5pyFile,
            ds: h5pyDataset,
            base_sicd: SICDType,
            pol_name: str,
            freq_name: str,
            j: int,
            pol: str,
            r_ca_sampled: numpy.ndarray,
            zd_time: numpy.ndarray,
            grid_zd_time: numpy.ndarray,
            grid_r: numpy.ndarray,
            doprate_sampled: numpy.ndarray,
            dopcentroid_sampled: numpy.ndarray,
            center_freq: float,
            ss_az_s: float,
            dop_bw: float,
            beta0,
            gamma0,
            sigma0) -> Tuple[SICDType, Tuple[int, ...], numpy.dtype]:
        """
        Gets the frequency/polarization specific sicd.

        Parameters
        ----------
        hf : h5py.File
        ds : h5py.Dataset
        base_sicd : SICDType
        pol_name : str
        freq_name : str
        j : int
        pol : str
        r_ca_sampled : numpy.ndarray
        zd_time : numpy.ndarray
        grid_zd_time : numpy.ndarray
        grid_r : numpy.ndarray
        doprate_sampled : numpy.ndarray
        dopcentroid_sampled : numpy.ndarray
        center_freq : float
        ss_az_s : float
        dop_bw : float

        Returns
        -------
        sicd: SICDType
        shape : Tuple[int, ...]
        numpy.dtype
        """

        def define_image_data() -> None:
            if dtype.name in ('float32', 'complex64'):
                pixel_type = 'RE32F_IM32F'
            elif dtype.name == 'int16':
                pixel_type = 'RE16I_IM16I'
            else:
                raise ValueError('Got unhandled dtype {}'.format(dtype))
            t_sicd.ImageData = ImageDataType(
                PixelType=pixel_type,
                NumRows=shape[1],
                NumCols=shape[0],
                FirstRow=0,
                FirstCol=0,
                SCPPixel=[0.5*shape[0], 0.5*shape[1]],
                FullImage=[shape[1], shape[0]])

        def update_image_formation() -> None:
            t_sicd.ImageFormation.RcvChanProc.ChanIndices = [j, ]
            t_sicd.ImageFormation.TxRcvPolarizationProc = pol

        def update_inca_and_grid() -> Tuple[numpy.ndarray, numpy.ndarray]:
            t_sicd.RMA.INCA.R_CA_SCP = r_ca_sampled[t_sicd.ImageData.SCPPixel.Row]
            scp_ca_time = zd_time[t_sicd.ImageData.SCPPixel.Col]

            # compute DRateSFPoly
            # velocity at scp ca time
            vel_ca = t_sicd.Position.ARPPoly.derivative_eval(scp_ca_time, der_order=1)
            # squared magnitude
            vm_ca_sq = numpy.sum(vel_ca*vel_ca)
            # polynomial coefficient for function representing range as a function of range distance from SCP
            r_ca_poly = numpy.array([t_sicd.RMA.INCA.R_CA_SCP, 1], dtype=numpy.float64)
            # closest Doppler rate polynomial to SCP
            min_ind = numpy.argmin(numpy.absolute(grid_zd_time - scp_ca_time))
            # define range coordinate grid
            coords_rg_m = grid_r - t_sicd.RMA.INCA.R_CA_SCP
            # determine dop_rate_poly coordinates
            dop_rate_poly = polynomial.polyfit(coords_rg_m, -doprate_sampled[min_ind, :], 4)  # why fourth order?
            t_sicd.RMA.INCA.FreqZero = center_freq
            t_sicd.RMA.INCA.DRateSFPoly = Poly2DType(Coefs=numpy.reshape(
                -numpy.convolve(dop_rate_poly, r_ca_poly)*speed_of_light/(2*center_freq*vm_ca_sq), (-1, 1)))

            # update Grid.Col parameters
            t_sicd.Grid.Col.SS = numpy.sqrt(vm_ca_sq)*abs(ss_az_s)*t_sicd.RMA.INCA.DRateSFPoly.Coefs[0, 0]
            t_sicd.Grid.Col.ImpRespBW = min(abs(dop_bw*ss_az_s), 1)/t_sicd.Grid.Col.SS
            t_sicd.RMA.INCA.TimeCAPoly = [scp_ca_time, ss_az_s/t_sicd.Grid.Col.SS]

            #TimeCOAPoly/DopCentroidPoly/DeltaKCOAPoly
            coords_az_m = (grid_zd_time - scp_ca_time)*t_sicd.Grid.Col.SS/ss_az_s

            # cerate the 2d grids
            coords_rg_2d_t, coords_az_2d_t = numpy.meshgrid(coords_rg_m, coords_az_m, indexing='xy')

            coefs, residuals, rank, sing_values = two_dim_poly_fit(
                coords_rg_2d_t, coords_az_2d_t, dopcentroid_sampled,
                x_order=3, y_order=3, x_scale=1e-3, y_scale=1e-3, rcond=1e-40)
            logger.info(
                'The dop_centroid_poly fit details:\n\t'
                'root mean square residuals = {}\n\t'
                'rank = {}\n\t'
                'singular values = {}'.format(residuals, rank, sing_values))
            t_sicd.RMA.INCA.DopCentroidPoly = Poly2DType(Coefs=coefs)
            t_sicd.Grid.Col.DeltaKCOAPoly = Poly2DType(Coefs=coefs*ss_az_s/t_sicd.Grid.Col.SS)

            timeca_sampled = numpy.outer(grid_zd_time, numpy.ones((grid_r.size, )))
            time_coa_sampled = timeca_sampled + (dopcentroid_sampled/doprate_sampled)
            coefs, residuals, rank, sing_values = two_dim_poly_fit(
                coords_rg_2d_t, coords_az_2d_t, time_coa_sampled,
                x_order=3, y_order=3, x_scale=1e-3, y_scale=1e-3, rcond=1e-40)
            logger.info(
                'The time_coa_poly fit details:\n\t'
                'root mean square residuals = {}\n\t'
                'rank = {}\n\t'
                'singular values = {}'.format(residuals, rank, sing_values))
            t_sicd.Grid.TimeCOAPoly = Poly2DType(Coefs=coefs)

            return coords_rg_2d_t, coords_az_2d_t

        def define_radiometric() -> None:
            def get_poly(ds: h5pyDataset, name: str) -> Optional[Poly2DType]:
                array = ds[:]
                fill = ds.attrs['_FillValue']
                boolc = (array != fill)

                if numpy.any(boolc):
                    array = array[boolc]
                    if numpy.any(array != array[0]):
                        coefs, residuals, rank, sing_values = two_dim_poly_fit(
                            coords_rg_2d[boolc], coords_az_2d[boolc], array,
                            x_order=3, y_order=3, x_scale=1e-3, y_scale=1e-3, rcond=1e-40)
                        logger.info(
                            'The {} fit details:\n\t'
                            'root mean square residuals = {}\n\t'
                            'rank = {}\n\t'
                            'singular values = {}'.format(name, residuals, rank, sing_values))
                    else:
                        # it's constant, so just use a constant polynomial
                        coefs = [[array[0], ], ]
                        logger.info('The {} values are constant'.format(name))
                    return Poly2DType(Coefs=coefs)
                else:
                    logger.warning('No non-trivial values for {} provided.'.format(name))
                    return None

            beta0_poly = get_poly(beta0, 'beta0')
            gamma0_poly = get_poly(gamma0, 'gamma0')
            sigma0_poly = get_poly(sigma0, 'sigma0')

            nesz = hf['/science/LSAR/SLC/metadata/calibrationInformation/frequency{}/{}/nes0'.format(freq_name,
                                                                                                     pol_name)][:]
            noise_samples = nesz - (10 * numpy.log10(sigma0_poly.Coefs[0, 0]))

            coefs, residuals, rank, sing_values = two_dim_poly_fit(
                coords_rg_2d, coords_az_2d, noise_samples,
                x_order=3, y_order=3, x_scale=1e-3, y_scale=1e-3, rcond=1e-40)
            logger.info(
                'The noise_poly fit details:\n\t'
                'root mean square residuals = {}\n\t'
                'rank = {}\n\t'
                'singular values = {}'.format(
                    residuals, rank, sing_values))
            t_sicd.Radiometric = RadiometricType(
                BetaZeroSFPoly=beta0_poly,
                GammaZeroSFPoly=gamma0_poly,
                SigmaZeroSFPoly=sigma0_poly,
                NoiseLevel=NoiseLevelType_(
                    NoiseLevelType='ABSOLUTE', NoisePoly=Poly2DType(Coefs=coefs)))

        def update_geodata() -> None:
            ecf = point_projection.image_to_ground(
                [t_sicd.ImageData.SCPPixel.Row, t_sicd.ImageData.SCPPixel.Col], t_sicd)
            t_sicd.GeoData.SCP = SCPType(ECF=ecf)  # LLH will be populated

        t_sicd = base_sicd.copy()
        shape = ds.shape
        dtype = ds.dtype

        define_image_data()
        update_image_formation()
        coords_rg_2d, coords_az_2d = update_inca_and_grid()
        define_radiometric()
        update_geodata()
        t_sicd.derive()
        t_sicd.populate_rniirs(override=False)
        return t_sicd, shape, dtype
Esempio n. 20
0
    def __init__(self,
                 file_object: Union[str, BinaryIO],
                 sicd_meta: SICDType,
                 user_data: Optional[Dict[str, str]] = None,
                 check_older_version: bool = False,
                 check_existence: bool = True):
        """

        Parameters
        ----------
        file_object : str|BinaryIO
        sicd_meta : SICDType
        user_data : None|Dict[str, str]
        check_older_version : bool
            Try to use an older version (1.1) of the SICD standard, for possible
            application compliance issues?
        check_existence : bool
            Should we check if the given file already exists, and raises an exception if so?
        """

        self._data_written = True
        if isinstance(file_object, str):
            if check_existence and os.path.exists(file_object):
                raise SarpyIOError(
                    'Given file {} already exists, and a new SIO file cannot be created here.'
                    .format(file_object))
            file_object = open(file_object, 'wb')

        if not is_file_like(file_object):
            raise ValueError(
                'file_object requires a file path or BinaryIO object')

        self._file_object = file_object
        if is_real_file(file_object):
            self._file_name = file_object.name
            self._in_memory = False
        else:
            self._file_name = None
            self._in_memory = True

        # choose magic number (with user data) and corresponding endian-ness
        magic_number = 0xFD7F02FF
        endian = SIODetails.ENDIAN[magic_number]

        # define basic image details
        raw_shape = (sicd_meta.ImageData.NumRows, sicd_meta.ImageData.NumCols,
                     2)
        pixel_type = sicd_meta.ImageData.PixelType
        if pixel_type == 'RE32F_IM32F':
            raw_dtype = numpy.dtype('{}f4'.format(endian))
            element_type = 13
            element_size = 8
            format_function = ComplexFormatFunction(raw_dtype,
                                                    order='MP',
                                                    band_dimension=2)
        elif pixel_type == 'RE16I_IM16I':
            raw_dtype = numpy.dtype('{}i2'.format(endian))
            element_type = 12
            element_size = 4
            format_function = ComplexFormatFunction(raw_dtype,
                                                    order='MP',
                                                    band_dimension=2)
        else:
            raw_dtype = numpy.dtype('{}u1'.format(endian))
            element_type = 11
            element_size = 2
            format_function = ComplexFormatFunction(
                raw_dtype,
                order='MP',
                band_dimension=2,
                magnitude_lookup_table=sicd_meta.ImageData.AmpTable)

        # construct the sio header
        header = numpy.array([
            magic_number, raw_shape[0], raw_shape[1], element_type,
            element_size
        ],
                             dtype='>u4')
        # construct the user data - must be {str : str}
        if user_data is None:
            user_data = {}
        uh_args = sicd_meta.get_des_details(check_older_version)
        user_data['SICDMETA'] = sicd_meta.to_xml_string(tag='SICD',
                                                        urn=uh_args['DESSHTN'])

        # write the initial things to the buffer
        self._file_object.seek(0, os.SEEK_SET)
        self._file_object.write(struct.pack('{}5I'.format(endian), *header))
        # write the user data - name size, name, value size, value
        for name in user_data:
            name_bytes = name.encode('utf-8')
            self._file_object.write(
                struct.pack('{}I'.format(endian), len(name_bytes)))
            self._file_object.write(
                struct.pack('{}{}s'.format(endian, len(name_bytes)),
                            name_bytes))
            val_bytes = user_data[name].encode('utf-8')
            self._file_object.write(
                struct.pack('{}I'.format(endian), len(val_bytes)))
            self._file_object.write(
                struct.pack('{}{}s'.format(endian, len(val_bytes)), val_bytes))
        self._data_offset = self._file_object.tell()
        # initialize the single data segment
        if self._in_memory:
            underlying_array = numpy.full(raw_shape,
                                          fill_value=0,
                                          dtype=raw_dtype)
            data_segment = NumpyArraySegment(underlying_array,
                                             'complex64',
                                             raw_shape[:2],
                                             format_function=format_function,
                                             mode='w')
            self._data_written = False
        else:
            data_segment = NumpyMemmapSegment(self._file_object,
                                              self._data_offset,
                                              raw_dtype,
                                              raw_shape,
                                              'complex64',
                                              raw_shape[:2],
                                              format_function=format_function,
                                              mode='w',
                                              close_file=False)
            self._data_written = True
        BaseWriter.__init__(self, data_segment)
Esempio n. 21
0
    def get_sicd(self):
        """
        Get the SICD metadata for the image.

        Returns
        -------
        SICDType
        """
        def convert_string_dict(dict_in):
            # type: (dict) -> dict
            dict_out = OrderedDict()
            for key, val in dict_in.items():
                if isinstance(val, string_types):
                    dict_out[key] = val
                elif isinstance(val, int):
                    dict_out[key] = str(val)
                elif isinstance(val, float):
                    dict_out[key] = '{0:0.16G}'.format(val)
                else:
                    raise TypeError('Got unhandled type {}'.format(type(val)))
            return dict_out

        def extract_state_vector():
            # type: () -> (numpy.ndarray, numpy.ndarray, numpy.ndarray)
            vecs = collect['state']['state_vectors']
            times = numpy.zeros((len(vecs), ), dtype=numpy.float64)
            positions = numpy.zeros((len(vecs), 3), dtype=numpy.float64)
            velocities = numpy.zeros((len(vecs), 3), dtype=numpy.float64)
            for i, entry in enumerate(vecs):
                times[i] = get_seconds(parse_timestring(entry['time'],
                                                        precision='ns'),
                                       start_time,
                                       precision='ns')
                positions[i, :] = entry['position']
                velocities[i, :] = entry['velocity']
            return times, positions, velocities

        def get_collection_info():
            # type: () -> CollectionInfoType
            coll_name = collect['platform']
            start_dt = start_time.astype('datetime64[us]').astype(datetime)
            mode = collect['mode'].strip().lower()
            if mode == 'stripmap':
                radar_mode = RadarModeType(ModeType='STRIPMAP')
            elif mode == 'sliding_spotlight':
                radar_mode = RadarModeType(ModeType='DYNAMIC STRIPMAP')
            else:
                raise ValueError('Got unhandled radar mode {}'.format(mode))

            return CollectionInfoType(CollectorName=coll_name,
                                      CoreName='{}{}{}'.format(
                                          start_dt.strftime('%d%b%y').upper(),
                                          coll_name,
                                          start_dt.strftime('%H%M%S')),
                                      RadarMode=radar_mode,
                                      Classification='UNCLASSIFIED',
                                      CollectType='MONOSTATIC')

        def get_image_creation():
            # type: () -> ImageCreationType
            from sarpy.__about__ import __version__
            return ImageCreationType(
                Application=self._tiff_details.tags['Software'],
                DateTime=parse_timestring(
                    self._img_desc_tags['processing_time'], precision='us'),
                Profile='sarpy {}'.format(__version__),
                Site='Unknown')

        def get_image_data():
            # type: () -> ImageDataType
            img = collect['image']
            rows = int(
                img['columns'])  # capella uses flipped row/column definition?
            cols = int(img['rows'])
            if img['data_type'] == 'CInt16':
                pixel_type = 'RE16I_IM16I'
            else:
                raise ValueError('Got unhandled data_type {}'.format(
                    img['data_type']))

            scp_pixel = (int(0.5 * rows), int(0.5 * cols))
            if collect['radar']['pointing'] == 'left':
                scp_pixel = (rows - scp_pixel[0] - 1, cols - scp_pixel[1] - 1)

            return ImageDataType(NumRows=rows,
                                 NumCols=cols,
                                 FirstRow=0,
                                 FirstCol=0,
                                 PixelType=pixel_type,
                                 FullImage=(rows, cols),
                                 SCPPixel=scp_pixel)

        def get_geo_data():
            # type: () -> GeoDataType
            return GeoDataType(SCP=SCPType(
                ECF=collect['image']['center_pixel']['target_position']))

        def get_position():
            # type: () -> PositionType
            px, py, pz = fit_position_xvalidation(state_time,
                                                  state_position,
                                                  state_velocity,
                                                  max_degree=6)
            return PositionType(ARPPoly=XYZPolyType(X=px, Y=py, Z=pz))

        def get_grid():
            # type: () -> GridType

            img = collect['image']

            image_plane = 'OTHER'
            grid_type = 'PLANE'
            if self._img_desc_tags['product_type'] == 'SLC' and img[
                    'algorithm'] != 'backprojection':
                image_plane = 'SLANT'
                grid_type = 'RGZERO'

            coa_time = parse_timestring(img['center_pixel']['center_time'],
                                        precision='ns')
            row_imp_rsp_bw = 2 * bw / speed_of_light
            row = DirParamType(SS=img['pixel_spacing_column'],
                               ImpRespBW=row_imp_rsp_bw,
                               ImpRespWid=img['range_resolution'],
                               KCtr=2 * fc / speed_of_light,
                               DeltaK1=-0.5 * row_imp_rsp_bw,
                               DeltaK2=0.5 * row_imp_rsp_bw,
                               DeltaKCOAPoly=[
                                   [
                                       0.0,
                                   ],
                               ],
                               WgtType=WgtTypeType(
                                   WindowName=img['range_window']['name'],
                                   Parameters=convert_string_dict(
                                       img['range_window']['parameters'])))

            # get timecoa value
            timecoa_value = get_seconds(coa_time,
                                        start_time)  # TODO: constant?
            # find an approximation for zero doppler spacing - necessarily rough for backprojected images
            # find velocity at coatime
            arp_velocity = position.ARPPoly.derivative_eval(timecoa_value,
                                                            der_order=1)
            arp_speed = numpy.linalg.norm(arp_velocity)
            col_ss = img['pixel_spacing_row']
            dop_bw = img['processed_azimuth_bandwidth']
            # ss_zd_s = col_ss/arp_speed

            col = DirParamType(SS=col_ss,
                               ImpRespWid=img['azimuth_resolution'],
                               ImpRespBW=dop_bw / arp_speed,
                               KCtr=0,
                               WgtType=WgtTypeType(
                                   WindowName=img['azimuth_window']['name'],
                                   Parameters=convert_string_dict(
                                       img['azimuth_window']['parameters'])))

            # TODO: from Wade - account for numeric WgtFunct

            return GridType(ImagePlane=image_plane,
                            Type=grid_type,
                            TimeCOAPoly=[
                                [
                                    timecoa_value,
                                ],
                            ],
                            Row=row,
                            Col=col)

        def get_radar_colection():
            # type: () -> RadarCollectionType

            radar = collect['radar']
            freq_min = fc - 0.5 * bw
            return RadarCollectionType(
                TxPolarization=radar['transmit_polarization'],
                TxFrequency=TxFrequencyType(Min=freq_min, Max=freq_min + bw),
                Waveform=[
                    WaveformParametersType(
                        TxRFBandwidth=bw,
                        TxPulseLength=radar['pulse_duration'],
                        RcvDemodType='CHIRP',
                        ADCSampleRate=radar['sampling_frequency'],
                        TxFreqStart=freq_min)
                ],
                RcvChannels=[
                    ChanParametersType(TxRcvPolarization='{}:{}'.format(
                        radar['transmit_polarization'],
                        radar['receive_polarization']))
                ])

        def get_timeline():
            # type: () -> TimelineType
            prf = collect['radar']['prf'][0]['prf']
            return TimelineType(CollectStart=start_time,
                                CollectDuration=duration,
                                IPP=[
                                    IPPSetType(TStart=0,
                                               TEnd=duration,
                                               IPPStart=0,
                                               IPPEnd=duration * prf,
                                               IPPPoly=(0, prf)),
                                ])

        def get_image_formation():
            # type: () -> ImageFormationType

            radar = collect['radar']
            algo = collect['image']['algorithm'].upper()
            processings = None
            if algo == 'BACKPROJECTION':
                processings = [
                    ProcessingType(Type='Backprojected to DEM', Applied=True),
                ]
            if algo not in ('PFA', 'RMA', 'RGAZCOMP'):
                logging.warning(
                    'Image formation algorithm {} not one of the recognized SICD options, '
                    'being set to "OTHER".'.format(algo))
                algo = 'OTHER'

            return ImageFormationType(
                RcvChanProc=RcvChanProcType(NumChanProc=1, PRFScaleFactor=1),
                ImageFormAlgo=algo,
                TStartProc=0,
                TEndProc=duration,
                TxRcvPolarizationProc='{}:{}'.format(
                    radar['transmit_polarization'],
                    radar['receive_polarization']),
                TxFrequencyProc=TxFrequencyProcType(
                    MinProc=radar_collection.TxFrequency.Min,
                    MaxProc=radar_collection.TxFrequency.Max),
                STBeamComp='NO',
                ImageBeamComp='NO',
                AzAutofocus='NO',
                RgAutofocus='NO',
                Processings=processings)

        # TODO: From Wade - Radiometric is not suitable?

        # extract general use information
        collect = self._img_desc_tags['collect']
        start_time = parse_timestring(collect['start_timestamp'],
                                      precision='ns')
        end_time = parse_timestring(collect['stop_timestamp'], precision='ns')
        duration = get_seconds(end_time, start_time, precision='ns')
        state_time, state_position, state_velocity = extract_state_vector()
        bw = collect['radar']['pulse_bandwidth']
        fc = collect['radar']['center_frequency']

        # define the sicd elements
        collection_info = get_collection_info()
        image_creation = get_image_creation()
        image_data = get_image_data()
        geo_data = get_geo_data()
        position = get_position()
        grid = get_grid()
        radar_collection = get_radar_colection()
        timeline = get_timeline()
        image_formation = get_image_formation()

        sicd = SICDType(CollectionInfo=collection_info,
                        ImageCreation=image_creation,
                        ImageData=image_data,
                        GeoData=geo_data,
                        Position=position,
                        Grid=grid,
                        RadarCollection=radar_collection,
                        Timeline=timeline,
                        ImageFormation=image_formation)
        sicd.derive()

        # this would be a rough estimate - waiting for radiometric data
        # sicd.populate_rniirs(override=False)
        return sicd
Esempio n. 22
0
    def _get_band_specific_sicds(self, base_sicd: SICDType, h5_dict: dict,
                                 band_dict: dict, shape_dict: dict,
                                 pixeltype_dict: dict) -> Dict[str, SICDType]:
        def update_scp_prelim(sicd: SICDType, band_name: str) -> None:
            if self._mission_id in ['CSK', 'KMPS']:
                LLH = band_dict[band_name]['Centre Geodetic Coordinates']
            elif self._mission_id == 'CSG':
                LLH = h5_dict['Scene Centre Geodetic Coordinates']
            else:
                raise ValueError(_unhandled_id_text.format(self._mission_id))
            sicd.GeoData = GeoDataType(
                SCP=SCPType(LLH=LLH))  # EarthModel & ECF will be populated

        def update_image_data(
                sicd: SICDType,
                band_name: str) -> (float, float, float, float, int):
            cols, rows = shape_dict[band_name]
            # zero doppler time of first/last columns
            t_az_first_time = band_dict[band_name][
                'Zero Doppler Azimuth First Time']
            t_az_last_time = band_dict[band_name][
                'Zero Doppler Azimuth Last Time']
            t_ss_az_s = band_dict[band_name]['Line Time Interval']
            t_use_sign2 = 1
            if h5_dict['Look Side'].upper() == 'LEFT':
                t_use_sign2 = -1
                t_az_first_time, t_az_last_time = t_az_last_time, t_az_first_time
            # zero doppler time of first row
            t_rg_first_time = band_dict[band_name][
                'Zero Doppler Range First Time']
            # row spacing in range time (seconds)
            t_ss_rg_s = band_dict[band_name]['Column Time Interval']

            sicd.ImageData = ImageDataType(NumRows=rows,
                                           NumCols=cols,
                                           FirstRow=0,
                                           FirstCol=0,
                                           FullImage=(rows, cols),
                                           PixelType=pixeltype_dict[band_name],
                                           SCPPixel=RowColType(
                                               Row=int(rows / 2),
                                               Col=int(cols / 2)))
            return t_rg_first_time, t_ss_rg_s, t_az_first_time, t_ss_az_s, t_use_sign2

        def check_switch_state() -> (int, Poly1DType):
            use_sign = 1 if t_dop_rate_poly_rg[0] < 0 else -1
            return use_sign, Poly1DType(Coefs=use_sign * t_dop_rate_poly_rg)

        def update_timeline(sicd: SICDType, band_name: str) -> None:
            prf = band_dict[band_name]['PRF']
            duration = sicd.Timeline.CollectDuration
            ipp_el = sicd.Timeline.IPP[0]
            ipp_el.IPPEnd = duration * prf
            ipp_el.TEnd = duration
            ipp_el.IPPPoly = Poly1DType(Coefs=(0, prf))

        def update_radar_collection(sicd: SICDType, band_name: str) -> None:
            ind = None
            for the_chan_index, chan in enumerate(
                    sicd.RadarCollection.RcvChannels):
                if chan.TxRcvPolarization == polarization:
                    ind = the_chan_index
                    break
            if ind is None:
                raise ValueError(
                    'Failed to find receive channel for polarization {}'.
                    format(polarization))

            chirp_length = band_dict[band_name]['Range Chirp Length']
            chirp_rate = abs(band_dict[band_name]['Range Chirp Rate'])
            sample_rate = band_dict[band_name]['Sampling Rate']
            ref_dechirp_time = band_dict[band_name].get(
                'Reference Dechirping Time', 0)  # TODO: is this right?
            win_length = band_dict[band_name]['Echo Sampling Window Length']
            rcv_fm_rate = 0 if numpy.isnan(ref_dechirp_time) else chirp_rate
            band_width = chirp_length * chirp_rate
            fr_min = center_frequency - 0.5 * band_width
            fr_max = center_frequency + 0.5 * band_width
            sicd.RadarCollection.TxFrequency = (fr_min, fr_max)
            sicd.RadarCollection.Waveform = [
                WaveformParametersType(index=0,
                                       TxPulseLength=chirp_length,
                                       TxRFBandwidth=band_width,
                                       TxFreqStart=fr_min,
                                       TxFMRate=chirp_rate,
                                       ADCSampleRate=sample_rate,
                                       RcvFMRate=rcv_fm_rate,
                                       RcvWindowLength=win_length /
                                       sample_rate),
            ]
            sicd.ImageFormation.RcvChanProc.ChanIndices = [
                ind + 1,
            ]
            sicd.ImageFormation.TxFrequencyProc = (fr_min, fr_max)

        def update_rma_and_grid(sicd: SICDType, band_name: str) -> None:
            rg_scp_time = rg_first_time + (ss_rg_s *
                                           sicd.ImageData.SCPPixel.Row)
            az_scp_time = az_first_time + (use_sign2 * ss_az_s *
                                           sicd.ImageData.SCPPixel.Col)
            r_ca_scp = rg_scp_time * speed_of_light / 2
            sicd.RMA.INCA.R_CA_SCP = r_ca_scp
            # compute DRateSFPoly
            scp_ca_time = az_scp_time + ref_time_offset
            vel_poly = sicd.Position.ARPPoly.derivative(der_order=1,
                                                        return_poly=True)
            vel_ca_vec = vel_poly(scp_ca_time)
            vel_ca_sq = numpy.sum(vel_ca_vec * vel_ca_vec)
            vel_ca = numpy.sqrt(vel_ca_sq)
            r_ca = numpy.array([r_ca_scp, 1.], dtype=numpy.float64)
            dop_rate_poly_rg_shifted = dop_rate_poly_rg.shift(
                rg_ref_time - rg_scp_time,
                alpha=ss_rg_s / row_ss,
                return_poly=False)
            drate_sf_poly = -(polynomial.polymul(dop_rate_poly_rg_shifted,
                                                 r_ca) * speed_of_light /
                              (2 * center_frequency * vel_ca_sq))
            # update grid.row
            sicd.Grid.Row.SS = row_ss
            sicd.Grid.Row.ImpRespBW = row_bw
            sicd.Grid.Row.DeltaK1 = -0.5 * row_bw
            sicd.Grid.Row.DeltaK2 = 0.5 * row_bw
            # update grid.col
            col_ss = abs(vel_ca * ss_az_s * drate_sf_poly[0])
            sicd.Grid.Col.SS = col_ss
            if self.mission_id == 'CSK':
                col_bw = min(
                    band_dict[band_name]
                    ['Azimuth Focusing Transition Bandwidth'] * ss_az_s,
                    1) / col_ss
            elif self.mission_id in ['CSG', 'KMPS']:
                col_bw = min(
                    band_dict[band_name]['Azimuth Focusing Bandwidth'] *
                    ss_az_s, 1) / col_ss
            else:
                raise ValueError('Got unhandled mission_id {}'.format(
                    self.mission_id))
            sicd.Grid.Col.ImpRespBW = col_bw
            # update inca
            sicd.RMA.INCA.DRateSFPoly = Poly2DType(
                Coefs=numpy.reshape(drate_sf_poly, (-1, 1)))
            sicd.RMA.INCA.TimeCAPoly = Poly1DType(
                Coefs=[scp_ca_time, use_sign2 * ss_az_s / col_ss])
            # compute DopCentroidPoly & DeltaKCOAPoly
            dop_centroid_poly = numpy.zeros(
                (dop_poly_rg.order1 + 1, dop_poly_az.order1 + 1),
                dtype=numpy.float64)
            dop_centroid_poly[0, 0] = dop_poly_rg(rg_scp_time-rg_ref_time) + \
                dop_poly_az(az_scp_time-az_ref_time) - \
                0.5*(dop_poly_rg[0] + dop_poly_az[0])
            dop_poly_rg_shifted = dop_poly_rg.shift(rg_ref_time - rg_scp_time,
                                                    alpha=ss_rg_s / row_ss)
            dop_poly_az_shifted = dop_poly_az.shift(az_ref_time - az_scp_time,
                                                    alpha=ss_az_s / col_ss)
            dop_centroid_poly[1:, 0] = dop_poly_rg_shifted[1:]
            dop_centroid_poly[0, 1:] = dop_poly_az_shifted[1:]
            sicd.RMA.INCA.DopCentroidPoly = Poly2DType(Coefs=dop_centroid_poly)
            sicd.RMA.INCA.DopCentroidCOA = True
            sicd.Grid.Col.DeltaKCOAPoly = Poly2DType(
                Coefs=use_sign * dop_centroid_poly * ss_az_s / col_ss)
            # fit TimeCOAPoly
            sicd.Grid.TimeCOAPoly = fit_time_coa_polynomial(
                sicd.RMA.INCA,
                sicd.ImageData,
                sicd.Grid,
                dop_rate_poly_rg_shifted,
                poly_order=2)

            if csk_addin is not None:
                csk_addin.check_sicd(sicd, self.mission_id, h5_dict)

        def update_radiometric(sicd: SICDType, band_name: str) -> None:
            if self.mission_id in ['KMPS', 'CSG']:
                # TODO: skipping for now - strange results for flag == 77. Awaiting gidance - see Wade.
                return
            if h5_dict['Range Spreading Loss Compensation Geometry'] != 'NONE':
                slant_range = h5_dict['Reference Slant Range']
                exp = h5_dict['Reference Slant Range Exponent']
                sf = slant_range**(2 * exp)
                rsf = h5_dict['Rescaling Factor']
                sf /= rsf * rsf
                if h5_dict.get('Calibration Constant Compensation Flag',
                               None) == 0:
                    cal = band_dict[band_name]['Calibration Constant']
                    sf /= cal
                sicd.Radiometric = RadiometricType(BetaZeroSFPoly=Poly2DType(
                    Coefs=[
                        [
                            sf,
                        ],
                    ]))

        def update_geodata(sicd: SICDType) -> None:
            scp_pixel = [
                sicd.ImageData.SCPPixel.Row, sicd.ImageData.SCPPixel.Col
            ]
            ecf = sicd.project_image_to_ground(scp_pixel,
                                               projection_type='HAE')
            sicd.update_scp(ecf, coord_system='ECF')

            SCP = sicd.GeoData.SCP.ECF.get_array(dtype='float64')
            scp_time = sicd.RMA.INCA.TimeCAPoly[0]
            ca_pos = sicd.Position.ARPPoly(scp_time)
            RG = SCP - ca_pos
            sicd.RMA.INCA.R_CA_SCP = numpy.linalg.norm(RG)

        out = {}
        center_frequency = h5_dict['Radar Frequency']
        # relative times in csk are wrt some reference time - for sicd they should be relative to start time
        collect_start = parse_timestring(h5_dict['Scene Sensing Start UTC'],
                                         precision='ns')
        ref_time = parse_timestring(h5_dict['Reference UTC'], precision='ns')
        ref_time_offset = get_seconds(ref_time, collect_start, precision='ns')

        for bd_name in band_dict:
            polarization = self._parse_pol(
                self._get_polarization(h5_dict, band_dict, bd_name))
            az_ref_time, rg_ref_time, t_dop_poly_az, t_dop_poly_rg, t_dop_rate_poly_rg = \
                self._get_dop_poly_details(h5_dict, band_dict, bd_name)
            dop_poly_az = Poly1DType(Coefs=t_dop_poly_az)
            dop_poly_rg = Poly1DType(Coefs=t_dop_poly_rg)

            t_sicd = base_sicd.copy()
            t_sicd.ImageFormation.TxRcvPolarizationProc = polarization

            row_bw = band_dict[bd_name][
                'Range Focusing Bandwidth'] * 2 / speed_of_light
            row_ss = band_dict[bd_name]['Column Spacing']
            rg_first_time, ss_rg_s, az_first_time, ss_az_s, use_sign2 = update_image_data(
                t_sicd, bd_name)
            use_sign, dop_rate_poly_rg = check_switch_state()
            update_timeline(t_sicd, bd_name)
            update_radar_collection(t_sicd, bd_name)
            update_rma_and_grid(t_sicd, bd_name)
            update_radiometric(t_sicd, bd_name)

            update_scp_prelim(
                t_sicd, bd_name
            )  # set preliminary value for SCP (required for projection)
            update_geodata(t_sicd)
            t_sicd.derive()
            # t_sicd.populate_rniirs(override=False)
            out[bd_name] = t_sicd
        return out