def _get_zero_doppler_data(hf, base_sicd): """ Gets zero-doppler parameters. Parameters ---------- hf : h5py.File base_sicd : SICDType Returns ------- (numpy.ndarray, float, numpy.ndarray, numpy.ndarray) The azimuth zero-doppler time array, azimuth zero-doppler time spacing, grid range array, range zero doppler time array. """ gp = hf['/science/LSAR/SLC/swaths'] ds = gp['zeroDopplerTime'] ref_time = _get_ref_time(ds.attrs['units']) zd_time = ds[:] + get_seconds(ref_time, base_sicd.Timeline.CollectStart, precision='ns') ss_az_s = gp['zeroDopplerTimeSpacing'][()] if base_sicd.SCPCOA.SideOfTrack == 'L': zd_time = zd_time[::-1] ss_az_s *= -1 gp = hf['/science/LSAR/SLC/metadata/processingInformation/parameters'] grid_r = gp['slantRange'][:] ds = gp['zeroDopplerTime'] ref_time = _get_ref_time(ds.attrs['units']) grid_zd_time = ds[:] + get_seconds(ref_time, base_sicd.Timeline.CollectStart, precision='ns') return zd_time, ss_az_s, grid_r, grid_zd_time
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_position() -> 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_position(self): """ Gets the Position. Returns ------- PositionType """ start_time = self._get_start_time() # get radar position state information state_vectors = self._findall('./sourceAttributes' '/orbitAndAttitude' '/orbitInformation' '/stateVector') # convert to relevant numpy arrays for polynomial fitting T = numpy.array([ get_seconds(parse_timestring(state_vec.find('timeStamp').text), start_time, precision='us') for state_vec in state_vectors ], dtype=numpy.float64) Pos = numpy.hstack((numpy.array([ float(state_vec.find('xPosition').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis], numpy.array([ float(state_vec.find('yPosition').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis], numpy.array([ float(state_vec.find('zPosition').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis])) Vel = numpy.hstack((numpy.array([ float(state_vec.find('xVelocity').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis], numpy.array([ float(state_vec.find('yVelocity').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis], numpy.array([ float(state_vec.find('zVelocity').text) for state_vec in state_vectors ], dtype=numpy.float64)[:, numpy.newaxis])) 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 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_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_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 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) logger.info( 'The dop_centroid_poly fit details:\n\troot mean square ' 'residuals = {}\n\trank = {}\n\tsingular 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) logger.info( 'The time_coa_poly fit details:\n\troot mean square ' 'residuals = {}\n\trank = {}\n\tsingular values = {}'.format( residuals, rank, sing_values)) return t_dop_centroid_poly, t_time_coa_poly
def _get_collection_times(hf): """ Gets the collection start and end times, and inferred duration. Parameters ---------- hf : h5py.File The h5py File object. Returns ------- (numpy.datetime64, numpy.datetime64, float) Start and end times and duration """ start = parse_timestring(_stringify(hf['/science/LSAR/identification/zeroDopplerStartTime'][()]), precision='ns') end = parse_timestring(_stringify(hf['/science/LSAR/identification/zeroDopplerEndTime'][()]), precision='ns') duration = get_seconds(end, start, precision='ns') return start, end, duration
def _get_collection_times(hf: h5pyFile) -> Tuple[numpy.datetime64, numpy.datetime64, float]: """ Gets the collection start and end times, and inferred duration. Parameters ---------- hf : h5py.File The h5py File object. Returns ------- start_time : numpy.datetime64 end_time : numpy.datetime64 duration : float """ start_time = parse_timestring(_stringify(hf['/science/LSAR/identification/zeroDopplerStartTime'][()]), precision='ns') end_time = parse_timestring(_stringify(hf['/science/LSAR/identification/zeroDopplerEndTime'][()]), precision='ns') duration = get_seconds(end_time, start_time, precision='ns') return start_time, end_time, duration
def is_same_start_time(sicd1, sicd2): """ Do the two SICD structures have the same start time with millisecond resolution? Parameters ---------- sicd1 : sarpy.io.complex.sicd_elements.SICD.SICDType sicd2 : sarpy.io.complex.sicd_elements.SICD.SICDType Returns ------- bool """ if sicd1 is sicd2: return True try: return abs( get_seconds(sicd1.Timeline.CollectStart, sicd2.Timeline.CollectStart, precision='ms')) < 2e-3 except AttributeError: return False
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
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
def _get_rma_adjust_grid(self, scpcoa, grid, image_data, position, collection_info): """ Gets the RMA metadata, and adjust the Grid.Col metadata. Parameters ---------- scpcoa : SCPCOAType grid : GridType image_data : ImageDataType position : PositionType collection_info : CollectionInfoType Returns ------- RMAType """ look = scpcoa.look start_time = self._get_start_time() center_freq = self._get_center_frequency() doppler_bandwidth = float( self._find('./imageGenerationParameters' '/sarProcessingInformation' '/totalProcessedAzimuthBandwidth').text) zero_dop_last_line = parse_timestring( self._find('./imageGenerationParameters' '/sarProcessingInformation' '/zeroDopplerTimeLastLine').text) zero_dop_first_line = parse_timestring( self._find('./imageGenerationParameters' '/sarProcessingInformation' '/zeroDopplerTimeFirstLine').text) if look > 1: # SideOfTrack == 'L' # we explicitly want negative time order if zero_dop_first_line < zero_dop_last_line: zero_dop_first_line, zero_dop_last_line = zero_dop_last_line, zero_dop_first_line else: # we explicitly want positive time order if zero_dop_first_line > zero_dop_last_line: zero_dop_first_line, zero_dop_last_line = zero_dop_last_line, zero_dop_first_line col_spacing_zd = get_seconds(zero_dop_last_line, zero_dop_first_line, precision='us') / (image_data.NumCols - 1) # zero doppler time of SCP relative to collect start time_scp_zd = get_seconds(zero_dop_first_line, start_time, precision='us') + \ image_data.SCPPixel.Col*col_spacing_zd if self.generation == 'RS2': near_range = float( self._find('./imageGenerationParameters' '/sarProcessingInformation' '/slantRangeNearEdge').text) elif self.generation == 'RCM': near_range = float( self._find('./sceneAttributes' '/imageAttributes' '/slantRangeNearEdge').text) else: raise ValueError('unhandled generation {}'.format(self.generation)) inca = INCAType(R_CA_SCP=near_range + (image_data.SCPPixel.Row * grid.Row.SS), FreqZero=center_freq) # doppler rate calculations velocity = position.ARPPoly.derivative_eval(time_scp_zd, 1) vel_ca_squared = numpy.sum(velocity * velocity) # polynomial representing range as a function of range distance from SCP r_ca = numpy.array([inca.R_CA_SCP, 1], dtype=numpy.float64) # doppler rate coefficients if self.generation == 'RS2': doppler_rate_coeffs = numpy.array([ float(entry) for entry in self._find( './imageGenerationParameters' '/dopplerRateValues' '/dopplerRateValuesCoefficients').text.split() ], dtype=numpy.float64) doppler_rate_ref_time = float( self._find('./imageGenerationParameters' '/dopplerRateValues' '/dopplerRateReferenceTime').text) elif self.generation == 'RCM': doppler_rate_coeffs = numpy.array([ float(entry) for entry in self._find( './dopplerRate' '/dopplerRateEstimate' '/dopplerRateCoefficients').text.split() ], dtype=numpy.float64) doppler_rate_ref_time = float( self._find('./dopplerRate' '/dopplerRateEstimate' '/dopplerRateReferenceTime').text) else: raise ValueError('unhandled generation {}'.format(self.generation)) # the doppler_rate_coeffs represents a polynomial in time, relative to # doppler_rate_ref_time. # to construct the doppler centroid polynomial, we need to change scales # to a polynomial in space, relative to SCP. doppler_rate_poly = Poly1DType(Coefs=doppler_rate_coeffs) alpha = 2.0 / speed_of_light t_0 = doppler_rate_ref_time - alpha * inca.R_CA_SCP dop_rate_scaled_coeffs = doppler_rate_poly.shift(t_0, alpha, return_poly=False) # DRateSFPoly is then a scaled multiple of this scaled poly and r_ca above coeffs = -numpy.convolve(dop_rate_scaled_coeffs, r_ca) / ( alpha * center_freq * vel_ca_squared) inca.DRateSFPoly = Poly2DType( Coefs=numpy.reshape(coeffs, (coeffs.size, 1))) # modify a few of the other fields ss_scale = numpy.sqrt(vel_ca_squared) * inca.DRateSFPoly[0, 0] grid.Col.SS = col_spacing_zd * ss_scale grid.Col.ImpRespBW = -look * doppler_bandwidth / ss_scale inca.TimeCAPoly = Poly1DType(Coefs=[time_scp_zd, 1. / ss_scale]) # doppler centroid if self.generation == 'RS2': doppler_cent_coeffs = numpy.array([ float(entry) for entry in self._find( './imageGenerationParameters' '/dopplerCentroid' '/dopplerCentroidCoefficients').text.split() ], dtype=numpy.float64) doppler_cent_ref_time = float( self._find('./imageGenerationParameters' '/dopplerCentroid' '/dopplerCentroidReferenceTime').text) doppler_cent_time_est = parse_timestring( self._find('./imageGenerationParameters' '/dopplerCentroid' '/timeOfDopplerCentroidEstimate').text) elif self.generation == 'RCM': doppler_cent_coeffs = numpy.array([ float(entry) for entry in self._find( './dopplerCentroid' '/dopplerCentroidEstimate' '/dopplerCentroidCoefficients').text.split() ], dtype=numpy.float64) doppler_cent_ref_time = float( self._find('./dopplerCentroid' '/dopplerCentroidEstimate' '/dopplerCentroidReferenceTime').text) doppler_cent_time_est = parse_timestring( self._find('./dopplerCentroid' '/dopplerCentroidEstimate' '/timeOfDopplerCentroidEstimate').text) else: raise ValueError('unhandled generation {}'.format(self.generation)) doppler_cent_poly = Poly1DType(Coefs=doppler_cent_coeffs) alpha = 2.0 / speed_of_light t_0 = doppler_cent_ref_time - alpha * inca.R_CA_SCP scaled_coeffs = doppler_cent_poly.shift(t_0, alpha, return_poly=False) inca.DopCentroidPoly = Poly2DType( Coefs=numpy.reshape(scaled_coeffs, (scaled_coeffs.size, 1))) # adjust doppler centroid for spotlight, we need to add a second # dimension to DopCentroidPoly if collection_info.RadarMode.ModeType == 'SPOTLIGHT': doppler_cent_est = get_seconds(doppler_cent_time_est, start_time, precision='us') doppler_cent_col = (doppler_cent_est - time_scp_zd) / col_spacing_zd dop_poly = numpy.zeros((scaled_coeffs.shape[0], 2), dtype=numpy.float64) dop_poly[:, 0] = scaled_coeffs dop_poly[0, 1] = -look * center_freq * alpha * numpy.sqrt( vel_ca_squared) / inca.R_CA_SCP # dopplerCentroid in native metadata was defined at specific column, # which might not be our SCP column. Adjust so that SCP column is correct. dop_poly[0, 0] = dop_poly[0, 0] - (dop_poly[0, 1] * doppler_cent_col * grid.Col.SS) inca.DopCentroidPoly = Poly2DType(Coefs=dop_poly) grid.Col.DeltaKCOAPoly = Poly2DType( Coefs=inca.DopCentroidPoly.get_array() * col_spacing_zd / grid.Col.SS) # compute grid.Col.DeltaK1/K2 from DeltaKCOAPoly coeffs = grid.Col.DeltaKCOAPoly.get_array()[:, 0] # get roots roots = polynomial.polyroots(coeffs) # construct range bounds (in meters) range_bounds = ( numpy.array([0, image_data.NumRows - 1], dtype=numpy.float64) - image_data.SCPPixel.Row) * grid.Row.SS possible_ranges = numpy.copy(range_bounds) useful_roots = ((roots > numpy.min(range_bounds)) & (roots < numpy.max(range_bounds))) if numpy.any(useful_roots): possible_ranges = numpy.concatenate( (possible_ranges, roots[useful_roots]), axis=0) azimuth_bounds = ( numpy.array([0, (image_data.NumCols - 1)], dtype=numpy.float64) - image_data.SCPPixel.Col) * grid.Col.SS coords_az_2d, coords_rg_2d = numpy.meshgrid(azimuth_bounds, possible_ranges) possible_bounds_deltak = grid.Col.DeltaKCOAPoly( coords_rg_2d, coords_az_2d) grid.Col.DeltaK1 = numpy.min( possible_bounds_deltak) - 0.5 * grid.Col.ImpRespBW grid.Col.DeltaK2 = numpy.max( possible_bounds_deltak) + 0.5 * grid.Col.ImpRespBW # Wrapped spectrum if (grid.Col.DeltaK1 < -0.5 / grid.Col.SS) or (grid.Col.DeltaK2 > 0.5 / grid.Col.SS): grid.Col.DeltaK1 = -0.5 / abs(grid.Col.SS) grid.Col.DeltaK2 = -grid.Col.DeltaK1 time_coa_poly = fit_time_coa_polynomial(inca, image_data, grid, dop_rate_scaled_coeffs, poly_order=2) if collection_info.RadarMode.ModeType == 'SPOTLIGHT': # using above was convenience, but not really sensible in spotlight mode grid.TimeCOAPoly = Poly2DType(Coefs=[ [ time_coa_poly.Coefs[0, 0], ], ]) inca.DopCentroidPoly = None elif collection_info.RadarMode.ModeType == 'STRIPMAP': # fit TimeCOAPoly for grid grid.TimeCOAPoly = time_coa_poly inca.DopCentroidCOA = True else: raise ValueError('unhandled ModeType {}'.format( collection_info.RadarMode.ModeType)) return RMAType(RMAlgoType='OMEGA_K', INCA=inca)
def _get_band_specific_sicds(self, base_sicd, h5_dict, band_dict, shape_dict): # type: (SICDType, dict, dict, dict) -> Dict[str, SICDType] az_ref_time, rg_ref_time, dop_poly_az, dop_poly_rg, dop_rate_poly_rg = self._get_dop_poly_details(h5_dict) 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') def update_scp_prelim(sicd, band_name): # type: (SICDType, str) -> None LLH = band_dict[band_name]['Centre Geodetic Coordinates'] sicd.GeoData = GeoDataType(SCP=SCPType(LLH=LLH)) # EarthModel & ECF will be populated def update_image_data(sicd, band_name): # type: (SICDType, str) -> Tuple[float, float, float, float] cols, rows = shape_dict[band_name] t_az_first_time = band_dict[band_name]['Zero Doppler Azimuth First Time'] # zero doppler time of first column t_ss_az_s = band_dict[band_name]['Line Time Interval'] if base_sicd.SCPCOA.SideOfTrack == 'L': # we need to reverse time order t_ss_az_s *= -1 t_az_first_time = band_dict[band_name]['Zero Doppler Azimuth Last 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, PixelType='RE16I_IM16I', 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 def update_timeline(sicd, band_name): # type: (SICDType, str) -> None prf = band_dict[band_name]['PRF'] duration = sicd.Timeline.CollectDuration ipp_el = sicd.Timeline.IPP[0] ipp_el.IPPEnd = duration ipp_el.TEnd = duration ipp_el.IPPPoly = Poly1DType(Coefs=(0, prf)) def update_radar_collection(sicd, band_name, ind): # type: (SICDType, str, int) -> None 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]['Reference Dechirping Time'] win_length = band_dict[band_name]['Echo Sampling Window Length'] rcv_fm_rate = 0 if numpy.isnan(ref_dechirp_time) else ref_dechirp_time # TODO: is this the correct value? 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 = TxFrequencyType(Min=fr_min, Max=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 = TxFrequencyProcType(MinProc=fr_min, MaxProc=fr_max) sicd.ImageFormation.TxRcvPolarizationProc = sicd.RadarCollection.RcvChannels[ind].TxRcvPolarization def update_rma_and_grid(sicd, band_name): # type: (SICDType, str) -> None rg_scp_time = rg_first_time + (ss_rg_s*sicd.ImageData.SCPPixel.Row) az_scp_time = az_first_time + (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 = vel_ca*ss_az_s*drate_sf_poly[0] sicd.Grid.Col.SS = col_ss col_bw = min(band_dict[band_name]['Azimuth Focusing Bandwidth'] * abs(ss_az_s), 1) / col_ss 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, 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=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) def update_radiometric(sicd, band_name): # type: (SICDType, str) -> None 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) if h5_dict['Calibration Constant Compensation Flag'] == 0: rsf = h5_dict['Rescaling Factor'] cal = band_dict[band_name]['Calibration Constant'] sf /= cal*(rsf**2) sicd.Radiometric = RadiometricType(BetaZeroSFPoly=Poly2DType(Coefs=[[sf, ], ])) def update_geodata(sicd): # type: (SICDType) -> None ecf = point_projection.image_to_ground([sicd.ImageData.SCPPixel.Row, sicd.ImageData.SCPPixel.Col], sicd) sicd.GeoData.SCP = SCPType(ECF=ecf) # LLH will be populated out = {} for i, bd_name in enumerate(band_dict): t_sicd = base_sicd.copy() update_scp_prelim(t_sicd, bd_name) # set preliminary value for SCP (required for projection) 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 = update_image_data(t_sicd, bd_name) update_timeline(t_sicd, bd_name) update_radar_collection(t_sicd, bd_name, i) update_rma_and_grid(t_sicd, bd_name) update_radiometric(t_sicd, bd_name) update_geodata(t_sicd) t_sicd.derive() t_sicd.populate_rniirs(override=False) out[bd_name] = t_sicd return out
def get_grid(): # type: () -> GridType def get_weight(window_dict): window_name = window_dict['name'] if window_name.lower() == 'rectangular': return WgtTypeType(WindowName='UNIFORM') else: # TODO: what is the proper interpretation for the avci-nacaroglu window? return WgtTypeType(WindowName=window_name, Parameters=convert_string_dict( window_dict['parameters'])) img = collect['image'] img_geometry = img['image_geometry'] if img_geometry.get('type', None) == 'slant_plane': image_plane = 'SLANT' else: image_plane = 'OTHER' grid_type = 'PLANE' if self._img_desc_tags['product_type'] == 'SLC' and img[ 'algorithm'] != 'backprojection': 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'], 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, ], ], WgtType=get_weight(img['range_window'])) # get timecoa value timecoa_value = get_seconds( coa_time, start_time) # TODO: this is not generally correct # 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, Sgn=-1, ImpRespWid=img['azimuth_resolution'], ImpRespBW=dop_bw / arp_speed, KCtr=0, WgtType=get_weight(img['azimuth_window'])) # TODO: # column deltakcoa poly - it's in there at ["image"]["frequency_doppler_centroid_polynomial"] # weight functions? return GridType(ImagePlane=image_plane, Type=grid_type, TimeCOAPoly=[ [ timecoa_value, ], ], Row=row, Col=col)
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)
def _get_band_specific_sicds(self, base_sicd, h5_dict, band_dict, shape_dict, dtype_dict): # type: (SICDType, dict, dict, dict, dict) -> Dict[str, SICDType] def update_scp_prelim(sicd, band_name): # type: (SICDType, 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 mission id {}'.format( self._mission_id)) sicd.GeoData = GeoDataType( SCP=SCPType(LLH=LLH)) # EarthModel & ECF will be populated def update_image_data(sicd, band_name): # type: (SICDType, str) -> Tuple[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=dtype_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(): # type: () -> Tuple[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, band_name): # type: (SICDType, 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, band_name, ind): # type: (SICDType, str, int) -> None 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 = TxFrequencyType(Min=fr_min, Max=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 = TxFrequencyProcType( MinProc=fr_min, MaxProc=fr_max) sicd.ImageFormation.TxRcvPolarizationProc = sicd.RadarCollection.RcvChannels[ ind].TxRcvPolarization def update_rma_and_grid(sicd, band_name): # type: (SICDType, 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, band_name): # type: (SICDType, 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): # type: (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 i, bd_name in enumerate(band_dict): 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() update_scp_prelim( t_sicd, bd_name ) # set preliminary value for SCP (required for projection) 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, i) update_rma_and_grid(t_sicd, bd_name) update_radiometric(t_sicd, bd_name) update_geodata(t_sicd) t_sicd.derive() # t_sicd.populate_rniirs(override=False) out[bd_name] = t_sicd return out
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
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
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)